mirror of
https://github.com/sasjs/core.git
synced 2025-12-11 14:34:35 +00:00
Compare commits
134 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d587b44b34 | ||
|
|
e43aac972a | ||
|
|
7dbe31b5d3 | ||
|
|
1672c96340 | ||
|
|
453aee2c1f | ||
|
|
00abbdcd65 | ||
|
|
88685dc585 | ||
|
|
cf8147d6ca | ||
|
|
f28f6b1530 | ||
|
|
cb4ea71e81 | ||
|
|
fe94d3781a | ||
|
|
7c17b39dad | ||
|
|
73dab4c651 | ||
|
|
5d72843167 | ||
|
|
f43df47cff | ||
|
|
aaca26770b | ||
|
|
4a124d5bd8 | ||
|
|
03cd52a01a | ||
|
|
da79181b00 | ||
|
|
a405104052 | ||
|
|
56fdaa65d2 | ||
|
|
9d60c49c9f | ||
|
|
380170d5ba | ||
|
|
4b450f2091 | ||
|
|
1b013fbf1c | ||
|
|
bf7459bd2d | ||
|
|
1096db0846 | ||
|
|
fc9b765246 | ||
|
|
4a8f7bb014 | ||
|
|
e0469be0d8 | ||
|
|
e9e576b5ec | ||
|
|
1a32d114f1 | ||
|
|
94e83f6b8d | ||
|
|
35a6dede6f | ||
|
|
039ec397dd | ||
|
|
dce4630eb8 | ||
|
|
1e142f042b | ||
|
|
7caca2f139 | ||
|
|
61556b2de8 | ||
|
|
9e12409389 | ||
|
|
c8050f5a79 | ||
|
|
cb4f71c7cd | ||
|
|
a39f4e4eee | ||
|
|
b525b4171d | ||
|
|
f2d80b3b63 | ||
|
|
96dda87f37 | ||
|
|
3435509eec | ||
|
|
42f2767129 | ||
|
|
099a5f7840 | ||
|
|
c83ea705a2 | ||
|
|
9ea6c875f2 | ||
|
|
0728f72c4f | ||
|
|
a90a6f00cf | ||
|
|
f71e53af8d | ||
|
|
cc1b971e19 | ||
|
|
8484c752ed | ||
|
|
8bd31e6c97 | ||
|
|
f9b0f87f44 | ||
|
|
d7e9f10291 | ||
|
|
3edc3587b3 | ||
|
|
63bf00e28f | ||
|
|
6b2574947a | ||
|
|
eb9027ecb6 | ||
|
|
2ad931a566 | ||
|
|
cebe119304 | ||
|
|
bd3082d7e3 | ||
|
|
11da53f1cb | ||
|
|
c4cb0b2395 | ||
|
|
58614e9a3d | ||
|
|
c94c334c4b | ||
|
|
3bf44405f8 | ||
|
|
db68a256cb | ||
|
|
611fac6338 | ||
|
|
ddd120bb75 | ||
|
|
0d75e0bad8 | ||
|
|
ba8c4ac844 | ||
|
|
6938a42896 | ||
|
|
efff77c94e | ||
|
|
2b10cf6192 | ||
|
|
134b91f266 | ||
|
|
969f551e10 | ||
|
|
26623ba085 | ||
|
|
8eb495890d | ||
|
|
c1a30977f1 | ||
|
|
9a6be61651 | ||
|
|
388839039e | ||
|
|
e760a89a6a | ||
|
|
d2e30267e8 | ||
|
|
190dbddfe3 | ||
|
|
05e769794e | ||
|
|
558ebaf6f2 | ||
|
|
970b56fe5a | ||
|
|
c2597bd07b | ||
|
|
c4baca477b | ||
|
|
7726b0e0b0 | ||
|
|
0a536245f3 | ||
|
|
edfa9ecc07 | ||
|
|
f4982c85ca | ||
|
|
3ce771d587 | ||
|
|
72d6b446c3 | ||
|
|
40d694eec8 | ||
|
|
6af1423666 | ||
|
|
23a01347f1 | ||
|
|
7c86d6163a | ||
|
|
d7233208f1 | ||
|
|
7f587ba720 | ||
|
|
21ecc1b675 | ||
|
|
6b13dc2b87 | ||
|
|
bb89184212 | ||
|
|
56338caaca | ||
|
|
d7e2ff8ac9 | ||
|
|
582ec0a1f9 | ||
|
|
53785f5644 | ||
|
|
a8acadb8f1 | ||
|
|
23dbda302e | ||
|
|
7e7ab4275d | ||
|
|
a455a3d98d | ||
|
|
588d987c25 | ||
|
|
8ffd06343a | ||
|
|
76207c443c | ||
|
|
7e9e0fac07 | ||
|
|
1fdbc7cce9 | ||
|
|
312369b200 | ||
|
|
c030174bfb | ||
|
|
faf466e79a | ||
|
|
856ffc1b72 | ||
|
|
c0924af06b | ||
|
|
33cec61a13 | ||
|
|
854ff696d8 | ||
|
|
cc3435d13d | ||
|
|
5ceaac195d | ||
|
|
5d5df977a6 | ||
|
|
245e85ef36 | ||
|
|
b96df6f14f |
@@ -126,6 +126,15 @@
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "eltociear",
|
||||
"name": "Ikko Ashimine",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/22633385?v=4",
|
||||
"profile": "https://bandism.net/",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
}
|
||||
],
|
||||
"contributorsPerLine": 7,
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
#!/bin/bash
|
||||
sasjs lint
|
||||
|
||||
# Ensure lint is passing
|
||||
LINT=`sasjs lint`
|
||||
if [[ "$LINT" != "✔ All matched files use @sasjs/lint code style!" ]]; then
|
||||
echo "$LINT"
|
||||
echo "To commit in spite of these warnings, use the -n parameter."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Avoid commits to the master branch
|
||||
BRANCH=`git rev-parse --abbrev-ref HEAD`
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
tasks:
|
||||
- init: |
|
||||
nvm install --lts
|
||||
npm i -g @sasjs/cli
|
||||
- init: npm install -g npm
|
||||
- command: npm i
|
||||
- command: npm i -g @sasjs/cli
|
||||
|
||||
image:
|
||||
file: .gitpod.dockerfile
|
||||
@@ -24,4 +24,4 @@ github:
|
||||
# add a "Review in Gitpod" button to pull requests (defaults to false)
|
||||
addBadge: false
|
||||
# add a label once the prebuild is ready to pull requests (defaults to false)
|
||||
addLabel: prebuilt-in-gitpod
|
||||
addLabel: prebuilt-in-gitpod
|
||||
|
||||
@@ -6,7 +6,6 @@ sasjs/
|
||||
.github/
|
||||
.git-hooks/
|
||||
.vscode/
|
||||
main.dox
|
||||
make_singlefile.sh
|
||||
*.md
|
||||
.all-contributorsrc
|
||||
|
||||
15
README.md
15
README.md
@@ -18,7 +18,7 @@
|
||||
[dependency-url]:https://github.com/sasjs/core/blob/main/package.json
|
||||
|
||||
|
||||
Much quality. Many standards. The **Macro Core** library exists to save time and development effort! Herein ye shall find a veritable host of MIT-licenced, production quality SAS macros. These are a mix of tools, utilities, functions and code generators that are useful in the context of [Application Development](https://sasapps.io) on the SAS platform (eg https://datacontroller.io). [Contributions](https://github.com/sasjs/core/blob/main/CONTRIBUTING.md) are welcomed.
|
||||
Much quality. Many standards. The **Macro Core** library exists to save time and development effort! Herein ye shall find a veritable host of MIT-licenced, production quality SAS macros. These are a mix of tools, utilities, functions and code generators that are useful in the context of [Application Development](https://sasapps.io) on the SAS platform (eg https://datacontroller.io). [Contributions](https://github.com/sasjs/core/blob/main/.github/CONTRIBUTING.md) are welcome.
|
||||
|
||||
You can download and compile them all in just two lines of SAS code:
|
||||
|
||||
@@ -217,10 +217,20 @@ If you find this library useful, please leave a [star](https://github.com/sasjs/
|
||||
|
||||

|
||||
|
||||
## Other SAS Repositories
|
||||
|
||||
The following repositories are also worth checking out:
|
||||
|
||||
* [chris-swenson/sasmacros](https://github.com/chris-swenson/sasmacros)
|
||||
* [greg-wotton/sas-programs](https://github.com/greg-wootton/sas-programs)
|
||||
* [KatjaGlassConsulting/SMILE-SmartSASMacros](https://github.com/KatjaGlassConsulting/SMILE-SmartSASMacros)
|
||||
* [rogerjdeangelis](https://github.com/rogerjdeangelis)
|
||||
* [scottbass/sas](https://github.com/scottbass/SAS)
|
||||
* [yabwon/sas_packages](https://github.com/yabwon/SAS_PACKAGES)
|
||||
|
||||
## 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)):
|
||||
|
||||
@@ -242,6 +252,7 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
|
||||
<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>Vladislav Parhomchik</b></sub></a><br /><a href="https://github.com/sasjs/core/commits?author=VladislavParhomchik" title="Tests">⚠️</a> <a href="https://github.com/sasjs/core/pulls?q=is%3Apr+reviewed-by%3AVladislavParhomchik" title="Reviewed Pull Requests">👀</a></td>
|
||||
<td align="center"><a href="https://github.com/vznesh"><img src="https://avatars.githubusercontent.com/u/28916792?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Vignesh T.</b></sub></a><br /><a href="https://github.com/sasjs/core/issues?q=author%3Avznesh" title="Bug reports">🐛</a></td>
|
||||
<td align="center"><a href="https://github.com/yabwon"><img src="https://avatars.githubusercontent.com/u/9314894?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Bart Jablonski</b></sub></a><br /><a href="https://github.com/sasjs/core/commits?author=yabwon" title="Code">💻</a></td>
|
||||
<td align="center"><a href="https://bandism.net/"><img src="https://avatars.githubusercontent.com/u/22633385?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Ikko Ashimine</b></sub></a><br /><a href="https://github.com/sasjs/core/commits?author=eltociear" title="Code">💻</a></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
|
||||
31
base/mf_deletefile.sas
Normal file
31
base/mf_deletefile.sas
Normal file
@@ -0,0 +1,31 @@
|
||||
/**
|
||||
@file
|
||||
@brief Deletes a physical file, if it exists
|
||||
@details Usage:
|
||||
|
||||
%mf_writefile(&sasjswork/myfile.txt,l1=some content)
|
||||
|
||||
%mf_deletefile(&sasjswork/myfile.txt)
|
||||
|
||||
%mf_deletefile(&sasjswork/myfile.txt)
|
||||
|
||||
|
||||
@param filepath Full path to the target file
|
||||
|
||||
@returns The return code from the fdelete() invocation
|
||||
|
||||
<h4> Related Macros </h4>
|
||||
@li mf_deletefile.test.sas
|
||||
@li mf_writefile.sas
|
||||
|
||||
@version 9.2
|
||||
@author Allan Bowe
|
||||
**/
|
||||
|
||||
%macro mf_deletefile(file
|
||||
)/*/STORE SOURCE*/;
|
||||
%local rc fref;
|
||||
%let rc= %sysfunc(filename(fref,&file));
|
||||
%if %sysfunc(fdelete(&fref)) ne 0 %then %put %sysfunc(sysmsg());
|
||||
%let rc= %sysfunc(filename(fref));
|
||||
%mend mf_deletefile;
|
||||
@@ -9,19 +9,17 @@
|
||||
|
||||
%put %mf_existfeature(PROCLUA);
|
||||
|
||||
@param feature the feature to detect. Leave blank to list all in log.
|
||||
@param [in] feature The feature to detect.
|
||||
|
||||
@return output returns 1 or 0 (or -1 if not found)
|
||||
|
||||
<h4> SAS Macros </h4>
|
||||
@li mf_getplatform.sas
|
||||
|
||||
|
||||
@version 8
|
||||
@author Allan Bowe
|
||||
**/
|
||||
/** @cond */
|
||||
|
||||
%macro mf_existfeature(feature
|
||||
)/*/STORE SOURCE*/;
|
||||
%let feature=%upcase(&feature);
|
||||
@@ -29,7 +27,11 @@
|
||||
%let platform=%mf_getplatform();
|
||||
|
||||
%if &feature= %then %do;
|
||||
%put Supported features: PROCLUA;
|
||||
%put No feature was requested for detection;
|
||||
%end;
|
||||
%else %if &feature=COLCONSTRAINTS %then %do;
|
||||
%if "%substr(&sysver,1,1)"="4" or "%substr(&sysver,1,1)"="5" %then 0;
|
||||
%else 1;
|
||||
%end;
|
||||
%else %if &feature=PROCLUA %then %do;
|
||||
/* https://blogs.sas.com/content/sasdummy/2015/08/03/using-lua-within-your-sas-programs */
|
||||
@@ -43,5 +45,4 @@
|
||||
%put &sysmacroname: &feature not found;
|
||||
%end;
|
||||
%mend mf_existfeature;
|
||||
|
||||
/** @endcond */
|
||||
/** @endcond */
|
||||
|
||||
42
base/mf_fmtdttm.sas
Normal file
42
base/mf_fmtdttm.sas
Normal file
@@ -0,0 +1,42 @@
|
||||
/**
|
||||
@file
|
||||
@brief Returns E8601DT26.6 if compatible else DATETIME19.3
|
||||
@details From our experience in [Data Controller for SAS]
|
||||
(https://datacontroller.io) deployments, the E8601DT26.6 datetime format has
|
||||
the widest support when it comes to pass-through SQL queries.
|
||||
|
||||
However, it is not supported in WPS or early versions of SAS 9 (M3 and below)
|
||||
when used as a datetime literal, eg:
|
||||
|
||||
data _null_;
|
||||
demo="%sysfunc(datetime(),E8601DT26.6)"dt;
|
||||
demo=;
|
||||
run;
|
||||
|
||||
This macro will therefore return DATEITME19.3 as an alternative format
|
||||
based on the runtime environment so that it can be used in such cases, eg:
|
||||
|
||||
data _null_;
|
||||
demo="%sysfunc(datetime(),%mf_fmtdttm())"dt;
|
||||
demo=;
|
||||
run;
|
||||
|
||||
<h4> Related Macros </h4>
|
||||
@li mf_fmtdttm.test.sas
|
||||
|
||||
@author Allan Bowe
|
||||
**/
|
||||
|
||||
%macro mf_fmtdttm(
|
||||
)/*/STORE SOURCE*/;
|
||||
|
||||
%if "&sysver"="9.2" or "&sysver"="9.3"
|
||||
or ("&sysver"="9.4" and "%substr(&SYSVLONG,9,1)" le "3")
|
||||
or "%substr(&sysver,1,1)"="4"
|
||||
or "%substr(&sysver,1,1)"="5"
|
||||
%then %do;DATETIME19.3%end;
|
||||
%else %do;E8601DT26.6%end;
|
||||
|
||||
%mend mf_fmtdttm;
|
||||
|
||||
|
||||
@@ -28,15 +28,17 @@
|
||||
be 8 characters, so a 7 letter prefix would mean `maxtries` should be 10.
|
||||
if using zero (0) as the prefix, a native assignment is used.
|
||||
@param [in] maxtries= (1000) the last part of the libref. Must be an integer.
|
||||
@param [in] lrecl= (32767) Provide a default lrecl with which to initialise
|
||||
the generated fileref.
|
||||
|
||||
@version 9.2
|
||||
@author Allan Bowe
|
||||
**/
|
||||
|
||||
%macro mf_getuniquefileref(prefix=_,maxtries=1000);
|
||||
%macro mf_getuniquefileref(prefix=_,maxtries=1000,lrecl=32767);
|
||||
%local rc fname;
|
||||
%if &prefix=0 %then %do;
|
||||
%let rc=%sysfunc(filename(fname,,temp));
|
||||
%let rc=%sysfunc(filename(fname,,temp,lrecl=&lrecl));
|
||||
%if &rc %then %put %sysfunc(sysmsg());
|
||||
&fname
|
||||
%end;
|
||||
@@ -47,7 +49,7 @@
|
||||
%do x=0 %to &maxtries;
|
||||
%let fname=&prefix%substr(%sysfunc(ranuni(0)),3,&len);
|
||||
%if %sysfunc(fileref(&fname)) > 0 %then %do;
|
||||
%let rc=%sysfunc(filename(fname,,temp));
|
||||
%let rc=%sysfunc(filename(fname,,temp,lrecl=&lrecl));
|
||||
%if &rc %then %put %sysfunc(sysmsg());
|
||||
&fname
|
||||
%return;
|
||||
|
||||
@@ -3,12 +3,12 @@
|
||||
@brief Returns an unused libref
|
||||
@details Use as follows:
|
||||
|
||||
libname mclib0 (work);
|
||||
libname mclib1 (work);
|
||||
libname mclib2 (work);
|
||||
libname mclib0 (work);
|
||||
libname mclib1 (work);
|
||||
libname mclib2 (work);
|
||||
|
||||
%let libref=%mf_getuniquelibref();
|
||||
%put &=libref;
|
||||
%let libref=%mf_getuniquelibref();
|
||||
%put &=libref;
|
||||
|
||||
which returns:
|
||||
|
||||
|
||||
@@ -33,7 +33,8 @@
|
||||
%else %if %symexist(&metavar) %then %do;
|
||||
%if %length(&&&metavar)=0 %then %let user=&sysuserid;
|
||||
/* sometimes SAS will add @domain extension - remove for consistency */
|
||||
%else %let user=%scan(&&&metavar,1,@);
|
||||
/* but be sure to quote in case of usernames with commas */
|
||||
%else %let user=%unquote(%scan(%quote(&&&metavar),1,@));
|
||||
%end;
|
||||
%else %let user=&sysuserid;
|
||||
|
||||
|
||||
@@ -2,31 +2,48 @@
|
||||
@file
|
||||
@brief Returns number of variables in a dataset
|
||||
@details Useful to identify those renagade datasets that have no columns!
|
||||
Can also be used to count for numeric, or character columns
|
||||
|
||||
%put Number of Variables=%mf_getvarcount(sashelp.class);
|
||||
%put Number of Variables=%mf_getvarcount(sashelp.class);
|
||||
%put Character Variables=%mf_getvarcount(sashelp.class,typefilter=C);
|
||||
%put Numeric Variables = %mf_getvarcount(sashelp.class,typefilter=N);
|
||||
|
||||
returns:
|
||||
> Number of Variables=4
|
||||
|
||||
@param libds Two part dataset (or view) reference.
|
||||
|
||||
@param [in] libds Two part dataset (or view) reference.
|
||||
@param [in] typefilter= (A) Filter for certain types of column. Valid values:
|
||||
@li A Count All columns
|
||||
@li C Count Character columns only
|
||||
@li N Count Numeric columns only
|
||||
|
||||
@version 9.2
|
||||
@author Allan Bowe
|
||||
|
||||
**/
|
||||
|
||||
%macro mf_getvarcount(libds
|
||||
%macro mf_getvarcount(libds,typefilter=A
|
||||
)/*/STORE SOURCE*/;
|
||||
%local dsid nvars rc ;
|
||||
%local dsid nvars rc outcnt x;
|
||||
%let dsid=%sysfunc(open(&libds));
|
||||
%let nvars=.;
|
||||
%let outcnt=0;
|
||||
%let typefilter=%upcase(&typefilter);
|
||||
%if &dsid %then %do;
|
||||
%let nvars=%sysfunc(attrn(&dsid,NVARS));
|
||||
%if &typefilter=A %then %let outcnt=&nvars;
|
||||
%else %if &nvars>0 %then %do x=1 %to &nvars;
|
||||
/* increment based on variable type */
|
||||
%if %sysfunc(vartype(&dsid,&x))=&typefilter %then %do;
|
||||
%let outcnt=%eval(&outcnt+1);
|
||||
%end;
|
||||
%end;
|
||||
%let rc=%sysfunc(close(&dsid));
|
||||
%end;
|
||||
%else %do;
|
||||
%put unable to open &libds (rc=&dsid);
|
||||
%let rc=%sysfunc(close(&dsid));
|
||||
%end;
|
||||
&nvars
|
||||
&outcnt
|
||||
%mend mf_getvarcount;
|
||||
@@ -20,8 +20,11 @@
|
||||
|
||||
%macro mf_isint(arg
|
||||
)/*/STORE SOURCE*/;
|
||||
/* remove minus sign if exists */
|
||||
|
||||
/* blank val is not an integer */
|
||||
%if "&arg"="" %then %do;0%return;%end;
|
||||
|
||||
/* remove minus sign if exists */
|
||||
%local val;
|
||||
%if "%substr(%str(&arg),1,1)"="-" %then %let val=%substr(%str(&arg),2);
|
||||
%else %let val=&arg;
|
||||
|
||||
@@ -59,7 +59,7 @@
|
||||
|
||||
%goto exit_success;
|
||||
%exit_err:
|
||||
%put %str(ERR)OR: &abortmsg;
|
||||
%put &abortmsg;
|
||||
%mf_abort(iftrue=(&mabort ne SOFT),
|
||||
mac=mf_verifymacvars,
|
||||
msg=%str(&abortmsg)
|
||||
|
||||
@@ -85,11 +85,16 @@
|
||||
%end;
|
||||
|
||||
/* Stored Process Server web app context */
|
||||
%if %symexist(_metaperson)
|
||||
%if %symexist(_METAFOLDER)
|
||||
or "&SYSPROCESSNAME "="Compute Server "
|
||||
or &mode=INCLUDE
|
||||
%then %do;
|
||||
options obs=max replace nosyntaxcheck mprint;
|
||||
options obs=max replace mprint;
|
||||
%if "%substr(&sysver,1,1)" ne "4" and "%substr(&sysver,1,1)" ne "5"
|
||||
%then %do;
|
||||
options nosyntaxcheck;
|
||||
%end;
|
||||
|
||||
%if &mode=INCLUDE %then %do;
|
||||
%if %sysfunc(exist(&errds))=1 %then %do;
|
||||
data _null_;
|
||||
@@ -161,12 +166,14 @@
|
||||
/* send response in SASjs JSON format */
|
||||
data _null_;
|
||||
file _webout mod lrecl=32000 encoding='utf-8';
|
||||
length msg $32767 ;
|
||||
length msg syswarningtext syserrortext $32767 ;
|
||||
sasdatetime=datetime();
|
||||
msg=symget('msg');
|
||||
%if &logline>0 %then %do;
|
||||
msg=cats(msg,'\n\nLog Extract:\n',symget('logmsg'));
|
||||
%end;
|
||||
/* escape the escapes */
|
||||
msg=tranwrd(msg,'\','\\');
|
||||
/* escape the quotes */
|
||||
msg=tranwrd(msg,'"','\"');
|
||||
/* ditch the CRLFs as chrome complains */
|
||||
@@ -260,4 +267,4 @@
|
||||
%end;
|
||||
%mend mp_abort;
|
||||
|
||||
/** @endcond */
|
||||
/** @endcond */
|
||||
|
||||
@@ -106,7 +106,7 @@
|
||||
|
||||
proc compare
|
||||
base=&scopeds(where=(upcase(name) not in (%mf_getquotedstr(&ilist))))
|
||||
compare=&ds;
|
||||
compare=&ds noprint;
|
||||
run;
|
||||
|
||||
%if &sysinfo=0 %then %do;
|
||||
|
||||
194
base/mp_chop.sas
Normal file
194
base/mp_chop.sas
Normal file
@@ -0,0 +1,194 @@
|
||||
/**
|
||||
@file
|
||||
@brief Splits a file of ANY SIZE by reference to a search string.
|
||||
@details Provide a fileref and a search string to chop off part of a file.
|
||||
|
||||
Works by reading in the file byte by byte, then marking the beginning and end
|
||||
of each matched string, before finally doing the chop.
|
||||
|
||||
Choose whether to keep the FIRST or the LAST section of the file. Optionally,
|
||||
use an OFFSET to fix the precise chop point.
|
||||
|
||||
Usage:
|
||||
|
||||
%let src="%sysfunc(pathname(work))/file.txt";
|
||||
%let str=Chop here!;
|
||||
%let out1="%sysfunc(pathname(work))/file1.txt";
|
||||
%let out2="%sysfunc(pathname(work))/file2.txt";
|
||||
%let out3="%sysfunc(pathname(work))/file3.txt";
|
||||
%let out4="%sysfunc(pathname(work))/file4.txt";
|
||||
|
||||
data _null_;
|
||||
file &src;
|
||||
put "startsection&str.endsection";
|
||||
run;
|
||||
|
||||
%mp_chop(&src, matchvar=str, keep=FIRST, outfile=&out1)
|
||||
%mp_chop(&src, matchvar=str, keep=LAST, outfile=&out2)
|
||||
%mp_chop(&src, matchvar=str, keep=FIRST, matchpoint=END, outfile=&out3)
|
||||
%mp_chop(&src, matchvar=str, keep=LAST, matchpoint=END, outfile=&out4)
|
||||
|
||||
filename results (&out1 &out2 &out3 &out4);
|
||||
data _null_;
|
||||
infile results;
|
||||
input;
|
||||
list;
|
||||
run;
|
||||
|
||||
Results:
|
||||
@li `startsection`
|
||||
@li `Chop here!endsection`
|
||||
@li `startsectionChop here!`
|
||||
@li `endsection`
|
||||
|
||||
For more examples, see mp_chop.test.sas
|
||||
|
||||
@param [in] infile The QUOTED path to the file on which to perform the chop
|
||||
@param [in] matchvar= Macro variable NAME containing the string to split by
|
||||
@param [in] matchpoint= (START) Valid values:
|
||||
@li START - chop at the beginning of the string in `matchvar`.
|
||||
@li END - chop at the end of the string in `matchvar`.
|
||||
@param [in] offset= (0) An adjustment to the precise chop location, by
|
||||
by reference to the `matchpoint`. Should be a positive or negative integer.
|
||||
@param [in] keep= (FIRST) Valid values:
|
||||
@li FIRST - keep the section of the file before the chop
|
||||
@li LAST - keep the section of the file after the chop
|
||||
@param [in] mdebug= (0) Set to 1 to provide macro debugging
|
||||
@param outfile= (0) Optional QUOTED path to the adjusted output file (avoids
|
||||
overwriting the first file).
|
||||
|
||||
<h4> SAS Macros </h4>
|
||||
@li mf_getuniquefileref.sas
|
||||
@li mf_getuniquename.sas
|
||||
|
||||
<h4> Related Macros </h4>
|
||||
@li mp_abort.sas
|
||||
@li mp_gsubfile.sas
|
||||
@li mp_replace.sas
|
||||
@li mp_chop.test.sas
|
||||
|
||||
@version 9.4
|
||||
@author Allan Bowe
|
||||
|
||||
**/
|
||||
|
||||
%macro mp_chop(infile,
|
||||
matchvar=,
|
||||
matchpoint=START,
|
||||
keep=FIRST,
|
||||
offset=0,
|
||||
mdebug=0,
|
||||
outfile=0
|
||||
)/*/STORE SOURCE*/;
|
||||
|
||||
%local fref0 dttm ds1 outref;
|
||||
%let fref0=%mf_getuniquefileref();
|
||||
%let ds1=%mf_getuniquename(prefix=allchars);
|
||||
%let ds2=%mf_getuniquename(prefix=startmark);
|
||||
|
||||
%if &outfile=0 %then %let outfile=&infile;
|
||||
|
||||
%mp_abort(iftrue= (%length(%superq(&matchvar))=0)
|
||||
,mac=mp_chop.sas
|
||||
,msg=%str(&matchvar is an empty variable)
|
||||
)
|
||||
|
||||
/* START */
|
||||
%let dttm=%sysfunc(datetime());
|
||||
|
||||
filename &fref0 &infile lrecl=1 recfm=n;
|
||||
|
||||
/* create dataset with one char per row */
|
||||
data &ds1;
|
||||
infile &fref0;
|
||||
input sourcechar $char1. @@;
|
||||
format sourcechar hex2.;
|
||||
run;
|
||||
|
||||
/* get start & stop position of first matchvar string (one row, two vars) */
|
||||
data &ds2;
|
||||
/* set find string to length in bytes to cover trailing spaces */
|
||||
length string $ %length(%superq(&matchvar));
|
||||
string =symget("&matchvar");
|
||||
drop string;
|
||||
|
||||
firstchar=char(string,1);
|
||||
findlen=lengthm(string); /* <- for trailing bytes */
|
||||
|
||||
do _N_=1 to nobs;
|
||||
set &ds1 nobs=nobs point=_N_;
|
||||
if sourcechar=firstchar then do;
|
||||
pos=1;
|
||||
s=0;
|
||||
do point=_N_ to min(_N_ + findlen -1,nobs);
|
||||
set &ds1 point=point;
|
||||
if sourcechar=char(string, pos) then s + 1;
|
||||
else goto _leave_;
|
||||
pos+1;
|
||||
end;
|
||||
_leave_:
|
||||
if s=findlen then do;
|
||||
START =_N_;
|
||||
_N_ =_N_+ s - 1;
|
||||
STOP =_N_;
|
||||
output;
|
||||
/* matched! */
|
||||
stop;
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
stop;
|
||||
keep START STOP;
|
||||
run;
|
||||
|
||||
%local split;
|
||||
%let split=0;
|
||||
data _null_;
|
||||
set &ds2;
|
||||
if "&matchpoint"='START' then do;
|
||||
if "&keep"='FIRST' then mp=start;
|
||||
else if "&keep"='LAST' then mp=start-1;
|
||||
end;
|
||||
else if "&matchpoint"='END' then do;
|
||||
if "&keep"='FIRST' then mp=stop+1;
|
||||
else if "&keep"='LAST' then mp=stop;
|
||||
end;
|
||||
split=mp+&offset;
|
||||
call symputx('split',split,'l');
|
||||
%if &mdebug=1 %then %do;
|
||||
put (_all_)(=);
|
||||
%put &=offset;
|
||||
%end;
|
||||
run;
|
||||
%if &split=0 %then %do;
|
||||
%put &sysmacroname: No match found in &infile for string %superq(&matchvar);
|
||||
%return;
|
||||
%end;
|
||||
|
||||
data _null_;
|
||||
file &outfile recfm=n;
|
||||
set &ds1;
|
||||
%if &keep=FIRST %then %do;
|
||||
if _n_ ge &split then stop;
|
||||
%end;
|
||||
%else %do;
|
||||
if _n_ gt &split;
|
||||
%end;
|
||||
put sourcechar char1.;
|
||||
run;
|
||||
|
||||
%if &mdebug=0 %then %do;
|
||||
filename &fref0 clear;
|
||||
%end;
|
||||
%else %do;
|
||||
data _null_;
|
||||
infile &outfile lrecl=32767;
|
||||
input;
|
||||
list;
|
||||
if _n_>50 then stop;
|
||||
run;
|
||||
%end;
|
||||
/* END */
|
||||
%put &sysmacroname took %sysevalf(%sysfunc(datetime())-&dttm) seconds to run;
|
||||
|
||||
%mend mp_chop;
|
||||
@@ -65,7 +65,8 @@ proc sql;
|
||||
%end;
|
||||
|
||||
%if &libds=0 %then %do;
|
||||
proc sql;
|
||||
describe table &syslast;
|
||||
drop table &syslast;
|
||||
%end;
|
||||
%mend mp_coretable;
|
||||
%mend mp_coretable;
|
||||
|
||||
@@ -29,6 +29,7 @@ Usage:
|
||||
<h4> SAS Macros </h4>
|
||||
@li mf_getplatform.sas
|
||||
@li mm_createwebservice.sas
|
||||
@li ms_createwebservice.sas
|
||||
@li mv_createwebservice.sas
|
||||
|
||||
@param [in,out] path= The full folder path where the service will be created
|
||||
@@ -40,7 +41,7 @@ Usage:
|
||||
be added
|
||||
@param [in] replace= (YES) Select YES to replace any existing service in that
|
||||
location
|
||||
|
||||
@param [in] mDebug= (0) set to 1 to show debug messages in the log
|
||||
|
||||
@version 9.2
|
||||
@author Allan Bowe
|
||||
@@ -53,6 +54,7 @@ Usage:
|
||||
,code=ft15f001
|
||||
,desc=This service was created by the mp_createwebservice macro
|
||||
,replace=YES
|
||||
,mdebug=0
|
||||
)/*/STORE SOURCE*/;
|
||||
|
||||
%if &syscc ge 4 %then %do;
|
||||
@@ -71,8 +73,17 @@ Usage:
|
||||
,replace=&replace
|
||||
)
|
||||
%end;
|
||||
%else %if &platform=SASJS %then %do;
|
||||
%if "&path"="HOME" %then %let path=/Users/&_sasjs_username/My Folder;
|
||||
%ms_createwebservice(path=&path
|
||||
,name=&name
|
||||
,code=&code
|
||||
,precode=&precode
|
||||
,mdebug=&mdebug
|
||||
)
|
||||
%end;
|
||||
%else %do;
|
||||
%if "&path"="HOME" %then %let path=/User Folders/&sysuserid/My Folder;
|
||||
%if "&path"="HOME" %then %let path=/User Folders/&_METAPERSON/My Folder;
|
||||
%mm_createwebservice(path=&path
|
||||
,name=&name
|
||||
,code=&code
|
||||
|
||||
@@ -94,6 +94,13 @@ data &out_ds(compress=no
|
||||
%end;
|
||||
if rc = 0 then do;
|
||||
did = dopen(fref);
|
||||
if did=0 then do;
|
||||
putlog "NOTE: This directory is empty, or does not exist - &path";
|
||||
msg=sysmsg();
|
||||
put msg;
|
||||
put _all_;
|
||||
stop;
|
||||
end;
|
||||
/* attribute is OS-dependent - could be "Directory" or "Directory Name" */
|
||||
numopts=doptnum(did);
|
||||
do i=1 to numopts;
|
||||
@@ -101,12 +108,6 @@ data &out_ds(compress=no
|
||||
if foption=:'Directory' then i=numopts;
|
||||
end;
|
||||
directory=dinfo(did,foption);
|
||||
if did=0 then do;
|
||||
putlog "NOTE: This directory is empty - " directory;
|
||||
msg=sysmsg();
|
||||
put _all_;
|
||||
stop;
|
||||
end;
|
||||
rc = filename(fref);
|
||||
end;
|
||||
else do;
|
||||
@@ -151,6 +152,7 @@ run;
|
||||
data &out_ds;
|
||||
set &out_ds;
|
||||
length infoname infoval $60 fref $8;
|
||||
if _n_=1 then call missing(fref);
|
||||
rc=filename(fref,filepath);
|
||||
drop rc infoname fid i close fref;
|
||||
if file_or_folder='file' then do;
|
||||
@@ -242,4 +244,4 @@ run;
|
||||
proc sql;
|
||||
drop table &out_ds;
|
||||
|
||||
%mend mp_dirlist;
|
||||
%mend mp_dirlist;
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
|
||||
Usage:
|
||||
|
||||
%mp_ds2cards(base_ds=sashelp.class
|
||||
%mp_ds2cards(sashelp.class
|
||||
, tgt_ds=work.class
|
||||
, cards_file= "C:\temp\class.sas"
|
||||
, showlog=NO
|
||||
@@ -23,7 +23,7 @@
|
||||
- explicity setting a unix LF
|
||||
- constraints / indexes etc
|
||||
|
||||
@param [in] base_ds= Should be two level - eg work.blah. This is the table
|
||||
@param [in] base_ds Should be two level - eg work.blah. This is the table
|
||||
that is converted to a cards file.
|
||||
@param [in] tgt_ds= Table that the generated cards file would create.
|
||||
Optional - if omitted, will be same as BASE_DS.
|
||||
@@ -48,6 +48,7 @@
|
||||
|
||||
@version 9.2
|
||||
@author Allan Bowe
|
||||
@cond
|
||||
**/
|
||||
|
||||
%macro mp_ds2cards(base_ds=, tgt_ds=
|
||||
@@ -219,7 +220,8 @@ data _null_;
|
||||
put ' @file';
|
||||
put " @brief Datalines for %upcase(%scan(&base_ds,2)) dataset";
|
||||
put " @details Generated by %nrstr(%%)mp_ds2cards()";
|
||||
put " Available on github.com/sasjs/core";
|
||||
put " Source: https://github.com/sasjs/core";
|
||||
put ' @cond ';
|
||||
put '**/';
|
||||
put "data &tgt_ds &indexes;";
|
||||
put "attrib ";
|
||||
@@ -264,6 +266,7 @@ data _null_;
|
||||
if __lastobs then do;
|
||||
put ';;;;';
|
||||
put 'run;';
|
||||
put '/** @endcond **/';
|
||||
stop;
|
||||
end;
|
||||
run;
|
||||
@@ -283,4 +286,5 @@ quit;
|
||||
%put NOTE-;%put NOTE-;
|
||||
%put NOTE- %sysfunc(dequote(&cards_file.));
|
||||
%put NOTE-;%put NOTE-;
|
||||
%mend mp_ds2cards;
|
||||
%mend mp_ds2cards;
|
||||
/** @endcond **/
|
||||
|
||||
@@ -92,7 +92,36 @@ data &outds;
|
||||
/*length GROUP_LOGIC SUBGROUP_LOGIC $3 SUBGROUP_ID 8 VARIABLE_NM $32
|
||||
OPERATOR_NM $10 RAW_VALUE $4000;*/
|
||||
set &inds;
|
||||
length reason_cd $4032;
|
||||
length reason_cd $4032 vtype $1 vnum dsid 8;
|
||||
|
||||
/* quick check to ensure column exists */
|
||||
if upcase(VARIABLE_NM) not in
|
||||
(%upcase(%mf_getvarlist(&targetds,dlm=%str(,),quote=SINGLE)))
|
||||
then do;
|
||||
REASON_CD="Variable "!!cats(variable_nm)!!" not in &targetds";
|
||||
putlog REASON_CD= VARIABLE_NM=;
|
||||
call symputx('reason_cd',reason_cd,'l');
|
||||
call symputx('nobs',_n_,'l');
|
||||
output;
|
||||
return;
|
||||
end;
|
||||
|
||||
/* need to open the dataset to get the column type */
|
||||
dsid=open("&targetds","i");
|
||||
if dsid>0 then do;
|
||||
vnum=varnum(dsid,VARIABLE_NM);
|
||||
if vnum<1 then do;
|
||||
/* should not happen as was also tested for above */
|
||||
REASON_CD=cats("Variable (",VARIABLE_NM,") not found in &targetds");
|
||||
putlog REASON_CD= dsid=;
|
||||
call symputx('reason_cd',reason_cd,'l');
|
||||
call symputx('nobs',_n_,'l');
|
||||
output;
|
||||
return;
|
||||
end;
|
||||
/* now we can get the type */
|
||||
else vtype=vartype(dsid,vnum);
|
||||
end;
|
||||
|
||||
/* closed list checks */
|
||||
if GROUP_LOGIC not in ('AND','OR') then do;
|
||||
@@ -116,17 +145,8 @@ data &outds;
|
||||
call symputx('nobs',_n_,'l');
|
||||
output;
|
||||
end;
|
||||
if upcase(VARIABLE_NM) not in
|
||||
(%upcase(%mf_getvarlist(&targetds,dlm=%str(,),quote=SINGLE)))
|
||||
then do;
|
||||
REASON_CD="Variable "!!cats(variable_nm)!!" not in &targetds";
|
||||
putlog REASON_CD= VARIABLE_NM=;
|
||||
call symputx('reason_cd',reason_cd,'l');
|
||||
call symputx('nobs',_n_,'l');
|
||||
output;
|
||||
end;
|
||||
if OPERATOR_NM not in
|
||||
('=','>','<','<=','>=','BETWEEN','IN','NOT IN','NE','CONTAINS','GE','LE')
|
||||
('=','>','<','<=','>=','NE','GE','LE','BETWEEN','IN','NOT IN','CONTAINS')
|
||||
then do;
|
||||
REASON_CD='Invalid OPERATOR_NM: '!!cats(OPERATOR_NM);
|
||||
putlog REASON_CD= OPERATOR_NM=;
|
||||
@@ -135,6 +155,18 @@ data &outds;
|
||||
output;
|
||||
end;
|
||||
|
||||
/* special missing logic */
|
||||
if vtype='N'
|
||||
and OPERATOR_NM in ('=','>','<','<=','>=','NE','GE','LE')
|
||||
and cats(upcase(raw_value)) in (
|
||||
'.','.A','.B','.C','.D','.E','.F','.G','.H','.I','.J','.K','.L','.M','.N'
|
||||
'.N','.O','.P','.Q','.R','.S','.T','.U','.V','.W','.X','.Y','.Z','._'
|
||||
)
|
||||
then do;
|
||||
/* valid numeric - exit data step loop */
|
||||
return;
|
||||
end;
|
||||
|
||||
/* special logic */
|
||||
if OPERATOR_NM='BETWEEN' then raw_value1=tranwrd(raw_value,' AND ','');
|
||||
else if OPERATOR_NM in ('IN','NOT IN') then do;
|
||||
|
||||
@@ -84,6 +84,9 @@ filename &outref temp;
|
||||
run;
|
||||
%end;
|
||||
%else %do;
|
||||
proc sort data=&inds;
|
||||
by SUBGROUP_ID;
|
||||
run;
|
||||
data _null_;
|
||||
file &outref lrecl=32800;
|
||||
set &inds end=last;
|
||||
|
||||
@@ -40,6 +40,22 @@
|
||||
%let lib=%upcase(&lib);
|
||||
%let ds=%upcase(&ds);
|
||||
|
||||
/**
|
||||
* Cater for environments where sashelp.vcncolu is not available
|
||||
*/
|
||||
%if %sysfunc(exist(sashelp.vcncolu,view))=0 %then %do;
|
||||
proc sql;
|
||||
create table &outds(
|
||||
libref char(8)
|
||||
,TABLE_NAME char(32)
|
||||
,constraint_type char(8) label='Constraint Type'
|
||||
,constraint_name char(32) label='Constraint Name'
|
||||
,column_name char(32) label='Column'
|
||||
,constraint_order num
|
||||
);
|
||||
%return;
|
||||
%end;
|
||||
|
||||
/**
|
||||
* Neither dictionary tables nor sashelp provides a constraint order column,
|
||||
* however they DO arrive in the correct order. So, create the col.
|
||||
|
||||
@@ -41,6 +41,7 @@
|
||||
<h4> SAS Macros </h4>
|
||||
@li mcf_length.sas
|
||||
@li mf_getuniquename.sas
|
||||
@li mf_getvarcount.sas
|
||||
@li mf_getvarlist.sas
|
||||
@li mf_getvartype.sas
|
||||
@li mf_getvarformat.sas
|
||||
@@ -60,7 +61,7 @@
|
||||
,outds=work.mp_getmaxvarlengths
|
||||
)/*/STORE SOURCE*/;
|
||||
|
||||
%local vars prefix x var fmt;
|
||||
%local vars prefix x var fmt srcds;
|
||||
%let vars=%mf_getvarlist(libds=&libds);
|
||||
%let prefix=%substr(%mf_getuniquename(),1,25);
|
||||
%let num2char=%upcase(&num2char);
|
||||
@@ -70,6 +71,24 @@
|
||||
%mcf_length(wrap=YES, insert_cmplib=YES)
|
||||
%end;
|
||||
|
||||
%if &num2char=NO
|
||||
and ("%substr(&sysver,1,1)"="4" or "%substr(&sysver,1,1)"="5")
|
||||
and %mf_getvarcount(&libds,typefilter=N) gt 0
|
||||
%then %do;
|
||||
/* custom functions not supported in summary operations */
|
||||
%let srcds=%mf_getuniquename();
|
||||
data &srcds/view=&srcds;
|
||||
set &libds;
|
||||
%do x=1 %to %sysfunc(countw(&vars,%str( )));
|
||||
%let var=%scan(&vars,&x);
|
||||
%if %mf_getvartype(&libds,&var)=N %then %do;
|
||||
&prefix.&x=mcf_length(&var);
|
||||
%end;
|
||||
%end;
|
||||
run;
|
||||
%end;
|
||||
%else %let srcds=&libds;
|
||||
|
||||
proc sql;
|
||||
create table &outds (rename=(
|
||||
%do x=1 %to %sysfunc(countw(&vars,%str( )));
|
||||
@@ -94,10 +113,15 @@ create table &outds (rename=(
|
||||
%end;
|
||||
%end;
|
||||
%else %do;
|
||||
max(mcf_length(&var)) as &prefix.&x
|
||||
%if "%substr(&sysver,1,1)"="4" or "%substr(&sysver,1,1)"="5" %then %do;
|
||||
max(&prefix.&x) as &prefix.&x
|
||||
%end;
|
||||
%else %do;
|
||||
max(mcf_length(&var)) as &prefix.&x
|
||||
%end;
|
||||
%end;
|
||||
%end;
|
||||
from &libds;
|
||||
from &srcds;
|
||||
|
||||
proc transpose data=&outds
|
||||
out=&outds(rename=(_name_=NAME COL1=MAXLEN));
|
||||
|
||||
@@ -55,7 +55,8 @@
|
||||
)/*/STORE SOURCE*/;
|
||||
|
||||
|
||||
%local engine schema ds1 ds2 ds3 dsn tabs1 tabs2 sum pk4sure pkdefault finalpks;
|
||||
%local engine schema ds1 ds2 ds3 dsn tabs1 tabs2 sum pk4sure pkdefault finalpks
|
||||
pkfromindex;
|
||||
|
||||
%let lib=%upcase(&lib);
|
||||
%let ds=%upcase(&ds);
|
||||
@@ -70,6 +71,7 @@
|
||||
%let sum=%mf_getuniquename(prefix=getpk_sum);
|
||||
%let pk4sure=%mf_getuniquename(prefix=getpk_pk4sure);
|
||||
%let pkdefault=%mf_getuniquename(prefix=getpk_pkdefault);
|
||||
%let pkfromindex=%mf_getuniquename(prefix=getpk_pkfromindex);
|
||||
%let finalpks=%mf_getuniquename(prefix=getpk_finalpks);
|
||||
|
||||
%local dbg;
|
||||
@@ -180,9 +182,23 @@ create table &ds1 as
|
||||
and a.constraint_name=b.constraint_name
|
||||
order by 1,2,3,4;
|
||||
|
||||
/* extract cols from the relevant unique INDEXES */
|
||||
create table &pkfromindex as
|
||||
select libname as libref
|
||||
,memname as table_name
|
||||
,indxname as constraint_name
|
||||
,indxpos as constraint_order
|
||||
,name
|
||||
from dictionary.indexes
|
||||
where nomiss='yes' and unique='yes' and upcase(libname)="&lib"
|
||||
%if &ds ne 0 %then %do;
|
||||
and upcase(memname)="&ds"
|
||||
%end;
|
||||
order by 1,2,3,4;
|
||||
|
||||
/* create one table */
|
||||
data &finalpks;
|
||||
set &pkdefault &pk4sure ;
|
||||
set &pkdefault &pk4sure &pkfromindex;
|
||||
pk_ind=1;
|
||||
/* if there are multiple unique constraints, take the first */
|
||||
by libref table_name constraint_name;
|
||||
@@ -256,4 +272,4 @@ create table &outds as
|
||||
iftrue=(&mdebug=0)
|
||||
)
|
||||
|
||||
%mend mp_getpk;
|
||||
%mend mp_getpk;
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
outfile=0
|
||||
)/*/STORE SOURCE*/;
|
||||
|
||||
%if "%substr(&sysver,1,4)"="V.04" %then %do;
|
||||
%if "%substr(&sysver.XX,1,4)"="V.04" %then %do;
|
||||
%put %str(ERR)OR: Viya 4 does not support the IO library in lua;
|
||||
%return;
|
||||
%end;
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
/**
|
||||
@file
|
||||
@brief Returns a unique hash for a dataset
|
||||
@details Ignores metadata attributes, used only to hash values. Compared
|
||||
datasets must be in the same order.
|
||||
@details Ignores metadata attributes, used only to hash values. If used to
|
||||
compare datasets, they must have their columns and rows in the same order.
|
||||
|
||||
%mp_hashdataset(sashelp.class,outds=myhash)
|
||||
|
||||
@@ -17,13 +17,16 @@
|
||||
@li mf_getattrn.sas
|
||||
@li mf_getuniquename.sas
|
||||
@li mf_getvarlist.sas
|
||||
@li mf_getvartype.sas
|
||||
@li mp_md5.sas
|
||||
|
||||
<h4> Related Files </h4>
|
||||
@li mp_hashdataset.test.sas
|
||||
|
||||
@param [in] libds dataset to hash
|
||||
@param [in] salt= Provide a salt (could be, for instance, the dataset name)
|
||||
@param [in] iftrue= A condition under which the macro should be executed.
|
||||
@param [out] outds= (work.mf_hashdataset) The output dataset to create. This
|
||||
will contain one column (hashkey) with one observation (a hex32.
|
||||
will contain one column (hashkey) with one observation (a $hex32.
|
||||
representation of the input hash)
|
||||
|hashkey:$32.|
|
||||
|---|
|
||||
@@ -35,48 +38,52 @@
|
||||
|
||||
%macro mp_hashdataset(
|
||||
libds,
|
||||
outds=,
|
||||
outds=work._data_,
|
||||
salt=,
|
||||
iftrue=%str(1=1)
|
||||
)/*/STORE SOURCE*/;
|
||||
|
||||
%if not(%eval(%unquote(&iftrue))) %then %return;
|
||||
%local keyvar /* roll up the md5 */
|
||||
prevkeyvar /* retain prev record md5 */
|
||||
lastvar /* last var in input ds */
|
||||
cvars nvars;
|
||||
|
||||
%if %mf_getattrn(&libds,NLOBS)=0 %then %do;
|
||||
%put %str(WARN)ING: Dataset &libds is empty, or is not a dataset;
|
||||
%if not(%eval(%unquote(&iftrue))) %then %return;
|
||||
|
||||
/* avoid naming conflict for hash key vars */
|
||||
%let keyvar=%mf_getuniquename();
|
||||
%let prevkeyvar=%mf_getuniquename();
|
||||
%let lastvar=%mf_getuniquename();
|
||||
|
||||
%if %mf_getattrn(&libds,NLOBS)=0 %then %do;
|
||||
data &outds;
|
||||
length hashkey $32;
|
||||
retain hashkey "%sysfunc(md5(%str(&salt)),$hex32.)";
|
||||
output;
|
||||
stop;
|
||||
run;
|
||||
%put &sysmacroname: Dataset &libds is empty, or is not a dataset;
|
||||
%put &sysmacroname: hashkey of &outds is based on salt (&salt) only;
|
||||
%end;
|
||||
%else %if %mf_getattrn(&libds,NLOBS)<0 %then %do;
|
||||
%put %str(ERR)OR: Dataset &libds is not a dataset;
|
||||
%end;
|
||||
%else %do;
|
||||
data &outds(rename=(&keyvar=hashkey) keep=&keyvar)
|
||||
%if "%substr(&sysver,1,1)" ne "4" and "%substr(&sysver,1,1)" ne "5" %then %do;
|
||||
/nonote2err
|
||||
%end;
|
||||
%else %if %mf_getattrn(&libds,NLOBS)<0 %then %do;
|
||||
%put %str(ERR)OR: Dataset &libds is not a dataset;
|
||||
%end;
|
||||
%else %do;
|
||||
%local keyvar /* roll up the md5 */
|
||||
prevkeyvar /* retain prev record md5 */
|
||||
lastvar /* last var in input ds */
|
||||
varlist var i;
|
||||
/* avoid naming conflict for hash key vars */
|
||||
%let keyvar=%mf_getuniquename();
|
||||
%let prevkeyvar=%mf_getuniquename();
|
||||
%let lastvar=%mf_getuniquename();
|
||||
%let varlist=%mf_getvarlist(&libds);
|
||||
data &outds(rename=(&keyvar=hashkey) keep=&keyvar);
|
||||
length &prevkeyvar &keyvar $32;
|
||||
retain &prevkeyvar "%sysfunc(md5(%str(&salt)),$hex32.)";
|
||||
set &libds end=&lastvar;
|
||||
/* hash should include previous row */
|
||||
&keyvar=put(md5(&prevkeyvar
|
||||
/* loop every column, hashing every individual value */
|
||||
%do i=1 %to %sysfunc(countw(&varlist));
|
||||
%let var=%scan(&varlist,&i,%str( ));
|
||||
%if %mf_getvartype(&libds,&var)=C %then %do;
|
||||
!!put(md5(trim(&var)),$hex32.)
|
||||
%end;
|
||||
%else %do;
|
||||
!!put(md5(trim(put(&var*1,binary64.))),$hex32.)
|
||||
%end;
|
||||
%end;
|
||||
),$hex32.);
|
||||
&prevkeyvar=&keyvar;
|
||||
if &lastvar then output;
|
||||
run;
|
||||
%end;
|
||||
%mend mp_hashdataset;
|
||||
;
|
||||
length &prevkeyvar &keyvar $32;
|
||||
retain &prevkeyvar "%sysfunc(md5(%str(&salt)),$hex32.)";
|
||||
set &libds end=&lastvar;
|
||||
/* hash should include previous row */
|
||||
&keyvar=%mp_md5(
|
||||
cvars=%mf_getvarlist(&libds,typefilter=C) &prevkeyvar,
|
||||
nvars=%mf_getvarlist(&libds,typefilter=N)
|
||||
);
|
||||
&prevkeyvar=&keyvar;
|
||||
if &lastvar then output;
|
||||
run;
|
||||
%end;
|
||||
%mend mp_hashdataset;
|
||||
|
||||
@@ -67,10 +67,10 @@ options
|
||||
validvarname=V7 /* avoid special characters etc in variable names */
|
||||
varinitchk=%str(ERR)OR /* avoid data mistakes from variable name typos */
|
||||
varlenchk=%str(ERR)OR /* fail hard if truncation (data loss) can result */
|
||||
%if %substr(&sysver,1,1) ne 4 %then %do;
|
||||
%if "%substr(&sysver,1,1)" ne "4" and "%substr(&sysver,1,1)" ne "5" %then %do;
|
||||
noautocorrect /* disallow misspelled procedure names */
|
||||
dsoptions=note2err /* undocumented - convert bad NOTEs to ERRs */
|
||||
%end;
|
||||
;
|
||||
|
||||
%mend mp_init;
|
||||
%mend mp_init;
|
||||
|
||||
@@ -70,7 +70,7 @@
|
||||
|
||||
%if &action=OPEN %then %do;
|
||||
options nobomfile;
|
||||
data _null_;file &jref encoding='utf-8' ;
|
||||
data _null_;file &jref encoding='utf-8' lrecl=200;
|
||||
put '{"PROCESSED_DTTM" : "' "%sysfunc(datetime(),E8601DT26.6)" '"';
|
||||
run;
|
||||
%end;
|
||||
@@ -199,12 +199,23 @@
|
||||
format _numeric_ bart.;
|
||||
%do i=1 %to &numcols;
|
||||
%if &&typelong&i=char or &fmt=Y %then %do;
|
||||
&&name&i='"'!!trim(prxchange('s/"/\"/',-1,
|
||||
prxchange('s/'!!'0A'x!!'/\n/',-1,
|
||||
prxchange('s/'!!'0D'x!!'/\r/',-1,
|
||||
prxchange('s/'!!'09'x!!'/\t/',-1,
|
||||
prxchange('s/\\/\\\\/',-1,&&name&i)
|
||||
)))))!!'"';
|
||||
if findc(&&name&i,'"\'!!'0A0D09000E0F01021011'x) then do;
|
||||
&&name&i='"'!!trim(
|
||||
prxchange('s/"/\\"/',-1, /* double quote */
|
||||
prxchange('s/\x0A/\n/',-1, /* new line */
|
||||
prxchange('s/\x0D/\r/',-1, /* carriage return */
|
||||
prxchange('s/\x09/\\t/',-1, /* tab */
|
||||
prxchange('s/\x00/\\u0000/',-1, /* NUL */
|
||||
prxchange('s/\x0E/\\u000E/',-1, /* SS */
|
||||
prxchange('s/\x0F/\\u000F/',-1, /* SF */
|
||||
prxchange('s/\x01/\\u0001/',-1, /* SOH */
|
||||
prxchange('s/\x02/\\u0002/',-1, /* STX */
|
||||
prxchange('s/\x10/\\u0010/',-1, /* DLE */
|
||||
prxchange('s/\x11/\\u0011/',-1, /* DC1 */
|
||||
prxchange('s/\\/\\\\/',-1,&&name&i)
|
||||
))))))))))))!!'"';
|
||||
end;
|
||||
else &&name&i=quote(cats(&&name&i));
|
||||
%end;
|
||||
%end;
|
||||
run;
|
||||
|
||||
@@ -40,13 +40,13 @@
|
||||
@li mp_abort.sas
|
||||
@li mp_cntlout.sas
|
||||
@li mp_lockanytable.sas
|
||||
@li mp_storediffs.sas
|
||||
|
||||
<h4> Related Macros </h4>
|
||||
@li mddl_dc_difftable.sas
|
||||
@li mddl_dc_locktable.sas
|
||||
@li mp_loadformat.test.sas
|
||||
@li mp_lockanytable.sas
|
||||
@li mp_storediffs.sas
|
||||
@li mp_stackdiffs.sas
|
||||
|
||||
|
||||
@@ -134,7 +134,7 @@ run;
|
||||
* First, extract only relevant formats from the catalog
|
||||
*/
|
||||
proc sql noprint;
|
||||
select distinct fmtname into: fmtlist separated by ' ' from &libds;
|
||||
select distinct upcase(fmtname) into: fmtlist separated by ' ' from &libds;
|
||||
|
||||
%mp_cntlout(libcat=&libcat,fmtlist=&fmtlist,cntlout=&base_fmts)
|
||||
|
||||
@@ -144,8 +144,11 @@ select distinct fmtname into: fmtlist separated by ' ' from &libds;
|
||||
*/
|
||||
%mddl_sas_cntlout(libds=&template)
|
||||
data &inlibds;
|
||||
length &delete_col $3;
|
||||
if 0 then set &template;
|
||||
set &libds;
|
||||
if &delete_col='' then &delete_col='No';
|
||||
fmtname=upcase(fmtname);
|
||||
if missing(type) then do;
|
||||
if substr(fmtname,1,1)='$' then type='C';
|
||||
else type='N';
|
||||
@@ -156,7 +159,6 @@ data &inlibds;
|
||||
end;
|
||||
run;
|
||||
|
||||
|
||||
/**
|
||||
* Identify new records
|
||||
*/
|
||||
@@ -263,7 +265,7 @@ options ibufsize=&ibufsize;
|
||||
%end;
|
||||
|
||||
%mp_storediffs(&libcat-FC
|
||||
,&inlibds
|
||||
,&base_fmts
|
||||
,FMTNAME START
|
||||
,delds=&outds_del
|
||||
,modds=&outds_mod
|
||||
@@ -272,6 +274,9 @@ options ibufsize=&ibufsize;
|
||||
,mdebug=&mdebug
|
||||
)
|
||||
|
||||
proc append base=&auditlibds data=&storediffs;
|
||||
run;
|
||||
|
||||
%if &locklibds ne 0 %then %do;
|
||||
%mp_lockanytable(UNLOCK
|
||||
,lib=%scan(&auditlibds,1,.)
|
||||
@@ -295,4 +300,4 @@ options ibufsize=&ibufsize;
|
||||
%put &sysmacroname exit vars:;
|
||||
%put _local_;
|
||||
%end;
|
||||
%mend mp_loadformat;
|
||||
%mend mp_loadformat;
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
@param [in] loop_secs= (1) Seconds to wait between each lock attempt
|
||||
|
||||
<h4> SAS Macros </h4>
|
||||
@li mf_fmtdttm.sas
|
||||
@li mp_abort.sas
|
||||
@li mp_lockfilecheck.sas
|
||||
@li mf_getuser.sas
|
||||
@@ -111,7 +112,7 @@ run;
|
||||
LOCK_LIB ="&lib";
|
||||
LOCK_DS="&ds";
|
||||
LOCK_STATUS_CD='LOCKED';
|
||||
LOCK_START_DTTM="%sysfunc(datetime(),E8601DT26.6)"dt;
|
||||
LOCK_START_DTTM="%sysfunc(datetime(),%mf_fmtdttm())"dt;
|
||||
LOCK_USER_NM="&user";
|
||||
LOCK_PID="&sysjobid";
|
||||
LOCK_REF="&ref";
|
||||
@@ -131,7 +132,7 @@ run;
|
||||
proc sql;
|
||||
update &ctl_ds
|
||||
set LOCK_STATUS_CD='LOCKED'
|
||||
, LOCK_START_DTTM="%sysfunc(datetime(),E8601DT26.6)"dt
|
||||
, LOCK_START_DTTM="%sysfunc(datetime(),%mf_fmtdttm())"dt
|
||||
, LOCK_USER_NM="&user"
|
||||
, LOCK_PID="&sysjobid"
|
||||
, LOCK_REF="&ref"
|
||||
@@ -206,7 +207,7 @@ run;
|
||||
proc sql;
|
||||
update &ctl_ds
|
||||
set LOCK_STATUS_CD='UNLOCKED'
|
||||
, LOCK_END_DTTM="%sysfunc(datetime(),E8601DT26.6)"dt
|
||||
, LOCK_END_DTTM="%sysfunc(datetime(),%mf_fmtdttm())"dt
|
||||
, LOCK_USER_NM="&user"
|
||||
, LOCK_PID="&sysjobid"
|
||||
, LOCK_REF="&ref"
|
||||
|
||||
@@ -54,5 +54,5 @@ put(md5(
|
||||
&sep put(md5(trim(put(ifn(missing(&var),&var,&var*1),binary64.))),$hex32.)
|
||||
%let sep=!!;
|
||||
%end;
|
||||
),hex32.)
|
||||
),$hex32.)
|
||||
%mend mp_md5;
|
||||
|
||||
@@ -35,7 +35,7 @@
|
||||
@param infile The QUOTED path to the file on which to perform the substitution
|
||||
@param findvar= Macro variable NAME containing the string to search for
|
||||
@param replacevar= Macro variable NAME containing the replacement string
|
||||
@param outfile= (0) Optional QUOTED path to an the adjusted output file (to
|
||||
@param outfile= (0) Optional QUOTED path to the adjusted output file (to
|
||||
avoid overwriting the first file).
|
||||
|
||||
<h4> SAS Macros </h4>
|
||||
@@ -43,7 +43,9 @@
|
||||
@li mf_getuniquename.sas
|
||||
|
||||
<h4> Related Macros </h4>
|
||||
@li mp_gsubfile.test.sas
|
||||
@li mp_chop.sas
|
||||
@li mp_gsubfile.sas
|
||||
@li mp_replace.test.sas
|
||||
|
||||
@version 9.4
|
||||
@author Bartosz Jabłoński
|
||||
@@ -70,7 +72,7 @@ filename &inref &infile lrecl=1 recfm=n;
|
||||
|
||||
data &ds1;
|
||||
infile &inref;
|
||||
input sourcechar $ 1. @@;
|
||||
input sourcechar $char1. @@;
|
||||
format sourcechar hex2.;
|
||||
run;
|
||||
|
||||
|
||||
@@ -58,6 +58,7 @@
|
||||
|
||||
<h4> SAS Macros </h4>
|
||||
@li mf_existvar.sas
|
||||
@li mf_fmtdttm.sas
|
||||
@li mf_getquotedstr.sas
|
||||
@li mf_getuniquename.sas
|
||||
@li mf_nobs.sas
|
||||
@@ -217,12 +218,12 @@ quit;
|
||||
set keytable="&base_libds"
|
||||
,keycolumn="&retained_key"
|
||||
,max_key=%eval(&maxkey+&newkey_cnt)
|
||||
,processed_dttm="%sysfunc(datetime(),E8601DT26.6)"dt;
|
||||
,processed_dttm="%sysfunc(datetime(),%mf_fmtdttm())"dt;
|
||||
%end;
|
||||
%else %do;
|
||||
update &maxkeytable
|
||||
set max_key=%eval(&maxkey+&newkey_cnt)
|
||||
,processed_dttm="%sysfunc(datetime(),E8601DT26.6)"dt
|
||||
,processed_dttm="%sysfunc(datetime(),%mf_fmtdttm())"dt
|
||||
where keytable="&base_libds";
|
||||
%end;
|
||||
%mp_lockanytable(UNLOCK
|
||||
|
||||
@@ -125,7 +125,7 @@
|
||||
data &ds1;
|
||||
set &dslist indsname=&inds_auto;
|
||||
&hashkey=put(md5(catx('|',%mf_getquotedstr(&key,quote=N))),$hex32.);
|
||||
&inds_keep=&inds_auto;
|
||||
&inds_keep=upcase(&inds_auto);
|
||||
proc sort;
|
||||
by &inds_keep &hashkey;
|
||||
run;
|
||||
@@ -160,8 +160,8 @@ data &ds4;
|
||||
tgtvar_nm=upcase(tgtvar_nm);
|
||||
if tgtvar_nm in (%upcase(&vlist));
|
||||
|
||||
if &inds_auto="&ds2" then tgtvar_type='N';
|
||||
else if &inds_auto="&ds3" then tgtvar_type='C';
|
||||
if upcase(&inds_auto)="&ds2" then tgtvar_type='N';
|
||||
else if upcase(&inds_auto)="&ds3" then tgtvar_type='C';
|
||||
else do;
|
||||
putlog "%str(ERR)OR: unidentified vartype input!" &inds_auto;
|
||||
call symputx('syscc',98);
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
/**
|
||||
@file
|
||||
@brief Streams a file to _webout according to content type
|
||||
@details Will set headers using appropriate functions (SAS 9 vs Viya) and send
|
||||
content as a binary stream.
|
||||
@details Will set headers using appropriate functions per the server type
|
||||
(Viya, EBI, [SASjs Server](https://github.com/sasjs/server)) and stream
|
||||
content using mp_binarycopy().
|
||||
|
||||
Usage:
|
||||
|
||||
@@ -12,7 +13,14 @@
|
||||
|
||||
%mp_streamfile(contenttype=csv,inloc=/some/where.txt,outname=myfile.txt)
|
||||
|
||||
@param [in] contenttype= (TEXT) Either TEXT, ZIP, CSV, EXCEL
|
||||
@param [in] contenttype= (TEXT) Supported:
|
||||
@li CSV
|
||||
@li EXCEL
|
||||
@li MARKDOWN
|
||||
@li TEXT
|
||||
@li ZIP
|
||||
Feel free to submit PRs to support more mime types! The official list is
|
||||
here: https://www.iana.org/assignments/media-types/media-types.xhtml
|
||||
@param [in] inloc= /path/to/file.ext to be sent
|
||||
@param [in] inref= fileref of file to be sent (if provided, overrides `inloc`)
|
||||
@param [in] iftrue= (1=1) Provide a condition under which to execute.
|
||||
@@ -58,7 +66,7 @@ run;
|
||||
%if &contentype=CSV %then %do;
|
||||
%if (&platform=SASMETA and &streamweb=1) %then %do;
|
||||
data _null_;
|
||||
rc=stpsrv_header('Content-type','application/csv');
|
||||
rc=stpsrv_header('Content-Type','application/csv');
|
||||
rc=stpsrv_header('Content-disposition',"attachment; filename=&outname");
|
||||
run;
|
||||
%end;
|
||||
@@ -68,7 +76,7 @@ run;
|
||||
contentdisp="attachment; filename=&outname";
|
||||
%end;
|
||||
%else %if &platform=SASJS %then %do;
|
||||
%mfs_httpheader(Content-type,application/csv)
|
||||
%mfs_httpheader(Content-Type,application/csv)
|
||||
%mfs_httpheader(Content-disposition,%str(attachment; filename=&outname))
|
||||
%end;
|
||||
%end;
|
||||
@@ -76,7 +84,7 @@ run;
|
||||
/* suitable for XLS format */
|
||||
%if (&platform=SASMETA and &streamweb=1) %then %do;
|
||||
data _null_;
|
||||
rc=stpsrv_header('Content-type','application/vnd.ms-excel');
|
||||
rc=stpsrv_header('Content-Type','application/vnd.ms-excel');
|
||||
rc=stpsrv_header('Content-disposition',"attachment; filename=&outname");
|
||||
run;
|
||||
%end;
|
||||
@@ -86,14 +94,14 @@ run;
|
||||
contentdisp="attachment; filename=&outname";
|
||||
%end;
|
||||
%else %if &platform=SASJS %then %do;
|
||||
%mfs_httpheader(Content-type,application/vnd.ms-excel)
|
||||
%mfs_httpheader(Content-Type,application/vnd.ms-excel)
|
||||
%mfs_httpheader(Content-disposition,%str(attachment; filename=&outname))
|
||||
%end;
|
||||
%end;
|
||||
%else %if &contentype=GIF or &contentype=JPEG or &contentype=PNG %then %do;
|
||||
%if (&platform=SASMETA and &streamweb=1) %then %do;
|
||||
data _null_;
|
||||
rc=stpsrv_header('Content-type',"image/%lowcase(&contenttype)");
|
||||
rc=stpsrv_header('Content-Type',"image/%lowcase(&contenttype)");
|
||||
run;
|
||||
%end;
|
||||
%else %if &platform=SASVIYA %then %do;
|
||||
@@ -101,22 +109,30 @@ run;
|
||||
contenttype="image/%lowcase(&contenttype)";
|
||||
%end;
|
||||
%else %if &platform=SASJS %then %do;
|
||||
%mfs_httpheader(Content-type,image/%lowcase(&contenttype))
|
||||
%mfs_httpheader(Content-Type,image/%lowcase(&contenttype))
|
||||
%end;
|
||||
%end;
|
||||
%else %if &contentype=HTML %then %do;
|
||||
%if &platform=SASVIYA %then %do;
|
||||
%else %if &contentype=HTML or &contenttype=MARKDOWN %then %do;
|
||||
%if (&platform=SASMETA and &streamweb=1) %then %do;
|
||||
data _null_;
|
||||
rc=stpsrv_header('Content-Type',"text/%lowcase(&contenttype)");
|
||||
rc=stpsrv_header('Content-disposition',"attachment; filename=&outname");
|
||||
run;
|
||||
%end;
|
||||
%else %if &platform=SASVIYA %then %do;
|
||||
filename &outref filesrvc parenturi="&SYS_JES_JOB_URI" name="_webout.json"
|
||||
contenttype="text/html";
|
||||
contenttype="text/%lowcase(&contenttype)"
|
||||
contentdisp="attachment; filename=&outname";
|
||||
%end;
|
||||
%else %if &platform=SASJS %then %do;
|
||||
%mfs_httpheader(Content-type,text/html)
|
||||
%mfs_httpheader(Content-Type,text/%lowcase(&contenttype))
|
||||
%mfs_httpheader(Content-disposition,%str(attachment; filename=&outname))
|
||||
%end;
|
||||
%end;
|
||||
%else %if &contentype=TEXT %then %do;
|
||||
%if (&platform=SASMETA and &streamweb=1) %then %do;
|
||||
data _null_;
|
||||
rc=stpsrv_header('Content-type','application/text');
|
||||
rc=stpsrv_header('Content-Type','application/text');
|
||||
rc=stpsrv_header('Content-disposition',"attachment; filename=&outname");
|
||||
run;
|
||||
%end;
|
||||
@@ -126,14 +142,14 @@ run;
|
||||
contentdisp="attachment; filename=&outname";
|
||||
%end;
|
||||
%else %if &platform=SASJS %then %do;
|
||||
%mfs_httpheader(Content-type,application/text)
|
||||
%mfs_httpheader(Content-Type,application/text)
|
||||
%mfs_httpheader(Content-disposition,%str(attachment; filename=&outname))
|
||||
%end;
|
||||
%end;
|
||||
%else %if &contentype=WOFF or &contentype=WOFF2 or &contentype=TTF %then %do;
|
||||
%if (&platform=SASMETA and &streamweb=1) %then %do;
|
||||
data _null_;
|
||||
rc=stpsrv_header('Content-type',"font/%lowcase(&contenttype)");
|
||||
rc=stpsrv_header('Content-Type',"font/%lowcase(&contenttype)");
|
||||
run;
|
||||
%end;
|
||||
%else %if &platform=SASVIYA %then %do;
|
||||
@@ -141,13 +157,13 @@ run;
|
||||
contenttype="font/%lowcase(&contenttype)";
|
||||
%end;
|
||||
%else %if &platform=SASJS %then %do;
|
||||
%mfs_httpheader(Content-type,font/%lowcase(&contenttype))
|
||||
%mfs_httpheader(Content-Type,font/%lowcase(&contenttype))
|
||||
%end;
|
||||
%end;
|
||||
%else %if &contentype=XLSX %then %do;
|
||||
%if (&platform=SASMETA and &streamweb=1) %then %do;
|
||||
data _null_;
|
||||
rc=stpsrv_header('Content-type',
|
||||
rc=stpsrv_header('Content-Type',
|
||||
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
|
||||
rc=stpsrv_header('Content-disposition',"attachment; filename=&outname");
|
||||
run;
|
||||
@@ -159,7 +175,7 @@ run;
|
||||
contentdisp="attachment; filename=&outname";
|
||||
%end;
|
||||
%else %if &platform=SASJS %then %do;
|
||||
%mfs_httpheader(Content-type
|
||||
%mfs_httpheader(Content-Type
|
||||
,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
|
||||
)
|
||||
%mfs_httpheader(Content-disposition,%str(attachment; filename=&outname))
|
||||
@@ -168,7 +184,7 @@ run;
|
||||
%else %if &contentype=ZIP %then %do;
|
||||
%if (&platform=SASMETA and &streamweb=1) %then %do;
|
||||
data _null_;
|
||||
rc=stpsrv_header('Content-type','application/zip');
|
||||
rc=stpsrv_header('Content-Type','application/zip');
|
||||
rc=stpsrv_header('Content-disposition',"attachment; filename=&outname");
|
||||
run;
|
||||
%end;
|
||||
@@ -178,7 +194,7 @@ run;
|
||||
contentdisp="attachment; filename=&outname";
|
||||
%end;
|
||||
%else %if &platform=SASJS %then %do;
|
||||
%mfs_httpheader(Content-type,application/zip)
|
||||
%mfs_httpheader(Content-Type,application/zip)
|
||||
%mfs_httpheader(Content-disposition,%str(attachment; filename=&outname))
|
||||
%end;
|
||||
%end;
|
||||
|
||||
@@ -40,7 +40,9 @@
|
||||
@li mf_getuniquename.sas
|
||||
@li mp_abort.sas
|
||||
@li mp_binarycopy.sas
|
||||
@li mp_chop.sas
|
||||
@li mp_ds2csv.sas
|
||||
@li ms_testservice.sas
|
||||
@li mv_getjobresult.sas
|
||||
@li mv_jobflow.sas
|
||||
|
||||
@@ -63,7 +65,7 @@
|
||||
viyaresult=WEBOUT_JSON,
|
||||
viyacontext=SAS Job Execution compute context
|
||||
)/*/STORE SOURCE*/;
|
||||
%local dbg pcnt fref1 webref i webcount var platform;
|
||||
%local dbg pcnt fref1 fref2 webref webrefpath i webcount var platform;
|
||||
%if &mdebug=1 %then %do;
|
||||
%put &sysmacroname entry vars:;
|
||||
%put _local_;
|
||||
@@ -104,10 +106,14 @@
|
||||
%end;
|
||||
%end;
|
||||
|
||||
|
||||
%let fref1=%mf_getuniquefileref();
|
||||
%let webref=%mf_getuniquefileref();
|
||||
%let platform=%mf_getplatform();
|
||||
%let fref1=%mf_getuniquefileref();
|
||||
%let fref2=%mf_getuniquefileref();
|
||||
%let webref=%mf_getuniquefileref();
|
||||
%let webrefpath=%sysfunc(pathname(work))/%mf_getuniquename();
|
||||
/* mp_chop requires a physical path as input */
|
||||
filename &webref "&webrefpath";
|
||||
|
||||
%if &platform=SASMETA %then %do;
|
||||
|
||||
/* parse the input files */
|
||||
@@ -262,13 +268,30 @@
|
||||
mdebug=&mdebug
|
||||
)
|
||||
|
||||
%end;
|
||||
%else %if &platform=SASJS %then %do;
|
||||
|
||||
%ms_testservice(&program
|
||||
,inputfiles=&inputfiles
|
||||
,inputdatasets=&inputdatasets
|
||||
,inputparams=&inputparams
|
||||
,debug=&debug
|
||||
,mdebug=&mdebug
|
||||
,outlib=&outlib
|
||||
,outref=&outref
|
||||
)
|
||||
|
||||
%end;
|
||||
%else %do;
|
||||
%put %str(ERR)OR: Unrecognised platform: &platform;
|
||||
%end;
|
||||
|
||||
%if &mdebug=0 %then %do;
|
||||
filename &webref clear;
|
||||
filename &fref1 clear;
|
||||
%if &platform ne SASJS %then %do;
|
||||
filename &fref2 clear;
|
||||
filename &webref clear;
|
||||
%end;
|
||||
%end;
|
||||
%else %do;
|
||||
%put &sysmacroname exit vars:;
|
||||
|
||||
77
build.py
77
build.py
@@ -4,8 +4,8 @@ from pathlib import Path
|
||||
# Prepare Lua Macros
|
||||
files = [f for f in Path('lua').iterdir() if f.match("*.lua")]
|
||||
for file in files:
|
||||
basename=os.path.basename(file)
|
||||
name='ml_' + os.path.splitext(basename)[0]
|
||||
basename = os.path.basename(file)
|
||||
name = 'ml_' + os.path.splitext(basename)[0]
|
||||
ml = open('lua/' + name + '.sas', "w")
|
||||
ml.write("/**\n")
|
||||
ml.write(" @file " + name + '.sas\n')
|
||||
@@ -20,50 +20,68 @@ for file in files:
|
||||
ml.write(" file \"%sysfunc(pathname(work))/" + name + ".lua\";\n")
|
||||
with open(file) as infile:
|
||||
for line in infile:
|
||||
ml.write(" put '" + line.rstrip().replace("'","''") + " ';\n")
|
||||
ml.write(" put '" + line.rstrip().replace("'", "''") + " ';\n")
|
||||
ml.write("run;\n\n")
|
||||
ml.write("%inc \"%sysfunc(pathname(work))/" + name + ".lua\" /source2;\n\n")
|
||||
|
||||
ml.write("/* ensure big enough lrecl to avoid lua compilation issues */\n")
|
||||
ml.write("%local optval;\n")
|
||||
ml.write("%let optval=%sysfunc(getoption(lrecl));\n")
|
||||
ml.write("options lrecl=1024;\n\n")
|
||||
ml.write("/* execute the lua code by using a .lua extension */\n")
|
||||
ml.write("%inc \"%sysfunc(pathname(work))/" +
|
||||
name + ".lua\" /source2;\n\n")
|
||||
ml.write("options lrecl=&optval;\n\n")
|
||||
ml.write("%mend " + name + ";\n")
|
||||
|
||||
ml.close()
|
||||
|
||||
# prepare web files
|
||||
files=['viya/mv_createwebservice.sas','meta/mm_createwebservice.sas']
|
||||
files = ['viya/mv_createwebservice.sas',
|
||||
'meta/mm_createwebservice.sas', 'server/ms_createwebservice.sas']
|
||||
for file in files:
|
||||
webout0=open('base/mp_jsonout.sas','r')
|
||||
if file=='viya/mv_createwebservice.sas':
|
||||
webout1=open('viya/mv_webout.sas',"r")
|
||||
webout0 = open('base/mp_jsonout.sas', 'r')
|
||||
webout1 = open('base/mf_getuser.sas', 'r')
|
||||
|
||||
if file == 'viya/mv_createwebservice.sas':
|
||||
webout2 = open('viya/mv_webout.sas', "r")
|
||||
weboutfiles = [webout0, webout1, webout2]
|
||||
elif file == 'server/ms_createwebservice.sas':
|
||||
webout2 = open('server/ms_webout.sas', "r")
|
||||
webout3 = open('server/mfs_httpheader.sas', 'r')
|
||||
weboutfiles = [webout0, webout1, webout2, webout3]
|
||||
else:
|
||||
webout1=open('meta/mm_webout.sas','r')
|
||||
webout2=open('base/mf_getuser.sas','r')
|
||||
outfile=open(file + 'TEMP','w')
|
||||
infile=open(file,'r')
|
||||
delrow=0
|
||||
webout2 = open('meta/mm_webout.sas', 'r')
|
||||
weboutfiles = [webout0, webout1, webout2]
|
||||
outfile = open(file + 'TEMP', 'w')
|
||||
infile = open(file, 'r')
|
||||
delrow = 0
|
||||
for line in infile:
|
||||
if line=='/* WEBOUT BEGIN */\n':
|
||||
delrow=1
|
||||
if line == '/* WEBOUT BEGIN */\n':
|
||||
delrow = 1
|
||||
outfile.write('/* WEBOUT BEGIN */\n')
|
||||
weboutfiles=[webout0,webout1,webout2]
|
||||
for weboutfile in weboutfiles:
|
||||
stripcomment=1
|
||||
stripcomment = 1
|
||||
for w in weboutfile:
|
||||
if w=='**/\n': stripcomment=0
|
||||
elif stripcomment==0:
|
||||
outfile.write(" put '" + w.rstrip().replace("'","''") + " ';\n")
|
||||
elif delrow==1 and line=='/* WEBOUT END */\n':
|
||||
delrow=0
|
||||
outfile.write('/* WEBOUT END */\n')
|
||||
elif delrow==0:
|
||||
if w == '**/\n':
|
||||
stripcomment = 0
|
||||
elif stripcomment == 0:
|
||||
outfile.write(
|
||||
" put '" + w.rstrip().replace("'", "''") + " ';\n")
|
||||
elif delrow == 1 and line == '/* WEBOUT END */\n':
|
||||
delrow = 0
|
||||
outfile.write('/* WEBOUT END */\n')
|
||||
elif delrow == 0:
|
||||
outfile.write(line.rstrip() + "\n")
|
||||
webout0.close()
|
||||
webout1.close()
|
||||
webout2.close()
|
||||
outfile.close()
|
||||
infile.close()
|
||||
os.remove(file)
|
||||
os.rename(file + 'TEMP',file)
|
||||
os.rename(file + 'TEMP', file)
|
||||
|
||||
# Concatenate all macros into a single file
|
||||
header="""
|
||||
header = """
|
||||
/**
|
||||
@file
|
||||
@brief Auto-generated file
|
||||
@@ -84,14 +102,15 @@ options noquotelenmax;
|
||||
"""
|
||||
f = open('all.sas', "w") # r / r+ / rb / rb+ / w / wb
|
||||
f.write(header)
|
||||
folders=['base','ddl','meta','metax','server','viya','lua','fcmp']
|
||||
folders = ['base', 'ddl', 'meta', 'metax', 'server', 'viya', 'lua', 'fcmp']
|
||||
for folder in folders:
|
||||
filenames = [fn for fn in Path('./' + folder).iterdir() if fn.match("*.sas")]
|
||||
filenames = [fn for fn in Path(
|
||||
'./' + folder).iterdir() if fn.match("*.sas")]
|
||||
filenames.sort()
|
||||
with open('mc_' + folder + '.sas', 'w') as outfile:
|
||||
for fname in filenames:
|
||||
with open(fname) as infile:
|
||||
outfile.write(infile.read())
|
||||
with open('mc_' + folder + '.sas','r') as c:
|
||||
with open('mc_' + folder + '.sas', 'r') as c:
|
||||
f.write(c.read())
|
||||
f.close()
|
||||
|
||||
@@ -26,9 +26,19 @@
|
||||
oldval_num num format=best32. label='Old (numeric) value',
|
||||
newval_num num format=best32. label='New (numeric) value',
|
||||
oldval_char char(32765) label='Old (character) value',
|
||||
newval_char char(32765) label='New (character) value',
|
||||
constraint pk_mpe_audit
|
||||
primary key(load_ref,libref,dsn,key_hash,tgtvar_nm)
|
||||
newval_char char(32765) label='New (character) value'
|
||||
);
|
||||
|
||||
%local lib;
|
||||
%let libds=%upcase(&libds);
|
||||
%if %index(&libds,.)=0 %then %let lib=WORK;
|
||||
%else %let lib=%scan(&libds,1,.);
|
||||
|
||||
proc datasets lib=&lib noprint;
|
||||
modify %scan(&libds,-1,.);
|
||||
index create
|
||||
pk_mpe_audit=(load_ref libref dsn key_hash tgtvar_nm)
|
||||
/nomiss unique;
|
||||
quit;
|
||||
|
||||
%mend mddl_dc_difftable;
|
||||
@@ -9,19 +9,32 @@
|
||||
|
||||
%macro mddl_dc_filterdetail(libds=WORK.FILTER_DETAIL);
|
||||
|
||||
%local nn lib;
|
||||
%if "%substr(&sysver,1,1)" ne "4" and "%substr(&sysver,1,1)" ne "5" %then %do;
|
||||
%let nn=not null;
|
||||
%end;
|
||||
%else %let nn=;
|
||||
|
||||
proc sql;
|
||||
create table &libds(
|
||||
filter_hash char(32) not null,
|
||||
filter_line num not null,
|
||||
group_logic char(3) not null,
|
||||
subgroup_logic char(3) not null,
|
||||
subgroup_id num not null,
|
||||
variable_nm varchar(32) not null,
|
||||
operator_nm varchar(12) not null,
|
||||
raw_value varchar(4000) not null,
|
||||
processed_dttm num not null format=E8601DT26.6,
|
||||
constraint pk_mpe_filteranytable
|
||||
primary key(filter_hash,filter_line)
|
||||
filter_hash char(32) &nn,
|
||||
filter_line num &nn,
|
||||
group_logic char(3) &nn,
|
||||
subgroup_logic char(3) &nn,
|
||||
subgroup_id num &nn,
|
||||
variable_nm varchar(32) &nn,
|
||||
operator_nm varchar(12) &nn,
|
||||
raw_value varchar(4000) &nn,
|
||||
processed_dttm num &nn format=E8601DT26.6
|
||||
);
|
||||
|
||||
%let libds=%upcase(&libds);
|
||||
%if %index(&libds,.)=0 %then %let lib=WORK;
|
||||
%else %let lib=%scan(&libds,1,.);
|
||||
|
||||
proc datasets lib=&lib noprint;
|
||||
modify %scan(&libds,-1,.);
|
||||
index create pk_mpe_filterdetail=(filter_hash filter_line)/nomiss unique;
|
||||
quit;
|
||||
|
||||
%mend mddl_dc_filterdetail;
|
||||
@@ -9,14 +9,27 @@
|
||||
|
||||
%macro mddl_dc_filtersummary(libds=WORK.FILTER_SUMMARY);
|
||||
|
||||
%local nn lib;
|
||||
%if "%substr(&sysver,1,1)" ne "4" and "%substr(&sysver,1,1)" ne "5" %then %do;
|
||||
%let nn=not null;
|
||||
%end;
|
||||
%else %let nn=;
|
||||
|
||||
proc sql;
|
||||
create table &libds(
|
||||
filter_rk num not null,
|
||||
filter_hash char(32) not null,
|
||||
filter_table char(41) not null,
|
||||
processed_dttm num not null format=E8601DT26.6,
|
||||
constraint pk_mpe_filteranytable
|
||||
primary key(filter_rk)
|
||||
filter_rk num &nn,
|
||||
filter_hash char(32) &nn,
|
||||
filter_table char(41) &nn,
|
||||
processed_dttm num &nn format=E8601DT26.6
|
||||
);
|
||||
|
||||
%let libds=%upcase(&libds);
|
||||
%if %index(&libds,.)=0 %then %let lib=WORK;
|
||||
%else %let lib=%scan(&libds,1,.);
|
||||
|
||||
proc datasets lib=&lib noprint;
|
||||
modify %scan(&libds,-1,.);
|
||||
index create filter_rk /nomiss unique;
|
||||
quit;
|
||||
|
||||
%mend mddl_dc_filtersummary;
|
||||
@@ -9,17 +9,33 @@
|
||||
|
||||
%macro mddl_dc_locktable(libds=WORK.LOCKTABLE);
|
||||
|
||||
%local nn lib;
|
||||
%if "%substr(&sysver,1,1)" ne "4" and "%substr(&sysver,1,1)" ne "5" %then %do;
|
||||
%let nn=not null;
|
||||
%end;
|
||||
%else %let nn=;
|
||||
|
||||
proc sql;
|
||||
create table &libds(
|
||||
lock_lib char(8),
|
||||
lock_ds char(32),
|
||||
lock_status_cd char(10) not null,
|
||||
lock_user_nm char(100) not null ,
|
||||
lock_status_cd char(10) &nn,
|
||||
lock_user_nm char(100) &nn ,
|
||||
lock_ref char(200),
|
||||
lock_pid char(10),
|
||||
lock_start_dttm num format=E8601DT26.6,
|
||||
lock_end_dttm num format=E8601DT26.6,
|
||||
constraint pk_mp_lockanytable primary key(lock_lib,lock_ds)
|
||||
lock_end_dttm num format=E8601DT26.6
|
||||
);
|
||||
|
||||
%let libds=%upcase(&libds);
|
||||
%if %index(&libds,.)=0 %then %let lib=WORK;
|
||||
%else %let lib=%scan(&libds,1,.);
|
||||
|
||||
proc datasets lib=&lib noprint;
|
||||
modify %scan(&libds,-1,.);
|
||||
index create
|
||||
pk_mp_lockanytable=(lock_lib lock_ds)
|
||||
/nomiss unique;
|
||||
quit;
|
||||
|
||||
%mend mddl_dc_locktable;
|
||||
@@ -17,8 +17,17 @@
|
||||
max_key num label=
|
||||
'Integer representing current max RK or SK value in the KEYTABLE',
|
||||
processed_dttm num format=E8601DT26.6
|
||||
label='Datetime this value was last updated',
|
||||
constraint pk_mpe_maxkeyvalues
|
||||
primary key(keytable));
|
||||
label='Datetime this value was last updated'
|
||||
);
|
||||
|
||||
%local lib;
|
||||
%let libds=%upcase(&libds);
|
||||
%if %index(&libds,.)=0 %then %let lib=WORK;
|
||||
%else %let lib=%scan(&libds,1,.);
|
||||
|
||||
proc datasets lib=&lib noprint;
|
||||
modify %scan(&libds,-1,.);
|
||||
index create keytable /nomiss unique;
|
||||
quit;
|
||||
|
||||
%mend mddl_dc_maxkeytable;
|
||||
@@ -14,8 +14,8 @@ proc sql;
|
||||
create table &libds(
|
||||
FMTNAME char(32) label='Format name'
|
||||
/*
|
||||
to accomodate larger START values, mp_loadformat.sas will need the
|
||||
SQL dependency removed (proc sql needs to accomodate 3 index values in
|
||||
to accommodate larger START values, mp_loadformat.sas will need the
|
||||
SQL dependency removed (proc sql needs to accommodate 3 index values in
|
||||
a 32767 ibufsize limit)
|
||||
*/
|
||||
,START char(10000) label='Starting value for format'
|
||||
@@ -40,4 +40,4 @@ create table &libds(
|
||||
,LANGUAGE char(8) label='Language for date strings'
|
||||
);
|
||||
|
||||
%mend mddl_sas_cntlout;
|
||||
%mend mddl_sas_cntlout;
|
||||
|
||||
@@ -39,6 +39,14 @@ data _null_;
|
||||
put 'io.close(file) ';
|
||||
run;
|
||||
|
||||
/* ensure big enough lrecl to avoid lua compilation issues */
|
||||
%local optval;
|
||||
%let optval=%sysfunc(getoption(lrecl));
|
||||
options lrecl=1024;
|
||||
|
||||
/* execute the lua code by using a .lua extension */
|
||||
%inc "%sysfunc(pathname(work))/ml_gsubfile.lua" /source2;
|
||||
|
||||
options lrecl=&optval;
|
||||
|
||||
%mend ml_gsubfile;
|
||||
|
||||
@@ -389,6 +389,14 @@ data _null_;
|
||||
put '-- JSON.LUA ENDS HERE ';
|
||||
run;
|
||||
|
||||
/* ensure big enough lrecl to avoid lua compilation issues */
|
||||
%local optval;
|
||||
%let optval=%sysfunc(getoption(lrecl));
|
||||
options lrecl=1024;
|
||||
|
||||
/* execute the lua code by using a .lua extension */
|
||||
%inc "%sysfunc(pathname(work))/ml_json.lua" /source2;
|
||||
|
||||
options lrecl=&optval;
|
||||
|
||||
%mend ml_json;
|
||||
|
||||
11
main.dox
11
main.dox
@@ -92,4 +92,15 @@
|
||||
* Auto-generated from the plain source `.lua` files in the same directory
|
||||
* Prefixes: _ml_
|
||||
|
||||
*/
|
||||
|
||||
/*! \dir ddl
|
||||
* \brief Data Definition Language files
|
||||
* \details Provides templates for commonly used tables in sasjs/core.
|
||||
Attributes:
|
||||
|
||||
* OS independent
|
||||
* No X command
|
||||
* Prefixes: _mddl_
|
||||
|
||||
*/
|
||||
@@ -13,8 +13,8 @@
|
||||
<h4> SAS Macros </h4>
|
||||
@li mp_abort.sas
|
||||
|
||||
@param libref the libref (not name) of the metadata library
|
||||
@param mAbort= If not assigned, HARD will call %mp_abort(), SOFT will
|
||||
@param [in] libref The libref (not name) of the metadata library
|
||||
@param [in] mAbort= If not assigned, HARD will call %mp_abort(), SOFT will
|
||||
silently return
|
||||
|
||||
@returns libname statement
|
||||
@@ -28,11 +28,11 @@
|
||||
libref
|
||||
,mAbort=HARD
|
||||
)/*/STORE SOURCE*/;
|
||||
|
||||
%local mp_abort msg;
|
||||
%let mp_abort=0;
|
||||
%if %sysfunc(libref(&libref)) %then %do;
|
||||
%local mp_abort msg; %let mp_abort=0;
|
||||
data _null_;
|
||||
length liburi LibName $200;
|
||||
length liburi LibName msg $200;
|
||||
call missing(of _all_);
|
||||
nobj=metadata_getnobj("omsobj:SASLibrary?@Libref='&libref'",1,liburi);
|
||||
if nobj=1 then do;
|
||||
@@ -46,7 +46,24 @@
|
||||
* not always helpful though. One example, previously received:
|
||||
* NOTE: Libref XX refers to the same library metadata as libref XX.
|
||||
*/
|
||||
call symputx('msg',sysmsg(),'l');
|
||||
msg=sysmsg();
|
||||
if msg=:'ERROR: Libref SAVE is not assigned.' then do;
|
||||
msg=catx(" ",
|
||||
"Could not assign %upcase(&libref).",
|
||||
"Please check metadata permissions! Libname:",libname,
|
||||
"Liburi:",liburi
|
||||
);
|
||||
end;
|
||||
else if msg="ERROR: User does not have appropriate authorization "!!
|
||||
"level for library SAVE."
|
||||
then do;
|
||||
msg=catx(" ",
|
||||
"ERROR: User does not have appropriate authorization level",
|
||||
"for library %upcase(&libref), libname:",libname,
|
||||
"Liburi:",liburi
|
||||
);
|
||||
end;
|
||||
call symputx('msg',msg,'l');
|
||||
if "&mabort"='HARD' then call symputx('mp_abort',1,'l');
|
||||
end;
|
||||
else do;
|
||||
@@ -65,20 +82,16 @@
|
||||
end;
|
||||
run;
|
||||
|
||||
%if &mp_abort=1 %then %do;
|
||||
%mp_abort(iftrue= (&mp_abort=1)
|
||||
,mac=mm_assignlib.sas
|
||||
,msg=&msg
|
||||
)
|
||||
%return;
|
||||
%end;
|
||||
%else %if %length(&msg)>2 %then %do;
|
||||
%put NOTE: &msg;
|
||||
%return;
|
||||
%end;
|
||||
%put NOTE: &msg;
|
||||
|
||||
%end;
|
||||
%else %do;
|
||||
%put NOTE: Library &libref is already assigned;
|
||||
%end;
|
||||
|
||||
%mp_abort(iftrue= (&mp_abort=1)
|
||||
,mac=mm_assignlib.sas
|
||||
,msg=%superq(msg)
|
||||
)
|
||||
|
||||
%mend mm_assignlib;
|
||||
|
||||
@@ -103,7 +103,7 @@ data _null_;
|
||||
put ' ';
|
||||
put '%if &action=OPEN %then %do; ';
|
||||
put ' options nobomfile; ';
|
||||
put ' data _null_;file &jref encoding=''utf-8'' ; ';
|
||||
put ' data _null_;file &jref encoding=''utf-8'' lrecl=200; ';
|
||||
put ' put ''{"PROCESSED_DTTM" : "'' "%sysfunc(datetime(),E8601DT26.6)" ''"''; ';
|
||||
put ' run; ';
|
||||
put '%end; ';
|
||||
@@ -232,12 +232,23 @@ data _null_;
|
||||
put ' format _numeric_ bart.; ';
|
||||
put ' %do i=1 %to &numcols; ';
|
||||
put ' %if &&typelong&i=char or &fmt=Y %then %do; ';
|
||||
put ' &&name&i=''"''!!trim(prxchange(''s/"/\"/'',-1, ';
|
||||
put ' prxchange(''s/''!!''0A''x!!''/\n/'',-1, ';
|
||||
put ' prxchange(''s/''!!''0D''x!!''/\r/'',-1, ';
|
||||
put ' prxchange(''s/''!!''09''x!!''/\t/'',-1, ';
|
||||
put ' prxchange(''s/\\/\\\\/'',-1,&&name&i) ';
|
||||
put ' )))))!!''"''; ';
|
||||
put ' if findc(&&name&i,''"\''!!''0A0D09000E0F01021011''x) then do; ';
|
||||
put ' &&name&i=''"''!!trim( ';
|
||||
put ' prxchange(''s/"/\\"/'',-1, /* double quote */ ';
|
||||
put ' prxchange(''s/\x0A/\n/'',-1, /* new line */ ';
|
||||
put ' prxchange(''s/\x0D/\r/'',-1, /* carriage return */ ';
|
||||
put ' prxchange(''s/\x09/\\t/'',-1, /* tab */ ';
|
||||
put ' prxchange(''s/\x00/\\u0000/'',-1, /* NUL */ ';
|
||||
put ' prxchange(''s/\x0E/\\u000E/'',-1, /* SS */ ';
|
||||
put ' prxchange(''s/\x0F/\\u000F/'',-1, /* SF */ ';
|
||||
put ' prxchange(''s/\x01/\\u0001/'',-1, /* SOH */ ';
|
||||
put ' prxchange(''s/\x02/\\u0002/'',-1, /* STX */ ';
|
||||
put ' prxchange(''s/\x10/\\u0010/'',-1, /* DLE */ ';
|
||||
put ' prxchange(''s/\x11/\\u0011/'',-1, /* DC1 */ ';
|
||||
put ' prxchange(''s/\\/\\\\/'',-1,&&name&i) ';
|
||||
put ' ))))))))))))!!''"''; ';
|
||||
put ' end; ';
|
||||
put ' else &&name&i=quote(cats(&&name&i)); ';
|
||||
put ' %end; ';
|
||||
put ' %end; ';
|
||||
put ' run; ';
|
||||
@@ -303,6 +314,25 @@ data _null_;
|
||||
put ' run; ';
|
||||
put '%end; ';
|
||||
put '%mend mp_jsonout; ';
|
||||
put ' ';
|
||||
put '%macro mf_getuser(type=META ';
|
||||
put ')/*/STORE SOURCE*/; ';
|
||||
put ' %local user metavar; ';
|
||||
put ' %if &type=OS %then %let metavar=_secureusername; ';
|
||||
put ' %else %let metavar=_metaperson; ';
|
||||
put ' ';
|
||||
put ' %if %symexist(SYS_COMPUTE_SESSION_OWNER) %then %let user=&SYS_COMPUTE_SESSION_OWNER; ';
|
||||
put ' %else %if %symexist(&metavar) %then %do; ';
|
||||
put ' %if %length(&&&metavar)=0 %then %let user=&sysuserid; ';
|
||||
put ' /* sometimes SAS will add @domain extension - remove for consistency */ ';
|
||||
put ' /* but be sure to quote in case of usernames with commas */ ';
|
||||
put ' %else %let user=%unquote(%scan(%quote(&&&metavar),1,@)); ';
|
||||
put ' %end; ';
|
||||
put ' %else %let user=&sysuserid; ';
|
||||
put ' ';
|
||||
put ' %quote(&user) ';
|
||||
put ' ';
|
||||
put '%mend mf_getuser; ';
|
||||
put '%macro mm_webout(action,ds,dslabel=,fref=_webout,fmt=Y,missing=NULL ';
|
||||
put ' ,showmeta=NO ';
|
||||
put '); ';
|
||||
@@ -421,14 +451,16 @@ data _null_;
|
||||
put ' put '',"_METAPERSON": '' _METAPERSON; ';
|
||||
put ' put '',"_PROGRAM" : '' _PROGRAM ; ';
|
||||
put ' put ",""SYSCC"" : ""&syscc"" "; ';
|
||||
put ' put ",""SYSERRORTEXT"" : ""&syserrortext"" "; ';
|
||||
put ' syserrortext=quote(cats(symget(''SYSERRORTEXT''))); ';
|
||||
put ' put '',"SYSERRORTEXT" : '' syserrortext; ';
|
||||
put ' put ",""SYSHOSTNAME"" : ""&syshostname"" "; ';
|
||||
put ' put ",""SYSJOBID"" : ""&sysjobid"" "; ';
|
||||
put ' put ",""SYSSCPL"" : ""&sysscpl"" "; ';
|
||||
put ' put ",""SYSSITE"" : ""&syssite"" "; ';
|
||||
put ' sysvlong=quote(trim(symget(''sysvlong''))); ';
|
||||
put ' put '',"SYSVLONG" : '' sysvlong; ';
|
||||
put ' put ",""SYSWARNINGTEXT"" : ""&syswarningtext"" "; ';
|
||||
put ' syswarningtext=quote(cats(symget(''SYSWARNINGTEXT''))); ';
|
||||
put ' put '',"SYSWARNINGTEXT" : '' syswarningtext; ';
|
||||
put ' put '',"END_DTTM" : "'' "%sysfunc(datetime(),E8601DT26.6)" ''" ''; ';
|
||||
put ' length memsize $32; ';
|
||||
put ' memsize="%sysfunc(INPUTN(%sysfunc(getoption(memsize)), best.),sizekmg.)"; ';
|
||||
@@ -442,24 +474,6 @@ data _null_;
|
||||
put '%end; ';
|
||||
put ' ';
|
||||
put '%mend mm_webout; ';
|
||||
put ' ';
|
||||
put '%macro mf_getuser(type=META ';
|
||||
put ')/*/STORE SOURCE*/; ';
|
||||
put ' %local user metavar; ';
|
||||
put ' %if &type=OS %then %let metavar=_secureusername; ';
|
||||
put ' %else %let metavar=_metaperson; ';
|
||||
put ' ';
|
||||
put ' %if %symexist(SYS_COMPUTE_SESSION_OWNER) %then %let user=&SYS_COMPUTE_SESSION_OWNER; ';
|
||||
put ' %else %if %symexist(&metavar) %then %do; ';
|
||||
put ' %if %length(&&&metavar)=0 %then %let user=&sysuserid; ';
|
||||
put ' /* sometimes SAS will add @domain extension - remove for consistency */ ';
|
||||
put ' %else %let user=%scan(&&&metavar,1,@); ';
|
||||
put ' %end; ';
|
||||
put ' %else %let user=&sysuserid; ';
|
||||
put ' ';
|
||||
put ' %quote(&user) ';
|
||||
put ' ';
|
||||
put '%mend mf_getuser; ';
|
||||
/* WEBOUT END */
|
||||
put '%macro webout(action,ds,dslabel=,fmt=,missing=NULL,showmeta=NO);';
|
||||
put ' %mm_webout(&action,ds=&ds,dslabel=&dslabel,fmt=&fmt,missing=&missing';
|
||||
|
||||
@@ -78,6 +78,7 @@ filename &fname2 clear;
|
||||
%local isgone;
|
||||
data _null_;
|
||||
length type uri $256;
|
||||
call missing (of _all_);
|
||||
rc=metadata_resolve("omsobj:SASLibrary?@Id='&liburi'",type,uri);
|
||||
call symputx('isgone',type,'l');
|
||||
run;
|
||||
|
||||
@@ -49,20 +49,25 @@
|
||||
|
||||
|
||||
<h4> SAS Macros </h4>
|
||||
@li mf_getuniquefileref.sas
|
||||
@li mf_getuniquename.sas
|
||||
@li mf_isblank.sas
|
||||
@li mf_loc.sas
|
||||
@li mm_tree.sas
|
||||
@li mf_getuniquefileref.sas
|
||||
@li mf_isblank.sas
|
||||
@li mp_abort.sas
|
||||
|
||||
@param metaloc= the metadata folder to export
|
||||
@param secureref= fileref containing the username / password (should point to
|
||||
a file in a secure location). Leave blank to substitute $bash type vars.
|
||||
@param outref= fileref to which to write the command
|
||||
@param cmdoutloc= the directory to which the command will write the SPK
|
||||
(default=WORK)
|
||||
@param cmdoutname= the name of the spk / log files to create (will be
|
||||
identical just with .spk or .log extension)
|
||||
|
||||
@param [in] metaloc= the metadata folder to export
|
||||
@param [in] secureref= fileref containing the username / password (should
|
||||
point to a file in a secure location). Leave blank to substitute $bash vars.
|
||||
@param [in] excludevars= (0) A space seperated list of macro variable names,
|
||||
each of which contains a value that should be used to filter the output
|
||||
objects.
|
||||
@param [out] outref= fileref to which to write the command
|
||||
@param [out] cmdoutloc= (%sysfunc(pathname(work))) The directory to which the
|
||||
command will write the SPK
|
||||
@param [out] cmdoutname= (mmxport) The name of the spk / log files to create
|
||||
(will be identical just with .spk or .log extension)
|
||||
|
||||
@version 9.4
|
||||
@author Allan Bowe
|
||||
@@ -71,6 +76,7 @@
|
||||
|
||||
%macro mm_spkexport(metaloc=
|
||||
,secureref=
|
||||
,excludevars=0
|
||||
,outref=
|
||||
,cmdoutloc=%sysfunc(pathname(work))
|
||||
,cmdoutname=mmxport
|
||||
@@ -82,7 +88,7 @@
|
||||
%end;
|
||||
|
||||
/* set creds */
|
||||
%local mmxuser mmxpath;
|
||||
%local mmxuser mmxpath i var;
|
||||
%let mmxuser=$1;
|
||||
%let mmxpass=$2;
|
||||
%if %mf_isblank(&secureref)=0 %then %do;
|
||||
@@ -90,35 +96,51 @@
|
||||
%end;
|
||||
|
||||
/* setup metadata connection options */
|
||||
%local host port platform_object_path connx_string;
|
||||
%local host port platform_object_path ds;
|
||||
%let host=%sysfunc(getoption(metaserver));
|
||||
%let port=%sysfunc(getoption(metaport));
|
||||
%let platform_object_path=%mf_loc(POF);
|
||||
%let ds=%mf_getuniquename(prefix=spkexportable);
|
||||
|
||||
%let connx_string=%str(-host &host -port &port -user &mmxuser %trim(
|
||||
)-password &mmxpass);
|
||||
|
||||
%mm_tree(root=%str(&metaloc) ,types=EXPORTABLE ,outds=exportable)
|
||||
%mm_tree(root=%str(&metaloc),types=EXPORTABLE ,outds=&ds)
|
||||
|
||||
%if %mf_isblank(&outref)=1 %then %let outref=%mf_getuniquefileref();
|
||||
|
||||
data _null_;
|
||||
set exportable end=last;
|
||||
set &ds end=last;
|
||||
file &outref lrecl=32767;
|
||||
length str $32767;
|
||||
if _n_=1 then do;
|
||||
put "# Script generated by &sysuserid on %sysfunc(datetime(),datetime19.)";
|
||||
put "cd ""&platform_object_path"" \";
|
||||
put "; ./ExportPackage &connx_string -disableX11 \";
|
||||
put " -package ""&cmdoutloc/&cmdoutname..spk"" \";
|
||||
put "; ./ExportPackage -host &host -port &port -user &mmxuser \";
|
||||
put " -disableX11 -password &mmxpass \";
|
||||
put " -package ""&cmdoutloc/&cmdoutname..spk"" \";
|
||||
end;
|
||||
/* exclude particular patterns from the exported SPK */
|
||||
%if "&excludevars" ne "0" %then %do;
|
||||
%do i=1 %to %sysfunc(countw(&excludevars));
|
||||
%let var=%scan(&excludevars,&i);
|
||||
if _n_=1 then do;
|
||||
length excludestr&i $1000;
|
||||
retain excludestr&i;
|
||||
excludestr&i=symget("&var");
|
||||
putlog excludestr&i=;
|
||||
putlog path=;
|
||||
end;
|
||||
if index(path,cats(excludestr&i))=0 and index(name,cats(excludestr&i))=0;
|
||||
%end;
|
||||
/* ignore top level folder else all subcontent will be exported regardless */
|
||||
if _n_>1;
|
||||
%end;
|
||||
str=' -objects '!!cats('"',path,'/',name,"(",publictype,')" \');
|
||||
put str;
|
||||
if last then put " -log ""&cmdoutloc/&cmdoutname..log"" 2>&1 ";
|
||||
run;
|
||||
|
||||
%mp_abort(iftrue= (&syscc ne 0)
|
||||
,mac=&sysmacroname
|
||||
,mac=mm_spkexport
|
||||
,msg=%str(syscc=&syscc)
|
||||
)
|
||||
|
||||
%mend mm_spkexport;
|
||||
%mend mm_spkexport;
|
||||
|
||||
@@ -157,14 +157,16 @@
|
||||
put ',"_METAPERSON": ' _METAPERSON;
|
||||
put ',"_PROGRAM" : ' _PROGRAM ;
|
||||
put ",""SYSCC"" : ""&syscc"" ";
|
||||
put ",""SYSERRORTEXT"" : ""&syserrortext"" ";
|
||||
syserrortext=quote(cats(symget('SYSERRORTEXT')));
|
||||
put ',"SYSERRORTEXT" : ' syserrortext;
|
||||
put ",""SYSHOSTNAME"" : ""&syshostname"" ";
|
||||
put ",""SYSJOBID"" : ""&sysjobid"" ";
|
||||
put ",""SYSSCPL"" : ""&sysscpl"" ";
|
||||
put ",""SYSSITE"" : ""&syssite"" ";
|
||||
sysvlong=quote(trim(symget('sysvlong')));
|
||||
put ',"SYSVLONG" : ' sysvlong;
|
||||
put ",""SYSWARNINGTEXT"" : ""&syswarningtext"" ";
|
||||
syswarningtext=quote(cats(symget('SYSWARNINGTEXT')));
|
||||
put ',"SYSWARNINGTEXT" : ' syswarningtext;
|
||||
put ',"END_DTTM" : "' "%sysfunc(datetime(),E8601DT26.6)" '" ';
|
||||
length memsize $32;
|
||||
memsize="%sysfunc(INPUTN(%sysfunc(getoption(memsize)), best.),sizekmg.)";
|
||||
|
||||
2443
package-lock.json
generated
2443
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -26,14 +26,13 @@
|
||||
"homepage": "https://core.sasjs.io",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"build": "sasjs cbd -t viya",
|
||||
"build": "sasjs cbd -t server",
|
||||
"docs": "sasjs doc -t docsonly && ./sasjs/utils/build.sh",
|
||||
"test": "sasjs test -t viya",
|
||||
"test": "sasjs test -t server",
|
||||
"lint": "sasjs lint",
|
||||
"prepare": "git rev-parse --git-dir && git config core.hooksPath ./.git-hooks || true"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@sasjs/cli": "3.6.0",
|
||||
"@sasjs/core": "4.4.7"
|
||||
"@sasjs/cli": "3.13.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,11 +4,8 @@
|
||||
"base",
|
||||
"ddl",
|
||||
"fcmp",
|
||||
"meta",
|
||||
"metax",
|
||||
"server",
|
||||
"viya",
|
||||
"lua",
|
||||
"server",
|
||||
"tests/crossplatform",
|
||||
"tests/ddl"
|
||||
],
|
||||
@@ -28,36 +25,33 @@
|
||||
"mcTestAppLoc": "/Public/temp/macrocore"
|
||||
}
|
||||
},
|
||||
"defaultTarget": "viya",
|
||||
"defaultTarget": "server",
|
||||
"targets": [
|
||||
{
|
||||
"name": "viya",
|
||||
"serverUrl": "https://sas.analytium.co.uk",
|
||||
"serverUrl": "",
|
||||
"serverType": "SASVIYA",
|
||||
"httpsAgentOptions": {
|
||||
"allowInsecureRequests": false
|
||||
},
|
||||
"appLoc": "/Public/temp/macrocore",
|
||||
"appLoc": "/Public/app/macrocore",
|
||||
"macroFolders": [
|
||||
"viya",
|
||||
"tests/viyaonly"
|
||||
],
|
||||
"programFolders": [],
|
||||
"binaryFolders": [],
|
||||
"deployConfig": {
|
||||
"deployServicePack": true,
|
||||
"deployScripts": []
|
||||
},
|
||||
"contextName": "SAS Job Execution compute context"
|
||||
},
|
||||
{
|
||||
"name": "sas9",
|
||||
"serverUrl": "https://sas.analytium.co.uk:8343",
|
||||
"serverUrl": "",
|
||||
"serverType": "SAS9",
|
||||
"httpsAgentOptions": {
|
||||
"allowInsecureRequests": false
|
||||
},
|
||||
"appLoc": "/Shared Data/temp/macrocore",
|
||||
"macroFolders": [
|
||||
"meta",
|
||||
"metax",
|
||||
"tests/sas9only"
|
||||
],
|
||||
"programFolders": [],
|
||||
@@ -71,28 +65,28 @@
|
||||
},
|
||||
{
|
||||
"name": "server",
|
||||
"serverUrl": "https://sas.analytium.co.uk:5000",
|
||||
"serverUrl": "https://sas.analytium.co.uk:5007",
|
||||
"serverType": "SASJS",
|
||||
"httpsAgentOptions": {
|
||||
"allowInsecureRequests": false
|
||||
},
|
||||
"appLoc": "/sasjs/core",
|
||||
"macroFolders": [
|
||||
"server",
|
||||
"tests/serveronly"
|
||||
],
|
||||
"programFolders": [],
|
||||
"binaryFolders": [],
|
||||
"deployConfig": {
|
||||
"deployServicePack": true,
|
||||
"deployScripts": []
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "docsonly",
|
||||
"serverType": "SAS9",
|
||||
"serverType": "SASJS",
|
||||
"appLoc": "dummy",
|
||||
"macroFolders": [
|
||||
"meta",
|
||||
"metax",
|
||||
"server",
|
||||
"viya",
|
||||
"tests/sas9only",
|
||||
"tests/serveronly",
|
||||
"tests/viyaonly"
|
||||
]
|
||||
},
|
||||
@@ -102,6 +96,7 @@
|
||||
"serverType": "SASVIYA",
|
||||
"appLoc": "/Public/temp/macrocore",
|
||||
"macroFolders": [
|
||||
"viya",
|
||||
"tests/viyaonly"
|
||||
],
|
||||
"deployConfig": {
|
||||
@@ -110,4 +105,4 @@
|
||||
"contextName": "SAS Job Execution compute context"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
|
||||
Usage:
|
||||
|
||||
%mfs_httpheader(Content-type,application/csv)
|
||||
%mfs_httpheader(Content-Type,application/csv)
|
||||
|
||||
@param [in] header_name Name of the http header to set
|
||||
@param [in] header_value Value of the http header to set
|
||||
@@ -28,6 +28,7 @@
|
||||
%macro mfs_httpheader(header_name
|
||||
,header_value
|
||||
)/*/STORE SOURCE*/;
|
||||
%global sasjs_stpsrv_header_loc;
|
||||
%local fref fid i;
|
||||
|
||||
%if %sysfunc(filename(fref,&sasjs_stpsrv_header_loc)) ne 0 %then %do;
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
@li mf_getuniquefileref.sas
|
||||
@li mf_getuniquename.sas
|
||||
@li mp_abort.sas
|
||||
@li ms_deletefile.sas
|
||||
|
||||
**/
|
||||
|
||||
@@ -29,14 +30,22 @@
|
||||
,mdebug=0
|
||||
);
|
||||
|
||||
%local fname0 fname1 boundary fname statcd msg;
|
||||
/* first, delete in case it exists */
|
||||
%ms_deletefile(&driveloc,mdebug=&mdebug)
|
||||
|
||||
%local fname0 fname1 fname2 boundary fname statcd msg optval;
|
||||
%let fname0=%mf_getuniquefileref();
|
||||
%let fname1=%mf_getuniquefileref();
|
||||
%let fname2=%mf_getuniquefileref();
|
||||
%let boundary=%mf_getuniquename();
|
||||
|
||||
/* avoid sending bom marker to API */
|
||||
%let optval=%sysfunc(getoption(bomfile));
|
||||
options nobomfile;
|
||||
|
||||
data _null_;
|
||||
file &fname0 termstr=crlf;
|
||||
infile &inref end=eof;
|
||||
file &fname0 termstr=crlf lrecl=32767;
|
||||
infile &inref end=eof lrecl=32767;
|
||||
if _n_ = 1 then do;
|
||||
put "--&boundary.";
|
||||
put 'Content-Disposition: form-data; name="filePath"';
|
||||
@@ -55,17 +64,28 @@ data _null_;
|
||||
end;
|
||||
run;
|
||||
|
||||
data _null_;
|
||||
file &fname1 lrecl=1000;
|
||||
infile "&_sasjs_tokenfile" lrecl=1000;
|
||||
input;
|
||||
if _n_=1 then put "Content-Type: multipart/form-data; boundary=&boundary";
|
||||
put _infile_;
|
||||
run;
|
||||
|
||||
%if &mdebug=1 %then %do;
|
||||
data _null_;
|
||||
infile &fname0;
|
||||
infile &fname0 lrecl=32767;
|
||||
input;
|
||||
put _infile_;
|
||||
data _null_;
|
||||
infile &fname1 lrecl=32767;
|
||||
input;
|
||||
put _infile_;
|
||||
run;
|
||||
%end;
|
||||
|
||||
proc http method='POST' in=&fname0 out=&fname1
|
||||
proc http method='POST' in=&fname0 headerin=&fname1 out=&fname2
|
||||
url="&_sasjs_apiserverurl/SASjsApi/drive/file";
|
||||
headers "Content-Type"="multipart/form-data; boundary=&boundary";
|
||||
%if &mdebug=1 %then %do;
|
||||
debug level=1;
|
||||
%end;
|
||||
@@ -73,7 +93,7 @@ run;
|
||||
|
||||
%let statcd=0;
|
||||
data _null_;
|
||||
infile &fname1;
|
||||
infile &fname2;
|
||||
input;
|
||||
putlog _infile_;
|
||||
if _infile_='{"status":"success"}' then call symputx('statcd',1,'l');
|
||||
@@ -86,4 +106,7 @@ run;
|
||||
,msg=%superq(msg)
|
||||
)
|
||||
|
||||
/* reset options */
|
||||
options &optval;
|
||||
|
||||
%mend ms_createfile;
|
||||
|
||||
146
server/ms_createuser.sas
Normal file
146
server/ms_createuser.sas
Normal file
@@ -0,0 +1,146 @@
|
||||
/**
|
||||
@file
|
||||
@brief Creates a user on SASjs Server
|
||||
@details Creates a user on SASjs Server with the following attributes:
|
||||
|
||||
@li UserName
|
||||
@li Password
|
||||
@li isAdmin
|
||||
@li displayName
|
||||
|
||||
The userid is created by sasjs/server. All users are created with `isActive`
|
||||
set to `true`.
|
||||
|
||||
Example:
|
||||
|
||||
%ms_createuser(newuser,secretpass,displayname=New User!)
|
||||
|
||||
@param [in] username The username to apply. No spaces or special characters.
|
||||
@param [in] password The initial password to set.
|
||||
@param [in] isadmin= (false) Set to true to give the user admin rights
|
||||
@param [in] displayName= (0) Set a friendly name (spaces & special characters
|
||||
are ok). If not provided, username will be used instead.
|
||||
@param [in] mdebug= (0) Set to 1 to enable DEBUG messages
|
||||
@param [out] outds= (work.ms_createuser) This output dataset will contain the
|
||||
values from the JSON response (such as the id of the new user)
|
||||
|ID:best.|DISPLAYNAME:$8.|USERNAME:$8.|ISACTIVE:best.|ISADMIN:best.|
|
||||
|---|---|---|---|---|
|
||||
|`6 `|`New User `|`newuser `|`1 `|`0 `|
|
||||
|
||||
|
||||
|
||||
<h4> SAS Macros </h4>
|
||||
@li mf_getuniquefileref.sas
|
||||
@li mf_getuniquelibref.sas
|
||||
@li mp_abort.sas
|
||||
|
||||
<h4> Related Files </h4>
|
||||
@li ms_createuser.test.sas
|
||||
@li ms_getusers.sas
|
||||
|
||||
**/
|
||||
|
||||
%macro ms_createuser(username,password
|
||||
,isadmin=false
|
||||
,displayname=0
|
||||
,outds=work.ms_createuser
|
||||
,mdebug=0
|
||||
);
|
||||
|
||||
%mp_abort(
|
||||
iftrue=(&syscc ne 0)
|
||||
,mac=ms_createuser.sas
|
||||
,msg=%str(syscc=&syscc on macro entry)
|
||||
)
|
||||
|
||||
%local fref0 fref1 fref2 libref optval rc msg;
|
||||
%let fref0=%mf_getuniquefileref();
|
||||
%let fref1=%mf_getuniquefileref();
|
||||
%let fref2=%mf_getuniquefileref();
|
||||
%let libref=%mf_getuniquelibref();
|
||||
|
||||
/* avoid sending bom marker to API */
|
||||
%let optval=%sysfunc(getoption(bomfile));
|
||||
options nobomfile;
|
||||
|
||||
data _null_;
|
||||
file &fref0 termstr=crlf;
|
||||
username=quote(cats(symget('username')));
|
||||
password=quote(cats(symget('password')));
|
||||
isadmin=symget('isadmin');
|
||||
displayname=quote(cats(symget('displayname')));
|
||||
if displayname='"0"' then displayname=username;
|
||||
|
||||
%if &mdebug=1 %then %do;
|
||||
putlog _all_;
|
||||
%end;
|
||||
|
||||
put '{'@;
|
||||
put '"displayName":' displayname @;
|
||||
put ',"username":' username @;
|
||||
put ',"password":' password @;
|
||||
put ',"isAdmin":' isadmin @;
|
||||
put ',"isActive": true }';
|
||||
run;
|
||||
|
||||
data _null_;
|
||||
file &fref1 lrecl=1000;
|
||||
infile "&_sasjs_tokenfile" lrecl=1000;
|
||||
input;
|
||||
if _n_=1 then do;
|
||||
put "Content-Type: application/json";
|
||||
put "accept: application/json";
|
||||
end;
|
||||
put _infile_;
|
||||
run;
|
||||
|
||||
%if &mdebug=1 %then %do;
|
||||
data _null_;
|
||||
infile &fref0;
|
||||
input;
|
||||
put _infile_;
|
||||
data _null_;
|
||||
infile &fref1;
|
||||
input;
|
||||
put _infile_;
|
||||
run;
|
||||
%end;
|
||||
|
||||
proc http method='POST' in=&fref0 headerin=&fref1 out=&fref2
|
||||
url="&_sasjs_apiserverurl/SASjsApi/user";
|
||||
%if &mdebug=1 %then %do;
|
||||
debug level=1;
|
||||
%end;
|
||||
run;
|
||||
|
||||
%mp_abort(
|
||||
iftrue=(&syscc ne 0)
|
||||
,mac=ms_createuser.sas
|
||||
,msg=%str(Issue submitting query to SASjsApi/user)
|
||||
)
|
||||
|
||||
libname &libref JSON fileref=&fref2;
|
||||
|
||||
data &outds;
|
||||
set &libref..root;
|
||||
drop ordinal_root;
|
||||
run;
|
||||
|
||||
|
||||
%mp_abort(
|
||||
iftrue=(&syscc ne 0)
|
||||
,mac=ms_createuser.sas
|
||||
,msg=%str(Issue reading response JSON)
|
||||
)
|
||||
|
||||
/* reset options */
|
||||
options &optval;
|
||||
|
||||
%if &mdebug=1 %then %do;
|
||||
filename &fref0 clear;
|
||||
filename &fref1 clear;
|
||||
filename &fref2 clear;
|
||||
libname &libref clear;
|
||||
%end;
|
||||
|
||||
%mend ms_createuser;
|
||||
537
server/ms_createwebservice.sas
Normal file
537
server/ms_createwebservice.sas
Normal file
@@ -0,0 +1,537 @@
|
||||
/**
|
||||
@file ms_createwebservice.sas
|
||||
@brief Create a Web-Ready Stored Program
|
||||
@details This macro creates a Stored Program along with the necessary precode
|
||||
to enable the %webout() macro
|
||||
|
||||
Usage:
|
||||
<code>
|
||||
%* compile macros ;
|
||||
filename mc url "https://raw.githubusercontent.com/sasjs/core/main/all.sas";
|
||||
%inc mc;
|
||||
|
||||
%* parmcards lets us write to a text file from open code ;
|
||||
filename ft15f001 temp;
|
||||
parmcards4;
|
||||
%webout(FETCH)
|
||||
%* do some sas, any inputs are now already WORK tables;
|
||||
data example1 example2;
|
||||
set sashelp.class;
|
||||
run;
|
||||
%* send data back;
|
||||
%webout(OPEN)
|
||||
%webout(ARR,example1) * Array format, fast, suitable for large tables ;
|
||||
%webout(OBJ,example2) * Object format, easier to work with ;
|
||||
%webout(CLOSE)
|
||||
;;;;
|
||||
%ms_createwebservice(path=/Public/app/common,name=appInit,code=ft15f001)
|
||||
|
||||
</code>
|
||||
|
||||
For more examples of using these web services with the SASjs Adapter, see:
|
||||
https://github.com/sasjs/adapter#readme
|
||||
|
||||
@param [in] path= (0) The full SASjs Drive path in which to create the service
|
||||
@param [in] name= Stored Program name
|
||||
@param [in] desc= The description of the service (not implemented yet)
|
||||
@param [in] precode= Space separated list of filerefs, pointing to the code
|
||||
that needs to be attached to the beginning of the service (optional)
|
||||
@param [in] code= (ft15f001) Space seperated fileref(s) of the actual code to
|
||||
be added
|
||||
@param [in] mDebug= (0) set to 1 to show debug messages in the log
|
||||
|
||||
<h4> SAS Macros </h4>
|
||||
@li ms_createfile.sas
|
||||
@li mf_getuser.sas
|
||||
@li mf_getuniquename.sas
|
||||
@li mf_getuniquefileref.sas
|
||||
@li mp_abort.sas
|
||||
|
||||
<h4> Related Files </h4>
|
||||
@li ms_createwebservice.test.sas
|
||||
|
||||
@version 9.2
|
||||
@author Allan Bowe
|
||||
|
||||
**/
|
||||
|
||||
%macro ms_createwebservice(path=0
|
||||
,name=initService
|
||||
,precode=
|
||||
,code=ft15f001
|
||||
,desc=Not currently used
|
||||
,mDebug=0
|
||||
)/*/STORE SOURCE*/;
|
||||
|
||||
%local dbg;
|
||||
%if &mdebug=1 %then %do;
|
||||
%put &sysmacroname entry vars:;
|
||||
%put _local_;
|
||||
%end;
|
||||
%else %let dbg=*;
|
||||
|
||||
%mp_abort(iftrue=(&syscc ge 4 )
|
||||
,mac=ms_createwebservice
|
||||
,msg=%str(syscc=&syscc on macro entry)
|
||||
)
|
||||
%mp_abort(iftrue=("&path"="0")
|
||||
,mac=ms_createwebservice
|
||||
,msg=%str(Path not provided)
|
||||
)
|
||||
|
||||
/* remove any trailing slash */
|
||||
%if "%substr(&path,%length(&path),1)" = "/" %then
|
||||
%let path=%substr(&path,1,%length(&path)-1);
|
||||
|
||||
/**
|
||||
* Add webout macro
|
||||
* These put statements are auto generated - to change the macro, change the
|
||||
* source (ms_webout) and run `build.py`
|
||||
*/
|
||||
%local sasjsref;
|
||||
%let sasjsref=%mf_getuniquefileref();
|
||||
data _null_;
|
||||
file &sasjsref termstr=crlf lrecl=512;
|
||||
put "/* Created on %sysfunc(datetime(),datetime19.) by %mf_getuser() */";
|
||||
/* WEBOUT BEGIN */
|
||||
put ' ';
|
||||
put '%macro mp_jsonout(action,ds,jref=_webout,dslabel=,fmt=Y ';
|
||||
put ' ,engine=DATASTEP ';
|
||||
put ' ,missing=NULL ';
|
||||
put ' ,showmeta=NO ';
|
||||
put ')/*/STORE SOURCE*/; ';
|
||||
put '%local tempds colinfo fmtds i numcols; ';
|
||||
put '%let numcols=0; ';
|
||||
put ' ';
|
||||
put '%if &action=OPEN %then %do; ';
|
||||
put ' options nobomfile; ';
|
||||
put ' data _null_;file &jref encoding=''utf-8'' lrecl=200; ';
|
||||
put ' put ''{"PROCESSED_DTTM" : "'' "%sysfunc(datetime(),E8601DT26.6)" ''"''; ';
|
||||
put ' run; ';
|
||||
put '%end; ';
|
||||
put '%else %if (&action=ARR or &action=OBJ) %then %do; ';
|
||||
put ' options validvarname=upcase; ';
|
||||
put ' data _null_; file &jref encoding=''utf-8'' mod; ';
|
||||
put ' put ", ""%lowcase(%sysfunc(coalescec(&dslabel,&ds)))"":"; ';
|
||||
put ' ';
|
||||
put ' /* grab col defs */ ';
|
||||
put ' proc contents noprint data=&ds ';
|
||||
put ' out=_data_(keep=name type length format formatl formatd varnum label); ';
|
||||
put ' run; ';
|
||||
put ' %let colinfo=%scan(&syslast,2,.); ';
|
||||
put ' proc sort data=&colinfo; ';
|
||||
put ' by varnum; ';
|
||||
put ' run; ';
|
||||
put ' /* move meta to mac vars */ ';
|
||||
put ' data _null_; ';
|
||||
put ' if _n_=1 then call symputx(''numcols'',nobs,''l''); ';
|
||||
put ' set &colinfo end=last nobs=nobs; ';
|
||||
put ' name=upcase(name); ';
|
||||
put ' /* fix formats */ ';
|
||||
put ' if type=2 or type=6 then do; ';
|
||||
put ' typelong=''char''; ';
|
||||
put ' length fmt $49.; ';
|
||||
put ' if format='''' then fmt=cats(''$'',length,''.''); ';
|
||||
put ' else if formatl=0 then fmt=cats(format,''.''); ';
|
||||
put ' else fmt=cats(format,formatl,''.''); ';
|
||||
put ' newlen=max(formatl,length); ';
|
||||
put ' end; ';
|
||||
put ' else do; ';
|
||||
put ' typelong=''num''; ';
|
||||
put ' if format='''' then fmt=''best.''; ';
|
||||
put ' else if formatl=0 then fmt=cats(format,''.''); ';
|
||||
put ' else if formatd=0 then fmt=cats(format,formatl,''.''); ';
|
||||
put ' else fmt=cats(format,formatl,''.'',formatd); ';
|
||||
put ' /* needs to be wide, for datetimes etc */ ';
|
||||
put ' newlen=max(length,formatl,24); ';
|
||||
put ' end; ';
|
||||
put ' /* 32 char unique name */ ';
|
||||
put ' newname=''sasjs''!!substr(cats(put(md5(name),$hex32.)),1,27); ';
|
||||
put ' ';
|
||||
put ' call symputx(cats(''name'',_n_),name,''l''); ';
|
||||
put ' call symputx(cats(''newname'',_n_),newname,''l''); ';
|
||||
put ' call symputx(cats(''len'',_n_),newlen,''l''); ';
|
||||
put ' call symputx(cats(''length'',_n_),length,''l''); ';
|
||||
put ' call symputx(cats(''fmt'',_n_),fmt,''l''); ';
|
||||
put ' call symputx(cats(''type'',_n_),type,''l''); ';
|
||||
put ' call symputx(cats(''typelong'',_n_),typelong,''l''); ';
|
||||
put ' call symputx(cats(''label'',_n_),coalescec(label,name),''l''); ';
|
||||
put ' run; ';
|
||||
put ' ';
|
||||
put ' %let tempds=%substr(_%sysfunc(compress(%sysfunc(uuidgen()),-)),1,32); ';
|
||||
put ' ';
|
||||
put ' %if &engine=PROCJSON %then %do; ';
|
||||
put ' %if &missing=STRING %then %do; ';
|
||||
put ' %put &sysmacroname: Special Missings not supported in proc json.; ';
|
||||
put ' %put &sysmacroname: Switching to DATASTEP engine; ';
|
||||
put ' %goto datastep; ';
|
||||
put ' %end; ';
|
||||
put ' data &tempds;set &ds; ';
|
||||
put ' %if &fmt=N %then format _numeric_ best32.;; ';
|
||||
put ' /* PRETTY is necessary to avoid line truncation in large files */ ';
|
||||
put ' proc json out=&jref pretty ';
|
||||
put ' %if &action=ARR %then nokeys ; ';
|
||||
put ' ;export &tempds / nosastags fmtnumeric; ';
|
||||
put ' run; ';
|
||||
put ' %end; ';
|
||||
put ' %else %if &engine=DATASTEP %then %do; ';
|
||||
put ' %datastep: ';
|
||||
put ' %if %sysfunc(exist(&ds)) ne 1 & %sysfunc(exist(&ds,VIEW)) ne 1 ';
|
||||
put ' %then %do; ';
|
||||
put ' %put &sysmacroname: &ds NOT FOUND!!!; ';
|
||||
put ' %return; ';
|
||||
put ' %end; ';
|
||||
put ' ';
|
||||
put ' %if &fmt=Y %then %do; ';
|
||||
put ' data _data_; ';
|
||||
put ' /* rename on entry */ ';
|
||||
put ' set &ds(rename=( ';
|
||||
put ' %do i=1 %to &numcols; ';
|
||||
put ' &&name&i=&&newname&i ';
|
||||
put ' %end; ';
|
||||
put ' )); ';
|
||||
put ' %do i=1 %to &numcols; ';
|
||||
put ' length &&name&i $&&len&i; ';
|
||||
put ' %if &&typelong&i=num %then %do; ';
|
||||
put ' &&name&i=left(put(&&newname&i,&&fmt&i)); ';
|
||||
put ' %end; ';
|
||||
put ' %else %do; ';
|
||||
put ' &&name&i=put(&&newname&i,&&fmt&i); ';
|
||||
put ' %end; ';
|
||||
put ' drop &&newname&i; ';
|
||||
put ' %end; ';
|
||||
put ' if _error_ then call symputx(''syscc'',1012); ';
|
||||
put ' run; ';
|
||||
put ' %let fmtds=&syslast; ';
|
||||
put ' %end; ';
|
||||
put ' ';
|
||||
put ' proc format; /* credit yabwon for special null removal */ ';
|
||||
put ' value bart (default=40) ';
|
||||
put ' %if &missing=NULL %then %do; ';
|
||||
put ' ._ - .z = null ';
|
||||
put ' %end; ';
|
||||
put ' %else %do; ';
|
||||
put ' ._ = [quote()] ';
|
||||
put ' . = null ';
|
||||
put ' .a - .z = [quote()] ';
|
||||
put ' %end; ';
|
||||
put ' other = [best.]; ';
|
||||
put ' ';
|
||||
put ' data &tempds; ';
|
||||
put ' attrib _all_ label=''''; ';
|
||||
put ' %do i=1 %to &numcols; ';
|
||||
put ' %if &&typelong&i=char or &fmt=Y %then %do; ';
|
||||
put ' length &&name&i $32767; ';
|
||||
put ' format &&name&i $32767.; ';
|
||||
put ' %end; ';
|
||||
put ' %end; ';
|
||||
put ' %if &fmt=Y %then %do; ';
|
||||
put ' set &fmtds; ';
|
||||
put ' %end; ';
|
||||
put ' %else %do; ';
|
||||
put ' set &ds; ';
|
||||
put ' %end; ';
|
||||
put ' format _numeric_ bart.; ';
|
||||
put ' %do i=1 %to &numcols; ';
|
||||
put ' %if &&typelong&i=char or &fmt=Y %then %do; ';
|
||||
put ' if findc(&&name&i,''"\''!!''0A0D09000E0F01021011''x) then do; ';
|
||||
put ' &&name&i=''"''!!trim( ';
|
||||
put ' prxchange(''s/"/\\"/'',-1, /* double quote */ ';
|
||||
put ' prxchange(''s/\x0A/\n/'',-1, /* new line */ ';
|
||||
put ' prxchange(''s/\x0D/\r/'',-1, /* carriage return */ ';
|
||||
put ' prxchange(''s/\x09/\\t/'',-1, /* tab */ ';
|
||||
put ' prxchange(''s/\x00/\\u0000/'',-1, /* NUL */ ';
|
||||
put ' prxchange(''s/\x0E/\\u000E/'',-1, /* SS */ ';
|
||||
put ' prxchange(''s/\x0F/\\u000F/'',-1, /* SF */ ';
|
||||
put ' prxchange(''s/\x01/\\u0001/'',-1, /* SOH */ ';
|
||||
put ' prxchange(''s/\x02/\\u0002/'',-1, /* STX */ ';
|
||||
put ' prxchange(''s/\x10/\\u0010/'',-1, /* DLE */ ';
|
||||
put ' prxchange(''s/\x11/\\u0011/'',-1, /* DC1 */ ';
|
||||
put ' prxchange(''s/\\/\\\\/'',-1,&&name&i) ';
|
||||
put ' ))))))))))))!!''"''; ';
|
||||
put ' end; ';
|
||||
put ' else &&name&i=quote(cats(&&name&i)); ';
|
||||
put ' %end; ';
|
||||
put ' %end; ';
|
||||
put ' run; ';
|
||||
put ' ';
|
||||
put ' /* write to temp loc to avoid _webout truncation ';
|
||||
put ' - https://support.sas.com/kb/49/325.html */ ';
|
||||
put ' filename _sjs temp lrecl=131068 encoding=''utf-8''; ';
|
||||
put ' data _null_; file _sjs lrecl=131068 encoding=''utf-8'' mod ; ';
|
||||
put ' if _n_=1 then put "["; ';
|
||||
put ' set &tempds; ';
|
||||
put ' if _n_>1 then put "," @; put ';
|
||||
put ' %if &action=ARR %then "[" ; %else "{" ; ';
|
||||
put ' %do i=1 %to &numcols; ';
|
||||
put ' %if &i>1 %then "," ; ';
|
||||
put ' %if &action=OBJ %then """&&name&i"":" ; ';
|
||||
put ' &&name&i ';
|
||||
put ' %end; ';
|
||||
put ' %if &action=ARR %then "]" ; %else "}" ; ; ';
|
||||
put ' /* now write the long strings to _webout 1 byte at a time */ ';
|
||||
put ' data _null_; ';
|
||||
put ' length filein 8 fileid 8; ';
|
||||
put ' filein=fopen("_sjs",''I'',1,''B''); ';
|
||||
put ' fileid=fopen("&jref",''A'',1,''B''); ';
|
||||
put ' rec=''20''x; ';
|
||||
put ' do while(fread(filein)=0); ';
|
||||
put ' rc=fget(filein,rec,1); ';
|
||||
put ' rc=fput(fileid, rec); ';
|
||||
put ' rc=fwrite(fileid); ';
|
||||
put ' end; ';
|
||||
put ' /* close out the table */ ';
|
||||
put ' rc=fput(fileid, "]"); ';
|
||||
put ' rc=fwrite(fileid); ';
|
||||
put ' rc=fclose(filein); ';
|
||||
put ' rc=fclose(fileid); ';
|
||||
put ' run; ';
|
||||
put ' filename _sjs clear; ';
|
||||
put ' %end; ';
|
||||
put ' ';
|
||||
put ' proc sql; ';
|
||||
put ' drop table &colinfo, &tempds; ';
|
||||
put ' ';
|
||||
put ' %if &showmeta=YES %then %do; ';
|
||||
put ' data _null_; file &jref encoding=''utf-8'' mod; ';
|
||||
put ' put ", ""$%lowcase(%sysfunc(coalescec(&dslabel,&ds)))"":{""vars"":{"; ';
|
||||
put ' do i=1 to &numcols; ';
|
||||
put ' name=quote(trim(symget(cats(''name'',i)))); ';
|
||||
put ' format=quote(trim(symget(cats(''fmt'',i)))); ';
|
||||
put ' label=quote(trim(symget(cats(''label'',i)))); ';
|
||||
put ' length=quote(trim(symget(cats(''length'',i)))); ';
|
||||
put ' type=quote(trim(symget(cats(''typelong'',i)))); ';
|
||||
put ' if i>1 then put "," @@; ';
|
||||
put ' put name '':{"format":'' format '',"label":'' label ';
|
||||
put ' '',"length":'' length '',"type":'' type ''}''; ';
|
||||
put ' end; ';
|
||||
put ' put ''}}''; ';
|
||||
put ' run; ';
|
||||
put ' %end; ';
|
||||
put '%end; ';
|
||||
put ' ';
|
||||
put '%else %if &action=CLOSE %then %do; ';
|
||||
put ' data _null_; file &jref encoding=''utf-8'' mod ; ';
|
||||
put ' put "}"; ';
|
||||
put ' run; ';
|
||||
put '%end; ';
|
||||
put '%mend mp_jsonout; ';
|
||||
put ' ';
|
||||
put '%macro mf_getuser(type=META ';
|
||||
put ')/*/STORE SOURCE*/; ';
|
||||
put ' %local user metavar; ';
|
||||
put ' %if &type=OS %then %let metavar=_secureusername; ';
|
||||
put ' %else %let metavar=_metaperson; ';
|
||||
put ' ';
|
||||
put ' %if %symexist(SYS_COMPUTE_SESSION_OWNER) %then %let user=&SYS_COMPUTE_SESSION_OWNER; ';
|
||||
put ' %else %if %symexist(&metavar) %then %do; ';
|
||||
put ' %if %length(&&&metavar)=0 %then %let user=&sysuserid; ';
|
||||
put ' /* sometimes SAS will add @domain extension - remove for consistency */ ';
|
||||
put ' /* but be sure to quote in case of usernames with commas */ ';
|
||||
put ' %else %let user=%unquote(%scan(%quote(&&&metavar),1,@)); ';
|
||||
put ' %end; ';
|
||||
put ' %else %let user=&sysuserid; ';
|
||||
put ' ';
|
||||
put ' %quote(&user) ';
|
||||
put ' ';
|
||||
put '%mend mf_getuser; ';
|
||||
put ' ';
|
||||
put '%macro ms_webout(action,ds,dslabel=,fref=_webout,fmt=Y,missing=NULL ';
|
||||
put ' ,showmeta=NO ';
|
||||
put '); ';
|
||||
put '%global _webin_file_count _webin_fileref1 _webin_name1 _program _debug ';
|
||||
put ' sasjs_tables; ';
|
||||
put ' ';
|
||||
put '%local i tempds; ';
|
||||
put '%let action=%upcase(&action); ';
|
||||
put ' ';
|
||||
put '%if &action=FETCH %then %do; ';
|
||||
put ' %if %str(&_debug) ge 131 %then %do; ';
|
||||
put ' options mprint notes mprintnest; ';
|
||||
put ' %end; ';
|
||||
put ' %let _webin_file_count=%eval(&_webin_file_count+0); ';
|
||||
put ' /* now read in the data */ ';
|
||||
put ' %do i=1 %to &_webin_file_count; ';
|
||||
put ' %if &_webin_file_count=1 %then %do; ';
|
||||
put ' %let _webin_fileref1=&_webin_fileref; ';
|
||||
put ' %let _webin_name1=&_webin_name; ';
|
||||
put ' %end; ';
|
||||
put ' data _null_; ';
|
||||
put ' infile &&_webin_fileref&i termstr=crlf lrecl=32767; ';
|
||||
put ' input; ';
|
||||
put ' call symputx(''input_statement'',_infile_); ';
|
||||
put ' putlog "&&_webin_name&i input statement: " _infile_; ';
|
||||
put ' stop; ';
|
||||
put ' data &&_webin_name&i; ';
|
||||
put ' infile &&_webin_fileref&i firstobs=2 dsd termstr=crlf encoding=''utf-8'' ';
|
||||
put ' lrecl=32767; ';
|
||||
put ' input &input_statement; ';
|
||||
put ' %if %str(&_debug) ge 131 %then %do; ';
|
||||
put ' if _n_<20 then putlog _infile_; ';
|
||||
put ' %end; ';
|
||||
put ' run; ';
|
||||
put ' %let sasjs_tables=&sasjs_tables &&_webin_name&i; ';
|
||||
put ' %end; ';
|
||||
put '%end; ';
|
||||
put ' ';
|
||||
put '%else %if &action=OPEN %then %do; ';
|
||||
put ' /* fix encoding and ensure enough lrecl */ ';
|
||||
put ' OPTIONS NOBOMFILE lrecl=32767; ';
|
||||
put ' ';
|
||||
put ' /* set the header */ ';
|
||||
put ' %mfs_httpheader(Content-type,application/json) ';
|
||||
put ' ';
|
||||
put ' /* setup json. */ ';
|
||||
put ' data _null_;file &fref encoding=''utf-8'' termstr=lf ; ';
|
||||
put ' put ''{"SYSDATE" : "'' "&SYSDATE" ''"''; ';
|
||||
put ' put '',"SYSTIME" : "'' "&SYSTIME" ''"''; ';
|
||||
put ' run; ';
|
||||
put ' ';
|
||||
put '%end; ';
|
||||
put ' ';
|
||||
put '%else %if &action=ARR or &action=OBJ %then %do; ';
|
||||
put ' %if "%substr(&sysver,1,1)"="4" or "%substr(&sysver,1,1)"="5" %then %do; ';
|
||||
put ' /* functions in formats unsupported */ ';
|
||||
put ' %put &sysmacroname: forcing missing back to NULL as feature not supported; ';
|
||||
put ' %let missing=NULL; ';
|
||||
put ' %end; ';
|
||||
put ' %mp_jsonout(&action,&ds,dslabel=&dslabel,fmt=&fmt,jref=&fref ';
|
||||
put ' ,engine=DATASTEP,missing=&missing,showmeta=&showmeta ';
|
||||
put ' ) ';
|
||||
put '%end; ';
|
||||
put '%else %if &action=CLOSE %then %do; ';
|
||||
put ' %if %str(&_debug) ge 131 %then %do; ';
|
||||
put ' /* if debug mode, send back first 10 records of each work table also */ ';
|
||||
put ' options obs=10; ';
|
||||
put ' data;run;%let tempds=%scan(&syslast,2,.); ';
|
||||
put ' ods output Members=&tempds; ';
|
||||
put ' proc datasets library=WORK memtype=data; ';
|
||||
put ' %local wtcnt;%let wtcnt=0; ';
|
||||
put ' data _null_; ';
|
||||
put ' set &tempds; ';
|
||||
put ' if not (upcase(name) =:"DATA"); /* ignore temp datasets */ ';
|
||||
put ' if not (upcase(name)=:"_DATA_"); ';
|
||||
put ' i+1; ';
|
||||
put ' call symputx(cats(''wt'',i),name,''l''); ';
|
||||
put ' call symputx(''wtcnt'',i,''l''); ';
|
||||
put ' data _null_; file &fref mod encoding=''utf-8'' termstr=lf; ';
|
||||
put ' put ",""WORK"":{"; ';
|
||||
put ' %do i=1 %to &wtcnt; ';
|
||||
put ' %let wt=&&wt&i; ';
|
||||
put ' data _null_; file &fref mod encoding=''utf-8'' termstr=lf; ';
|
||||
put ' dsid=open("WORK.&wt",''is''); ';
|
||||
put ' nlobs=attrn(dsid,''NLOBS''); ';
|
||||
put ' nvars=attrn(dsid,''NVARS''); ';
|
||||
put ' rc=close(dsid); ';
|
||||
put ' if &i>1 then put '',''@; ';
|
||||
put ' put " ""&wt"" : {"; ';
|
||||
put ' put ''"nlobs":'' nlobs; ';
|
||||
put ' put '',"nvars":'' nvars; ';
|
||||
put ' %mp_jsonout(OBJ,&wt,jref=&fref,dslabel=first10rows,showmeta=YES) ';
|
||||
put ' data _null_; file &fref mod encoding=''utf-8'' termstr=lf; ';
|
||||
put ' put "}"; ';
|
||||
put ' %end; ';
|
||||
put ' data _null_; file &fref mod encoding=''utf-8'' termstr=lf; ';
|
||||
put ' put "}"; ';
|
||||
put ' run; ';
|
||||
put ' %end; ';
|
||||
put ' /* close off json */ ';
|
||||
put ' data _null_;file &fref mod encoding=''utf-8'' termstr=lf lrecl=32767; ';
|
||||
put ' _PROGRAM=quote(trim(resolve(symget(''_PROGRAM'')))); ';
|
||||
put ' put ",""SYSUSERID"" : ""&sysuserid"" "; ';
|
||||
put ' put ",""MF_GETUSER"" : ""%mf_getuser()"" "; ';
|
||||
put ' put ",""_DEBUG"" : ""&_debug"" "; ';
|
||||
put ' put '',"_PROGRAM" : '' _PROGRAM ; ';
|
||||
put ' put ",""SYSCC"" : ""&syscc"" "; ';
|
||||
put ' syserrortext=quote(cats(symget(''SYSERRORTEXT''))); ';
|
||||
put ' put '',"SYSERRORTEXT" : '' syserrortext; ';
|
||||
put ' SYSHOSTINFOLONG=quote(trim(symget(''SYSHOSTINFOLONG''))); ';
|
||||
put ' put '',"SYSHOSTINFOLONG" : '' SYSHOSTINFOLONG; ';
|
||||
put ' put ",""SYSHOSTNAME"" : ""&syshostname"" "; ';
|
||||
put ' put ",""SYSPROCESSID"" : ""&SYSPROCESSID"" "; ';
|
||||
put ' put ",""SYSPROCESSMODE"" : ""&SYSPROCESSMODE"" "; ';
|
||||
put ' length SYSPROCESSNAME $512; ';
|
||||
put ' SYSPROCESSNAME=quote(urlencode(cats(SYSPROCESSNAME))); ';
|
||||
put ' put ",""SYSPROCESSNAME"" : " SYSPROCESSNAME; ';
|
||||
put ' put ",""SYSJOBID"" : ""&sysjobid"" "; ';
|
||||
put ' put ",""SYSSCPL"" : ""&sysscpl"" "; ';
|
||||
put ' put ",""SYSSITE"" : ""&syssite"" "; ';
|
||||
put ' put ",""SYSTCPIPHOSTNAME"" : ""&SYSTCPIPHOSTNAME"" "; ';
|
||||
put ' sysvlong=quote(trim(symget(''sysvlong''))); ';
|
||||
put ' put '',"SYSVLONG" : '' sysvlong; ';
|
||||
put ' syswarningtext=quote(cats(symget(''SYSWARNINGTEXT''))); ';
|
||||
put ' put '',"SYSWARNINGTEXT" : '' syswarningtext; ';
|
||||
put ' put '',"END_DTTM" : "'' "%sysfunc(datetime(),E8601DT26.6)" ''" ''; ';
|
||||
put ' length autoexec $512; ';
|
||||
put ' autoexec=quote(urlencode(trim(getoption(''autoexec'')))); ';
|
||||
put ' put '',"AUTOEXEC" : '' autoexec; ';
|
||||
put ' length memsize $32; ';
|
||||
put ' memsize="%sysfunc(INPUTN(%sysfunc(getoption(memsize)), best.),sizekmg.)"; ';
|
||||
put ' memsize=quote(cats(memsize)); ';
|
||||
put ' put '',"MEMSIZE" : '' memsize; ';
|
||||
put ' put "}" @; ';
|
||||
put ' run; ';
|
||||
put '%end; ';
|
||||
put ' ';
|
||||
put '%mend ms_webout; ';
|
||||
put ' ';
|
||||
put '%macro mfs_httpheader(header_name ';
|
||||
put ' ,header_value ';
|
||||
put ')/*/STORE SOURCE*/; ';
|
||||
put '%global sasjs_stpsrv_header_loc; ';
|
||||
put '%local fref fid i; ';
|
||||
put ' ';
|
||||
put '%if %sysfunc(filename(fref,&sasjs_stpsrv_header_loc)) ne 0 %then %do; ';
|
||||
put ' %put &=fref &=sasjs_stpsrv_header_loc; ';
|
||||
put ' %put %str(ERR)OR: %sysfunc(sysmsg()); ';
|
||||
put ' %return; ';
|
||||
put '%end; ';
|
||||
put ' ';
|
||||
put '%let fid=%sysfunc(fopen(&fref,A)); ';
|
||||
put ' ';
|
||||
put '%if &fid=0 %then %do; ';
|
||||
put ' %put %str(ERR)OR: %sysfunc(sysmsg()); ';
|
||||
put ' %return; ';
|
||||
put '%end; ';
|
||||
put ' ';
|
||||
put '%let rc=%sysfunc(fput(&fid,%str(&header_name): %str(&header_value))); ';
|
||||
put '%let rc=%sysfunc(fwrite(&fid)); ';
|
||||
put ' ';
|
||||
put '%let rc=%sysfunc(fclose(&fid)); ';
|
||||
put '%let rc=%sysfunc(filename(&fref)); ';
|
||||
put ' ';
|
||||
put '%mend mfs_httpheader; ';
|
||||
/* WEBOUT END */
|
||||
put '%macro webout(action,ds,dslabel=,fmt=,missing=NULL,showmeta=NO);';
|
||||
put ' %ms_webout(&action,ds=&ds,dslabel=&dslabel,fmt=&fmt,missing=&missing';
|
||||
put ' ,showmeta=&showmeta';
|
||||
put ' )';
|
||||
put '%mend;';
|
||||
run;
|
||||
|
||||
/* add precode and code */
|
||||
%local x fref freflist;
|
||||
%let freflist=&precode &code ;
|
||||
%do x=1 %to %sysfunc(countw(&freflist));
|
||||
%let fref=%scan(&freflist,&x);
|
||||
%put &sysmacroname: adding &fref;
|
||||
data _null_;
|
||||
file &sasjsref lrecl=3000 termstr=crlf mod;
|
||||
infile &fref lrecl=3000;
|
||||
input;
|
||||
put _infile_;
|
||||
run;
|
||||
%end;
|
||||
|
||||
/* create the web service */
|
||||
%ms_createfile(&path/&name..sas, inref=&sasjsref, mdebug=&mdebug)
|
||||
|
||||
%put ;%put ;%put ;%put ;%put ;%put ;
|
||||
%put &sysmacroname: STP &name successfully created in &path;
|
||||
%put ;%put ;%put ;
|
||||
%put Check it out here:;
|
||||
%put ;%put ;%put ;
|
||||
%put &_sasjs_apiserverurl.&_sasjs_apipath?_PROGRAM=&path/&name;
|
||||
%put ;%put ;%put ;%put ;%put ;%put ;
|
||||
|
||||
%mend ms_createwebservice;
|
||||
48
server/ms_deletefile.sas
Normal file
48
server/ms_deletefile.sas
Normal file
@@ -0,0 +1,48 @@
|
||||
/**
|
||||
@file
|
||||
@brief Deletes a file from SASjs Drive
|
||||
@details Deletes a file from SASjs Drive, if it exists.
|
||||
|
||||
Example:
|
||||
|
||||
filename stpcode temp;
|
||||
data _null_;
|
||||
file stpcode;
|
||||
put '%put hello world;';
|
||||
run;
|
||||
%ms_createfile(/some/stored/program.sas, inref=stpcode)
|
||||
|
||||
%ms_deletefile(/some/stored/program.sas)
|
||||
|
||||
@param [in] driveloc The full path to the file in SASjs Drive
|
||||
@param [in] mdebug= (0) Set to 1 to enable DEBUG messages
|
||||
|
||||
<h4> SAS Macros </h4>
|
||||
@li mf_getuniquefileref.sas
|
||||
|
||||
**/
|
||||
|
||||
%macro ms_deletefile(driveloc
|
||||
,mdebug=0
|
||||
);
|
||||
|
||||
%local headref;
|
||||
%let headref=%mf_getuniquefileref();
|
||||
|
||||
data _null_;
|
||||
file &headref lrecl=1000;
|
||||
infile "&_sasjs_tokenfile" lrecl=1000;
|
||||
input;
|
||||
put _infile_;
|
||||
run;
|
||||
|
||||
proc http method='DELETE' headerin=&headref
|
||||
url="&_sasjs_apiserverurl/SASjsApi/drive/file?_filePath=&driveloc";
|
||||
%if &mdebug=1 %then %do;
|
||||
debug level=2;
|
||||
%end;
|
||||
run;
|
||||
|
||||
filename &headref clear;
|
||||
|
||||
%mend ms_deletefile;
|
||||
@@ -5,12 +5,15 @@
|
||||
|
||||
Example:
|
||||
|
||||
%ms_getfile(/some/stored/file.ext, outref=myfile)
|
||||
%ms_getfile(/Public/app/dc/services/public/settings.sas, outref=myfile)
|
||||
|
||||
@param [in] driveloc The full path to the file in SASjs Drive
|
||||
@param [out] outref= (msgetfil) The fileref to contain the file.
|
||||
@param [in] mdebug= (0) Set to 1 to enable DEBUG messages
|
||||
|
||||
<h4> SAS Macros </h4>
|
||||
@li mf_getuniquefileref.sas
|
||||
@li mf_getuniquename.sas
|
||||
|
||||
**/
|
||||
|
||||
@@ -19,14 +22,29 @@
|
||||
,mdebug=0
|
||||
);
|
||||
|
||||
filename &outref temp;
|
||||
/* use the recfm in a separate fileref to avoid issues with subsequent reads */
|
||||
%local binaryfref floc headref;
|
||||
%let binaryfref=%mf_getuniquefileref();
|
||||
%let headref=%mf_getuniquefileref();
|
||||
%let floc=%sysfunc(pathname(work))/%mf_getuniquename().txt;
|
||||
filename &outref "&floc" lrecl=32767;
|
||||
filename &binaryfref "&floc" recfm=n;
|
||||
|
||||
proc http method='GET' out=&outref
|
||||
url="&_sasjs_apiserverurl/SASjsApi/drive/file?filePath=&driveloc";
|
||||
data _null_;
|
||||
file &headref lrecl=1000;
|
||||
infile "&_sasjs_tokenfile" lrecl=1000;
|
||||
input;
|
||||
put _infile_;
|
||||
run;
|
||||
|
||||
proc http method='GET' out=&binaryfref headerin=&headref
|
||||
url="&_sasjs_apiserverurl/SASjsApi/drive/file?_filePath=&driveloc";
|
||||
%if &mdebug=1 %then %do;
|
||||
debug level=2;
|
||||
%end;
|
||||
run;
|
||||
|
||||
filename &binaryfref clear;
|
||||
filename &headref clear;
|
||||
|
||||
%mend ms_getfile;
|
||||
%mend ms_getfile;
|
||||
107
server/ms_getusers.sas
Normal file
107
server/ms_getusers.sas
Normal file
@@ -0,0 +1,107 @@
|
||||
/**
|
||||
@file
|
||||
@brief Fetches the list of users from SASjs Server
|
||||
@details Fetches the list of users from SASjs Server and writes them to an
|
||||
output dataset.
|
||||
|
||||
Example:
|
||||
|
||||
%ms_getusers(outds=userlist)
|
||||
|
||||
@param [in] mdebug= (0) Set to 1 to enable DEBUG messages
|
||||
@param [out] outds= (work.ms_getusers) This output dataset will contain the
|
||||
list of user accounts. Format:
|
||||
|DISPLAYNAME:$18.|USERNAME:$10.|ID:best.|
|
||||
|---|---|---|
|
||||
|`Super Admin `|`secretuser `|`1`|
|
||||
|`Sabir Hassan`|`sabir`|`2`|
|
||||
|`Mihajlo Medjedovic `|`mihajlo `|`3`|
|
||||
|`Ivor Townsend `|`ivor `|`4`|
|
||||
|`New User `|`newuser `|`5`|
|
||||
|
||||
|
||||
|
||||
<h4> SAS Macros </h4>
|
||||
@li mf_getuniquefileref.sas
|
||||
@li mf_getuniquelibref.sas
|
||||
@li mp_abort.sas
|
||||
|
||||
<h4> Related Files </h4>
|
||||
@li ms_createuser.sas
|
||||
@li ms_getusers.test.sas
|
||||
|
||||
**/
|
||||
|
||||
%macro ms_getusers(
|
||||
outds=work.ms_getusers
|
||||
,mdebug=0
|
||||
);
|
||||
|
||||
%mp_abort(
|
||||
iftrue=(&syscc ne 0)
|
||||
,mac=ms_getusers.sas
|
||||
,msg=%str(syscc=&syscc on macro entry)
|
||||
)
|
||||
|
||||
%local fref0 fref1 libref optval rc msg;
|
||||
%let fref0=%mf_getuniquefileref();
|
||||
%let fref1=%mf_getuniquefileref();
|
||||
%let libref=%mf_getuniquelibref();
|
||||
|
||||
/* avoid sending bom marker to API */
|
||||
%let optval=%sysfunc(getoption(bomfile));
|
||||
options nobomfile;
|
||||
|
||||
data _null_;
|
||||
file &fref0 lrecl=1000;
|
||||
infile "&_sasjs_tokenfile" lrecl=1000;
|
||||
input;
|
||||
if _n_=1 then put "accept: application/json";
|
||||
put _infile_;
|
||||
run;
|
||||
|
||||
%if &mdebug=1 %then %do;
|
||||
data _null_;
|
||||
infile &fref0;
|
||||
input;
|
||||
put _infile_;
|
||||
run;
|
||||
%end;
|
||||
|
||||
proc http method='GET' headerin=&fref0 out=&fref1
|
||||
url="&_sasjs_apiserverurl/SASjsApi/user";
|
||||
%if &mdebug=1 %then %do;
|
||||
debug level=1;
|
||||
%end;
|
||||
run;
|
||||
|
||||
%mp_abort(
|
||||
iftrue=(&syscc ne 0)
|
||||
,mac=ms_getusers.sas
|
||||
,msg=%str(Issue submitting GET query to SASjsApi/user)
|
||||
)
|
||||
|
||||
libname &libref JSON fileref=&fref1;
|
||||
|
||||
data &outds;
|
||||
set &libref..root;
|
||||
drop ordinal_root;
|
||||
run;
|
||||
|
||||
|
||||
%mp_abort(
|
||||
iftrue=(&syscc ne 0)
|
||||
,mac=ms_getusers.sas
|
||||
,msg=%str(Issue reading response JSON)
|
||||
)
|
||||
|
||||
/* reset options */
|
||||
options &optval;
|
||||
|
||||
%if &mdebug=1 %then %do;
|
||||
filename &fref0 clear;
|
||||
filename &fref1 clear;
|
||||
libname &libref clear;
|
||||
%end;
|
||||
|
||||
%mend ms_getusers;
|
||||
@@ -15,48 +15,154 @@
|
||||
parameter)
|
||||
@param [in] debug= (131) The value to supply to the _debug URL parameter
|
||||
@param [in] mdebug= (0) Set to 1 to enable DEBUG messages
|
||||
@param [in] inputparams=(_null_) A dataset containing name/value pairs in the
|
||||
following format:
|
||||
|name:$32|value:$10000|
|
||||
|---|---|
|
||||
|stpmacname|some value|
|
||||
|mustbevalidname|can be anything, oops, %abort!!|
|
||||
@param [in] inputfiles= (_null_) A dataset containing fileref/name/filename in
|
||||
the following format:
|
||||
|fileref:$8|name:$32|filename:$256|
|
||||
|---|---|--|
|
||||
|someref|some_name|some_filename.xls|
|
||||
|fref2|another_file|zyx_v2.csv|
|
||||
|
||||
@param [out] outref= (outweb) The output fileref to contain the response JSON
|
||||
(will be created using temp engine)
|
||||
@param [out] outlogds= (_null_) Set to the name of a dataset to contain the
|
||||
log. Table format:
|
||||
|line:$2000|
|
||||
|---|
|
||||
|log line 1|
|
||||
|log line 2|
|
||||
|
||||
<h4> SAS Macros </h4>
|
||||
@li mf_getuniquefileref.sas
|
||||
@li mf_getuniquelibref.sas
|
||||
@li mp_abort.sas
|
||||
|
||||
**/
|
||||
|
||||
%macro ms_runstp(pgm
|
||||
,debug=131
|
||||
,inputparams=_null_
|
||||
,inputfiles=_null_
|
||||
,outref=outweb
|
||||
,outlogds=_null_
|
||||
,mdebug=0
|
||||
);
|
||||
%local dbg fname1;
|
||||
%local dbg mainref authref boundary;
|
||||
%let mainref=%mf_getuniquefileref();
|
||||
%let authref=%mf_getuniquefileref();
|
||||
%let boundary=%mf_getuniquename();
|
||||
%if &inputparams=0 %then %let inputparams=_null_;
|
||||
|
||||
%if &mdebug=1 %then %do;
|
||||
%put &sysmacroname entry vars:;
|
||||
%put _local_;
|
||||
%end;
|
||||
%else %let dbg=*;
|
||||
%let fname1=%mf_getuniquefileref();
|
||||
|
||||
|
||||
%mp_abort(iftrue=("&pgm"="")
|
||||
,mac=&sysmacroname
|
||||
,msg=%str(Program not provided)
|
||||
)
|
||||
|
||||
/* avoid sending bom marker to API */
|
||||
%local optval;
|
||||
%let optval=%sysfunc(getoption(bomfile));
|
||||
options nobomfile;
|
||||
|
||||
/* add params */
|
||||
data _null_;
|
||||
file &fname1;
|
||||
infile "&_sasjs_tokenfile";
|
||||
input;
|
||||
put 'Authorization: Bearer' _infile_;
|
||||
file &mainref termstr=crlf lrecl=32767 mod;
|
||||
length line $1000 name $32 value $32767;
|
||||
if _n_=1 then call missing(of _all_);
|
||||
set &inputparams;
|
||||
put "--&boundary";
|
||||
line=cats('Content-Disposition: form-data; name="',name,'"');
|
||||
put line;
|
||||
put ;
|
||||
put value;
|
||||
run;
|
||||
|
||||
filename &outref temp;
|
||||
/* parse input file list */
|
||||
%local webcount;
|
||||
%let webcount=0;
|
||||
data _null_;
|
||||
set &inputfiles end=last;
|
||||
length fileref $8 name $32 filename $256;
|
||||
call symputx(cats('webref',_n_),fileref,'l');
|
||||
call symputx(cats('webname',_n_),name,'l');
|
||||
call symputx(cats('webfilename',_n_),filename,'l');
|
||||
if last then do;
|
||||
call symputx('webcount',_n_);
|
||||
call missing(of _all_);
|
||||
end;
|
||||
run;
|
||||
|
||||
/* write out the input files */
|
||||
%local i;
|
||||
%do i=1 %to &webcount;
|
||||
data _null_;
|
||||
file &mainref termstr=crlf lrecl=32767 mod;
|
||||
infile &&webref&i lrecl=32767;
|
||||
if _n_ = 1 then do;
|
||||
length line $32767;
|
||||
line=cats(
|
||||
'Content-Disposition: form-data; name="'
|
||||
,"&&webname&i"
|
||||
,'"; filename="'
|
||||
,"&&webfilename&i"
|
||||
,'"'
|
||||
);
|
||||
put "--&boundary";
|
||||
put line;
|
||||
put "Content-Type: text/plain";
|
||||
put ;
|
||||
end;
|
||||
input;
|
||||
put _infile_; /* add the actual file to be sent */
|
||||
run;
|
||||
%end;
|
||||
|
||||
data _null_;
|
||||
file &mainref termstr=crlf mod;
|
||||
put "--&boundary--";
|
||||
run;
|
||||
|
||||
data _null_;
|
||||
file &authref lrecl=1000;
|
||||
infile "&_sasjs_tokenfile" lrecl=1000;
|
||||
input;
|
||||
if _n_=1 then put "Content-Type: multipart/form-data; boundary=&boundary";
|
||||
put _infile_;
|
||||
run;
|
||||
|
||||
%if &mdebug=1 %then %do;
|
||||
data _null_;
|
||||
infile &authref;
|
||||
input;
|
||||
put _infile_;
|
||||
data _null_;
|
||||
infile &mainref;
|
||||
input;
|
||||
put _infile_;
|
||||
run;
|
||||
%end;
|
||||
|
||||
filename &outref temp lrecl=32767;
|
||||
/* prepare request*/
|
||||
proc http method='POST' headerin=&fname1 out=&outref
|
||||
proc http method='POST' headerin=&authref in=&mainref out=&outref
|
||||
url="&_sasjs_apiserverurl.&_sasjs_apipath?_program=&pgm%str(&)_debug=131";
|
||||
%if &mdebug=1 %then %do;
|
||||
debug level=2;
|
||||
%end;
|
||||
run;
|
||||
%if (&SYS_PROCHTTP_STATUS_CODE ne 200 and &SYS_PROCHTTP_STATUS_CODE ne 201)
|
||||
or &mdebug=1 %then %do;
|
||||
%then %do;
|
||||
data _null_;infile &outref;input;putlog _infile_;run;
|
||||
%end;
|
||||
%mp_abort(
|
||||
@@ -65,6 +171,20 @@ run;
|
||||
,msg=%str(&SYS_PROCHTTP_STATUS_CODE &SYS_PROCHTTP_STATUS_PHRASE)
|
||||
)
|
||||
|
||||
/* reset options */
|
||||
options &optval;
|
||||
|
||||
%if &outlogds ne _null_ or &mdebug=1 %then %do;
|
||||
%local dumplib;
|
||||
%let dumplib=%mf_getuniquelibref();
|
||||
libname &dumplib json (&outref);
|
||||
data &outlogds;
|
||||
set &dumplib..log;
|
||||
%if &mdebug=1 %then %do;
|
||||
putlog line=;
|
||||
%end;
|
||||
run;
|
||||
%end;
|
||||
|
||||
%if &mdebug=1 %then %do;
|
||||
%put &sysmacroname exit vars:;
|
||||
@@ -72,6 +192,7 @@ run;
|
||||
%end;
|
||||
%else %do;
|
||||
/* clear refs */
|
||||
filename &fname1 clear;
|
||||
filename &authref;
|
||||
filename &mainref;
|
||||
%end;
|
||||
%mend ms_runstp;
|
||||
154
server/ms_testservice.sas
Normal file
154
server/ms_testservice.sas
Normal file
@@ -0,0 +1,154 @@
|
||||
/**
|
||||
@file
|
||||
@brief Will execute a SASjs web service on SASjs Server
|
||||
@details Prepares the input files and retrieves the resulting datasets from
|
||||
the response JSON.
|
||||
|
||||
@param [in] program The Stored Program endpoint to test
|
||||
@param [in] inputfiles=(0) A list of space seperated fileref:filename pairs as
|
||||
follows:
|
||||
inputfiles=inref:filename inref2:filename2
|
||||
@param [in] inputdatasets= (0) All datasets in this space seperated list are
|
||||
converted into SASJS-formatted CSVs (see mp_ds2csv.sas) files and added to
|
||||
the list of `inputfiles` for ingestion. The dataset will be sent with the
|
||||
same name (no need for a colon modifier).
|
||||
@param [in] inputparams=(0) A dataset containing name/value pairs in the
|
||||
following format:
|
||||
|name:$32|value:$1000|
|
||||
|---|---|
|
||||
|stpmacname|some value|
|
||||
|mustbevalidname|can be anything, oops, %abort!!|
|
||||
|
||||
@param [in] debug= (131) Provide the _debug value to pass to the STP
|
||||
@param [in] mdebug= (0) Set to 1 to provide macro debugging (this macro)
|
||||
@param [out] outlib= (0) Output libref to contain the final tables. Set to
|
||||
0 if the service output is not in JSON format.
|
||||
@param [out] outref= (0) Output fileref to create, to contain the full _webout
|
||||
response.
|
||||
@param [out] outlogds= (_null_) Set to the name of a dataset to contain the
|
||||
log. Table format:
|
||||
|line:$2000|
|
||||
|---|
|
||||
|log line 1|
|
||||
|log line 2|
|
||||
|
||||
<h4> SAS Macros </h4>
|
||||
@li mf_getuniquefileref.sas
|
||||
@li mf_getuniquename.sas
|
||||
@li mp_abort.sas
|
||||
@li mp_binarycopy.sas
|
||||
@li mp_chop.sas
|
||||
@li mp_ds2csv.sas
|
||||
@li ms_runstp.sas
|
||||
|
||||
<h4> Related Programs </h4>
|
||||
@li mp_testservice.test.sas
|
||||
|
||||
@version 9.4
|
||||
@author Allan Bowe
|
||||
|
||||
**/
|
||||
|
||||
%macro ms_testservice(program,
|
||||
inputfiles=0,
|
||||
inputdatasets=0,
|
||||
inputparams=0,
|
||||
debug=0,
|
||||
mdebug=0,
|
||||
outlib=0,
|
||||
outref=0,
|
||||
outlogds=_null_
|
||||
)/*/STORE SOURCE*/;
|
||||
%local dbg i var ds1 fref1 chopout1 chopout2;
|
||||
%if &mdebug=1 %then %do;
|
||||
%put &sysmacroname entry vars:;
|
||||
%put _local_;
|
||||
%end;
|
||||
%else %let dbg=*;
|
||||
|
||||
/* convert inputdatasets to filerefs */
|
||||
%if "&inputdatasets" ne "0" %then %do;
|
||||
%if %quote(&inputfiles)=0 %then %let inputfiles=;
|
||||
%do i=1 %to %sysfunc(countw(&inputdatasets,%str( )));
|
||||
%let var=%scan(&inputdatasets,&i,%str( ));
|
||||
%local dsref&i;
|
||||
%let dsref&i=%mf_getuniquefileref();
|
||||
%mp_ds2csv(&var,outref=&&dsref&i,headerformat=SASJS)
|
||||
%let inputfiles=&inputfiles &&dsref&i:%scan(&var,-1,.);
|
||||
%end;
|
||||
%end;
|
||||
|
||||
/* parse the filerefs - convert to a dataset */
|
||||
%let ds1=%mf_getuniquename();
|
||||
data &ds1;
|
||||
length fileref $8 name $32 filename $256 var $300;
|
||||
if "&inputfiles" ne "0" then do;
|
||||
webcount=countw("&inputfiles");
|
||||
do i=1 to webcount;
|
||||
var=scan("&inputfiles",i,' ');
|
||||
fileref=scan(var,1,':');
|
||||
name=scan(var,2,':');
|
||||
filename=cats(name,'.csv');
|
||||
output;
|
||||
end;
|
||||
end;
|
||||
run;
|
||||
|
||||
|
||||
/* execute the STP */
|
||||
%let fref1=%mf_getuniquefileref();
|
||||
|
||||
%ms_runstp(&program
|
||||
,debug=&debug
|
||||
,inputparams=&inputparams
|
||||
,inputfiles=&ds1
|
||||
,outref=&fref1
|
||||
,mdebug=&mdebug
|
||||
,outlogds=&outlogds
|
||||
)
|
||||
|
||||
|
||||
/* SASjs services have the _webout embedded in wrapper JSON */
|
||||
/* Files can also be very large - so use a dedicated macro to chop it out */
|
||||
%local matchstr1 matchstr2 ;
|
||||
%let matchstr1={"status":"success","_webout":{;
|
||||
%let matchstr2=},"log":[{;
|
||||
%let chopout1=%sysfunc(pathname(work))/%mf_getuniquename(prefix=chop1);
|
||||
%let chopout2=%sysfunc(pathname(work))/%mf_getuniquename(prefix=chop2);
|
||||
|
||||
%mp_chop("%sysfunc(pathname(&fref1,F))"
|
||||
,matchvar=matchstr1
|
||||
,keep=LAST
|
||||
,matchpoint=END
|
||||
,offset=-1
|
||||
,outfile="&chopout1"
|
||||
,mdebug=&mdebug
|
||||
)
|
||||
|
||||
%mp_chop("&chopout1"
|
||||
,matchvar=matchstr2
|
||||
,keep=FIRST
|
||||
,matchpoint=START
|
||||
,offset=1
|
||||
,outfile="&chopout2"
|
||||
,mdebug=&mdebug
|
||||
)
|
||||
|
||||
%if &outlib ne 0 %then %do;
|
||||
libname &outlib json "&chopout2";
|
||||
%end;
|
||||
%if &outref ne 0 %then %do;
|
||||
filename &outref "&chopout2";
|
||||
%end;
|
||||
|
||||
%if &mdebug=0 %then %do;
|
||||
filename &webref clear;
|
||||
filename &fref1 clear;
|
||||
filename &fref2 clear;
|
||||
%end;
|
||||
%else %do;
|
||||
%put &sysmacroname exit vars:;
|
||||
%put _local_;
|
||||
%end;
|
||||
|
||||
%mend ms_testservice;
|
||||
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
@file
|
||||
@brief Send data to/from @sasjs/server
|
||||
@brief Send data to/from sasjs/server
|
||||
@details This macro should be added to the start of each web service,
|
||||
**immediately** followed by a call to:
|
||||
|
||||
@@ -67,13 +67,14 @@
|
||||
%let _webin_name1=&_webin_name;
|
||||
%end;
|
||||
data _null_;
|
||||
infile &&_webin_fileref&i termstr=crlf;
|
||||
infile &&_webin_fileref&i termstr=crlf lrecl=32767;
|
||||
input;
|
||||
call symputx('input_statement',_infile_);
|
||||
putlog "&&_webin_name&i input statement: " _infile_;
|
||||
stop;
|
||||
data &&_webin_name&i;
|
||||
infile &&_webin_fileref&i firstobs=2 dsd termstr=crlf encoding='utf-8';
|
||||
infile &&_webin_fileref&i firstobs=2 dsd termstr=crlf encoding='utf-8'
|
||||
lrecl=32767;
|
||||
input &input_statement;
|
||||
%if %str(&_debug) ge 131 %then %do;
|
||||
if _n_<20 then putlog _infile_;
|
||||
@@ -84,14 +85,14 @@
|
||||
%end;
|
||||
|
||||
%else %if &action=OPEN %then %do;
|
||||
/* fix encoding */
|
||||
OPTIONS NOBOMFILE;
|
||||
/* fix encoding and ensure enough lrecl */
|
||||
OPTIONS NOBOMFILE lrecl=32767;
|
||||
|
||||
/* set the header */
|
||||
%mfs_httpheader(Content-type,application/json)
|
||||
|
||||
/* setup json */
|
||||
data _null_;file &fref encoding='utf-8' termstr=lf;
|
||||
/* setup json. */
|
||||
data _null_;file &fref encoding='utf-8' termstr=lf ;
|
||||
put '{"SYSDATE" : "' "&SYSDATE" '"';
|
||||
put ',"SYSTIME" : "' "&SYSTIME" '"';
|
||||
run;
|
||||
@@ -99,6 +100,11 @@
|
||||
%end;
|
||||
|
||||
%else %if &action=ARR or &action=OBJ %then %do;
|
||||
%if "%substr(&sysver,1,1)"="4" or "%substr(&sysver,1,1)"="5" %then %do;
|
||||
/* functions in formats unsupported */
|
||||
%put &sysmacroname: forcing missing back to NULL as feature not supported;
|
||||
%let missing=NULL;
|
||||
%end;
|
||||
%mp_jsonout(&action,&ds,dslabel=&dslabel,fmt=&fmt,jref=&fref
|
||||
,engine=DATASTEP,missing=&missing,showmeta=&showmeta
|
||||
)
|
||||
@@ -114,6 +120,7 @@
|
||||
data _null_;
|
||||
set &tempds;
|
||||
if not (upcase(name) =:"DATA"); /* ignore temp datasets */
|
||||
if not (upcase(name)=:"_DATA_");
|
||||
i+1;
|
||||
call symputx(cats('wt',i),name,'l');
|
||||
call symputx('wtcnt',i,'l');
|
||||
@@ -134,19 +141,20 @@
|
||||
data _null_; file &fref mod encoding='utf-8' termstr=lf;
|
||||
put "}";
|
||||
%end;
|
||||
data _null_; file &fref mod encoding='utf-8' termstr=lf termstr=lf;
|
||||
data _null_; file &fref mod encoding='utf-8' termstr=lf;
|
||||
put "}";
|
||||
run;
|
||||
%end;
|
||||
/* close off json */
|
||||
data _null_;file &fref mod encoding='utf-8' termstr=lf;
|
||||
data _null_;file &fref mod encoding='utf-8' termstr=lf lrecl=32767;
|
||||
_PROGRAM=quote(trim(resolve(symget('_PROGRAM'))));
|
||||
put ",""SYSUSERID"" : ""&sysuserid"" ";
|
||||
put ",""MF_GETUSER"" : ""%mf_getuser()"" ";
|
||||
put ",""_DEBUG"" : ""&_debug"" ";
|
||||
put ',"_PROGRAM" : ' _PROGRAM ;
|
||||
put ",""SYSCC"" : ""&syscc"" ";
|
||||
put ",""SYSERRORTEXT"" : ""&syserrortext"" ";
|
||||
syserrortext=quote(cats(symget('SYSERRORTEXT')));
|
||||
put ',"SYSERRORTEXT" : ' syserrortext;
|
||||
SYSHOSTINFOLONG=quote(trim(symget('SYSHOSTINFOLONG')));
|
||||
put ',"SYSHOSTINFOLONG" : ' SYSHOSTINFOLONG;
|
||||
put ",""SYSHOSTNAME"" : ""&syshostname"" ";
|
||||
@@ -161,7 +169,8 @@
|
||||
put ",""SYSTCPIPHOSTNAME"" : ""&SYSTCPIPHOSTNAME"" ";
|
||||
sysvlong=quote(trim(symget('sysvlong')));
|
||||
put ',"SYSVLONG" : ' sysvlong;
|
||||
put ",""SYSWARNINGTEXT"" : ""&syswarningtext"" ";
|
||||
syswarningtext=quote(cats(symget('SYSWARNINGTEXT')));
|
||||
put ',"SYSWARNINGTEXT" : ' syswarningtext;
|
||||
put ',"END_DTTM" : "' "%sysfunc(datetime(),E8601DT26.6)" '" ';
|
||||
length autoexec $512;
|
||||
autoexec=quote(urlencode(trim(getoption('autoexec'))));
|
||||
|
||||
29
tests/crossplatform/mf_deletefile.test.sas
Normal file
29
tests/crossplatform/mf_deletefile.test.sas
Normal file
@@ -0,0 +1,29 @@
|
||||
/**
|
||||
@file
|
||||
@brief Testing mf_deletefile.sas macro
|
||||
|
||||
<h4> SAS Macros </h4>
|
||||
@li mf_deletefile.sas
|
||||
@li mf_writefile.sas
|
||||
@li mp_assert.sas
|
||||
@li mp_assertscope.sas
|
||||
|
||||
**/
|
||||
|
||||
%let test1file=&sasjswork/myfile1.txt;
|
||||
|
||||
%mf_writefile(&test1file,l1=some content)
|
||||
|
||||
%mp_assert(
|
||||
iftrue=(%sysfunc(fileexist(&test1file))=1),
|
||||
desc=Check &test1file exists
|
||||
)
|
||||
|
||||
%mp_assertscope(SNAPSHOT)
|
||||
%mf_deletefile(&test1file)
|
||||
%mp_assertscope(COMPARE)
|
||||
|
||||
%mp_assert(
|
||||
iftrue=(%sysfunc(fileexist(&test1file))=0),
|
||||
desc=Check &test1file no longer exists
|
||||
)
|
||||
22
tests/crossplatform/mf_fmtdttm.test.sas
Normal file
22
tests/crossplatform/mf_fmtdttm.test.sas
Normal file
@@ -0,0 +1,22 @@
|
||||
/**
|
||||
@file
|
||||
@brief Testing mf_fmtdttm macro
|
||||
|
||||
<h4> SAS Macros </h4>
|
||||
@li mf_fmtdttm.sas
|
||||
@li mp_assert.sas
|
||||
@li mp_assertscope.sas
|
||||
|
||||
**/
|
||||
|
||||
%global test1;
|
||||
|
||||
%mp_assertscope(SNAPSHOT)
|
||||
%let test1=%mf_fmtdttm();
|
||||
%mp_assertscope(COMPARE,ignorelist=test1)
|
||||
|
||||
%mp_assert(
|
||||
iftrue=("&test1"="DATETIME19.3" or "&test1"="E8601DT26.6"),
|
||||
desc=Basic test,
|
||||
outds=work.test_results
|
||||
)
|
||||
46
tests/crossplatform/mf_getvarcount.test.sas
Normal file
46
tests/crossplatform/mf_getvarcount.test.sas
Normal file
@@ -0,0 +1,46 @@
|
||||
/**
|
||||
@file
|
||||
@brief Testing mf_getvarlist macro
|
||||
|
||||
<h4> SAS Macros </h4>
|
||||
@li mf_getvarcount.sas
|
||||
@li mp_assertscope.sas
|
||||
|
||||
**/
|
||||
|
||||
data work.all work.nums(keep=num1 num2) work.chars(keep=char1 char2);
|
||||
length num1 num2 8 char1 char2 char3 $4;
|
||||
call missing (of _all_);
|
||||
output;
|
||||
run;
|
||||
|
||||
%mp_assertscope(SNAPSHOT)
|
||||
%put scope check:%mf_getvarcount(work.all);
|
||||
%mp_assertscope(COMPARE)
|
||||
|
||||
%mp_assert(
|
||||
iftrue=(%mf_getvarcount(work.all)=5),
|
||||
desc=%str(Checking for mixed vars),
|
||||
outds=work.test_results
|
||||
)
|
||||
%mp_assert(
|
||||
iftrue=(%mf_getvarcount(work.all,typefilter=C)=3),
|
||||
desc=%str(Checking for char in mixed vars),
|
||||
outds=work.test_results
|
||||
)
|
||||
%mp_assert(
|
||||
iftrue=(%mf_getvarcount(work.all,typefilter=N)=2),
|
||||
desc=%str(Checking for num in mixed vars),
|
||||
outds=work.test_results
|
||||
)
|
||||
%mp_assert(
|
||||
iftrue=(%mf_getvarcount(work.nums,typefilter=c)=0),
|
||||
desc=%str(Checking for char in num vars),
|
||||
outds=work.test_results
|
||||
)
|
||||
%mp_assert(
|
||||
iftrue=(%mf_getvarcount(work.chars,typefilter=N)=0),
|
||||
desc=%str(Checking for num in char vars),
|
||||
outds=work.test_results
|
||||
)
|
||||
|
||||
@@ -30,4 +30,12 @@
|
||||
),
|
||||
desc=Checking mf_isint(-1),
|
||||
outds=work.test_results
|
||||
)
|
||||
|
||||
%mp_assert(
|
||||
iftrue=(
|
||||
"%mf_isint()"="0"
|
||||
),
|
||||
desc=Checking mf_isint(),
|
||||
outds=work.test_results
|
||||
)
|
||||
@@ -41,7 +41,7 @@ run;
|
||||
|
||||
/* multibyte string check */
|
||||
|
||||
filename tmp2 temp;
|
||||
filename tmp2 temp lrecl=500;
|
||||
data _null_;
|
||||
file tmp2;
|
||||
put "'╤', '╔', '╗', '═', '╧', '╚', '╝', '║', '╟', '─', '┼', '║', '╢', '│'";
|
||||
|
||||
70
tests/crossplatform/mp_chop.test.sas
Normal file
70
tests/crossplatform/mp_chop.test.sas
Normal file
@@ -0,0 +1,70 @@
|
||||
/**
|
||||
@file
|
||||
@brief Testing mp_chop.sas macro
|
||||
|
||||
<h4> SAS Macros </h4>
|
||||
@li mp_chop.sas
|
||||
@li mp_assert.sas
|
||||
@li mp_assertscope.sas
|
||||
|
||||
**/
|
||||
|
||||
/* prep input string */
|
||||
%let src="%sysfunc(pathname(work))/file.txt";
|
||||
%let str=Chop here!;
|
||||
%let out1="%sysfunc(pathname(work))/file1.txt";
|
||||
%let out2="%sysfunc(pathname(work))/file2.txt";
|
||||
%let out3="%sysfunc(pathname(work))/file3.txt";
|
||||
%let out4="%sysfunc(pathname(work))/file4.txt";
|
||||
|
||||
data _null_;
|
||||
file &src;
|
||||
put "startsection&str.endsection";
|
||||
run;
|
||||
|
||||
|
||||
%mp_assertscope(SNAPSHOT)
|
||||
%mp_chop(&src, matchvar=str, keep=FIRST, outfile=&out1)
|
||||
%mp_chop(&src, matchvar=str, keep=LAST, outfile=&out2)
|
||||
%mp_chop(&src, matchvar=str, keep=FIRST, matchpoint=END, outfile=&out3)
|
||||
%mp_chop(&src, matchvar=str, keep=LAST, matchpoint=END, outfile=&out4)
|
||||
%mp_assertscope(COMPARE)
|
||||
|
||||
data _null_;
|
||||
infile &out1 lrecl=200;
|
||||
input;
|
||||
call symputx('test1',_infile_);
|
||||
data _null_;
|
||||
infile &out2 lrecl=200;
|
||||
input;
|
||||
call symputx('test2',_infile_);
|
||||
data _null_;
|
||||
infile &out3 lrecl=200;
|
||||
input;
|
||||
call symputx('test3',_infile_);
|
||||
data _null_;
|
||||
infile &out4 lrecl=200;
|
||||
input;
|
||||
call symputx('test4',_infile_);
|
||||
run;
|
||||
|
||||
%mp_assert(
|
||||
iftrue=("&test1" = "startsection"),
|
||||
desc=Checking keep FIRST matchpoint START
|
||||
outds=work.test_results
|
||||
)
|
||||
%mp_assert(
|
||||
iftrue=("&test2" = "Chop here!endsection"),
|
||||
desc=Checking keep LAST matchpoint START
|
||||
outds=work.test_results
|
||||
)
|
||||
%mp_assert(
|
||||
iftrue=("&test3" = "startsectionChop here!"),
|
||||
desc=Checking keep FIRST matchpoint END
|
||||
outds=work.test_results
|
||||
)
|
||||
%mp_assert(
|
||||
iftrue=("&test4" = "endsection"),
|
||||
desc=Checking keep LAST matchpoint END
|
||||
outds=work.test_results
|
||||
)
|
||||
28
tests/crossplatform/mp_createwebservice.test.sas
Normal file
28
tests/crossplatform/mp_createwebservice.test.sas
Normal file
@@ -0,0 +1,28 @@
|
||||
/**
|
||||
@file
|
||||
@brief Testing ms_createwebservice.sas macro
|
||||
|
||||
<h4> SAS Macros </h4>
|
||||
@li mf_getuniquefileref.sas
|
||||
@li mp_assertscope.sas
|
||||
@li mp_createwebservice.sas
|
||||
|
||||
**/
|
||||
|
||||
%let path=&mcTestAppLoc/mp_createwebservice;
|
||||
%let name=myservice;
|
||||
%let fref=%mf_getuniquefileref();
|
||||
|
||||
data _null_;
|
||||
file &fref lrecl=3000;
|
||||
put '%put hello world;';
|
||||
run;
|
||||
|
||||
%mp_assertscope(SNAPSHOT)
|
||||
%mp_createwebservice(path=&path,name=&name,code=&fref,mdebug=&sasjs_mdebug)
|
||||
%mp_assertscope(COMPARE)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -47,4 +47,18 @@ run;
|
||||
iftrue=(%mf_nobs(work.mytable3)=2),
|
||||
desc=Top level returned,
|
||||
outds=work.test_results
|
||||
)
|
||||
)
|
||||
|
||||
%mp_dirlist(path=&root/b, outds=work.myTable4)
|
||||
%mp_assert(
|
||||
iftrue=(%mf_nobs(work.mytable4)=0),
|
||||
desc=Empty table for empty directory,
|
||||
outds=work.test_results
|
||||
)
|
||||
|
||||
%mp_dirlist(path=&root/notexisting, outds=work.myTable5)
|
||||
%mp_assert(
|
||||
iftrue=(%mf_nobs(work.mytable5)=0),
|
||||
desc=Empty table for non-existing directory,
|
||||
outds=work.test_results
|
||||
)
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
, cards_file= "%sysfunc(pathname(work))/cars.sas"
|
||||
, showlog=NO
|
||||
)
|
||||
%inc "%sysfunc(pathname(work))/cars.sas"/source2;
|
||||
%inc "%sysfunc(pathname(work))/cars.sas"/source2 lrecl=32767;
|
||||
|
||||
proc compare base=sashelp.cars compare=work.test;
|
||||
quit;
|
||||
@@ -48,7 +48,7 @@ run;
|
||||
, append=
|
||||
)
|
||||
|
||||
%inc "%sysfunc(pathname(work))/c2.sas"/source2;
|
||||
%inc "%sysfunc(pathname(work))/c2.sas"/source2 lrecl=32767;
|
||||
|
||||
proc compare base=work.binarybase compare=work.binarycompare;
|
||||
run;
|
||||
|
||||
@@ -6,11 +6,39 @@
|
||||
@li mp_filtercheck.sas
|
||||
@li mp_assertdsobs.sas
|
||||
@li mp_assert.sas
|
||||
@li mp_assertscope.sas
|
||||
|
||||
**/
|
||||
|
||||
/* set up test data */
|
||||
data work.class ;
|
||||
length name $8 sex $1 age height weight 8;
|
||||
infile cards dsd;
|
||||
input Name:$char. Sex :$char. Age Height Weight;
|
||||
datalines4;
|
||||
Alfred,M,14,69,112.5
|
||||
Alice,F,13,56.5,84
|
||||
Barbara,F,13,65.3,98
|
||||
Carol,F,14,62.8,102.5
|
||||
Henry,M,14,63.5,102.5
|
||||
James,M,12,57.3,83
|
||||
Jane,F,12,59.8,84.5
|
||||
Janet,F,15,62.5,112.5
|
||||
Jeffrey,M,13,62.5,84
|
||||
John,M,12,59,99.5
|
||||
Joyce,F,11,51.3,50.5
|
||||
Judy,F,14,64.3,90
|
||||
Louise,F,12,56.3,77
|
||||
Mary,F,15,66.5,112
|
||||
Philip,M,16,72,150
|
||||
Robert,M,12,64.8,128
|
||||
Ronald,M,15,67,133
|
||||
Thomas,M,11,57.5,85
|
||||
William,M,15,66.5,112
|
||||
;;;;
|
||||
run;
|
||||
|
||||
/* valid filter */
|
||||
/* valid filter conditions */
|
||||
data work.inds;
|
||||
infile datalines4 dsd;
|
||||
input GROUP_LOGIC:$3. SUBGROUP_LOGIC:$3. SUBGROUP_ID:8. VARIABLE_NM:$32.
|
||||
@@ -21,14 +49,19 @@ AND,AND,1,SEX,<=,"'M'"
|
||||
AND,OR,2,Name,NOT IN,"('Jane','Alfred')"
|
||||
AND,OR,2,Weight,>=,77.7
|
||||
AND,OR,2,Weight,NE,77.7
|
||||
AND,AND,1,age,=,.A
|
||||
AND,AND,1,height,<,.B
|
||||
;;;;
|
||||
run;
|
||||
|
||||
%mp_assertscope(SNAPSHOT)
|
||||
%mp_filtercheck(work.inds,
|
||||
targetds=sashelp.class,
|
||||
targetds=work.class,
|
||||
outds=work.badrecords,
|
||||
abort=NO
|
||||
)
|
||||
%mp_assertscope(COMPARE)
|
||||
|
||||
%let syscc=0;
|
||||
%mp_assertdsobs(work.badrecords,
|
||||
desc=Valid filter query,
|
||||
@@ -49,7 +82,7 @@ AND,OR,2,Weight,>=,7
|
||||
;;;;
|
||||
run;
|
||||
%mp_filtercheck(work.inds,
|
||||
targetds=sashelp.class,
|
||||
targetds=work.class,
|
||||
outds=work.badrecords,
|
||||
abort=NO
|
||||
)
|
||||
@@ -71,7 +104,7 @@ AND,OR,2,Name,NOT IN,"(''''Jane','Alfred')"
|
||||
run;
|
||||
|
||||
%mp_filtercheck(work.inds,
|
||||
targetds=sashelp.class,
|
||||
targetds=work.class,
|
||||
outds=work.badrecords,
|
||||
abort=NO
|
||||
)
|
||||
@@ -94,7 +127,7 @@ AND,OR,2,Weight,>=,7
|
||||
run;
|
||||
|
||||
%mp_filtercheck(work.inds,
|
||||
targetds=sashelp.class,
|
||||
targetds=work.class,
|
||||
outds=work.badrecords,
|
||||
abort=NO
|
||||
)
|
||||
@@ -115,7 +148,7 @@ AND,AND,1,age,=,;;%abort
|
||||
;;;;
|
||||
run;
|
||||
%mp_filtercheck(work.inds,
|
||||
targetds=sashelp.class,
|
||||
targetds=work.class,
|
||||
outds=work.badrecords,
|
||||
abort=NO
|
||||
)
|
||||
@@ -137,7 +170,7 @@ AND,AND,1,age,=,0
|
||||
run;
|
||||
%let syscc=0;
|
||||
%mp_filtercheck(work.inds,
|
||||
targetds=sashelp.class,
|
||||
targetds=work.class,
|
||||
outds=work.badrecords,
|
||||
abort=NO
|
||||
)
|
||||
|
||||
@@ -11,6 +11,34 @@
|
||||
|
||||
options source2;
|
||||
|
||||
/* set up test data */
|
||||
data work.class ;
|
||||
length name $8 sex $1 age height weight 8;
|
||||
infile cards dsd;
|
||||
input Name:$char. Sex :$char. Age Height Weight;
|
||||
datalines4;
|
||||
Alfred,M,14,69,112.5
|
||||
Alice,F,13,56.5,84
|
||||
Barbara,F,13,65.3,98
|
||||
Carol,F,14,62.8,102.5
|
||||
Henry,M,14,63.5,102.5
|
||||
James,M,12,57.3,83
|
||||
Jane,F,12,59.8,84.5
|
||||
Janet,F,15,62.5,112.5
|
||||
Jeffrey,M,13,62.5,84
|
||||
John,M,12,59,99.5
|
||||
Joyce,F,11,51.3,50.5
|
||||
Judy,F,14,64.3,90
|
||||
Louise,F,12,56.3,77
|
||||
Mary,F,15,66.5,112
|
||||
Philip,M,16,72,150
|
||||
Robert,M,12,64.8,128
|
||||
Ronald,M,15,67,133
|
||||
Thomas,M,11,57.5,85
|
||||
William,M,15,66.5,112
|
||||
;;;;
|
||||
run;
|
||||
|
||||
/* valid filter */
|
||||
data work.inds;
|
||||
infile datalines4 dsd;
|
||||
@@ -23,10 +51,10 @@ AND,OR,2,Name,NOT IN,"('Jane','Janet')"
|
||||
AND,OR,2,Weight,>=,84.6
|
||||
;;;;
|
||||
run;
|
||||
%mp_filtercheck(work.inds,targetds=sashelp.class)
|
||||
%mp_filtercheck(work.inds,targetds=work.class)
|
||||
%mp_filtergenerate(work.inds,outref=myfilter)
|
||||
data work.test;
|
||||
set sashelp.class;
|
||||
set work.class;
|
||||
where %inc myfilter;;
|
||||
run;
|
||||
%mp_assertdsobs(work.test,
|
||||
@@ -43,10 +71,10 @@ data work.inds;
|
||||
datalines4;
|
||||
;;;;
|
||||
run;
|
||||
%mp_filtercheck(work.inds,targetds=sashelp.class)
|
||||
%mp_filtercheck(work.inds,targetds=work.class)
|
||||
%mp_filtergenerate(work.inds,outref=myfilter)
|
||||
data work.test;
|
||||
set sashelp.class;
|
||||
set work.class;
|
||||
where %inc myfilter;;
|
||||
run;
|
||||
%mp_assertdsobs(work.test,
|
||||
@@ -64,10 +92,10 @@ datalines4;
|
||||
AND,OR,2,Name,IN,"('Jane','Janet')"
|
||||
;;;;
|
||||
run;
|
||||
%mp_filtercheck(work.inds,targetds=sashelp.class)
|
||||
%mp_filtercheck(work.inds,targetds=work.class)
|
||||
%mp_filtergenerate(work.inds,outref=myfilter)
|
||||
data work.test;
|
||||
set sashelp.class;
|
||||
set work.class;
|
||||
where %inc myfilter;;
|
||||
run;
|
||||
%mp_assertdsobs(work.test,
|
||||
@@ -86,10 +114,10 @@ OR,OR,2,Name,IN,"('Jane','Janet')"
|
||||
OR,OR,3,Name,IN,"('James')"
|
||||
;;;;
|
||||
run;
|
||||
%mp_filtercheck(work.inds,targetds=sashelp.class)
|
||||
%mp_filtercheck(work.inds,targetds=work.class)
|
||||
%mp_filtergenerate(work.inds,outref=myfilter)
|
||||
data work.test;
|
||||
set sashelp.class;
|
||||
set work.class;
|
||||
where %inc myfilter;;
|
||||
run;
|
||||
%mp_assertdsobs(work.test,
|
||||
@@ -108,10 +136,10 @@ AND,OR,2,Name,IN,"('Jane','Janet')"
|
||||
AND,OR,3,Name,IN,"('James')"
|
||||
;;;;
|
||||
run;
|
||||
%mp_filtercheck(work.inds,targetds=sashelp.class)
|
||||
%mp_filtercheck(work.inds,targetds=work.class)
|
||||
%mp_filtergenerate(work.inds,outref=myfilter)
|
||||
data work.test;
|
||||
set sashelp.class;
|
||||
set work.class;
|
||||
where %inc myfilter;;
|
||||
run;
|
||||
%mp_assertdsobs(work.test,
|
||||
|
||||
@@ -10,6 +10,34 @@
|
||||
|
||||
**/
|
||||
|
||||
/* set up test data */
|
||||
data work.class ;
|
||||
length name $8 sex $1 age height weight 8;
|
||||
infile cards dsd;
|
||||
input Name:$char. Sex :$char. Age Height Weight;
|
||||
datalines4;
|
||||
Alfred,M,14,69,112.5
|
||||
Alice,F,13,56.5,84
|
||||
Barbara,F,13,65.3,98
|
||||
Carol,F,14,62.8,102.5
|
||||
Henry,M,14,63.5,102.5
|
||||
James,M,12,57.3,83
|
||||
Jane,F,12,59.8,84.5
|
||||
Janet,F,15,62.5,112.5
|
||||
Jeffrey,M,13,62.5,84
|
||||
John,M,12,59,99.5
|
||||
Joyce,F,11,51.3,50.5
|
||||
Judy,F,14,64.3,90
|
||||
Louise,F,12,56.3,77
|
||||
Mary,F,15,66.5,112
|
||||
Philip,M,16,72,150
|
||||
Robert,M,12,64.8,128
|
||||
Ronald,M,15,67,133
|
||||
Thomas,M,11,57.5,85
|
||||
William,M,15,66.5,112
|
||||
;;;;
|
||||
run;
|
||||
|
||||
libname permlib (work);
|
||||
|
||||
%mp_coretable(LOCKTABLE,libds=permlib.locktable)
|
||||
@@ -31,7 +59,7 @@ AND,OR,2,Weight,NE,77.7
|
||||
;;;;
|
||||
run;
|
||||
|
||||
%mp_filterstore(libds=sashelp.class,
|
||||
%mp_filterstore(libds=work.class,
|
||||
queryds=work.inds,
|
||||
filter_summary=permlib.filtsum,
|
||||
filter_detail=permlib.filtdet,
|
||||
@@ -60,7 +88,7 @@ select max(filter_rk) into: test1 from work.result;
|
||||
)
|
||||
|
||||
/* Test 2 - load same table again and ensure we get the same RK */
|
||||
%mp_filterstore(libds=sashelp.class,
|
||||
%mp_filterstore(libds=work.class,
|
||||
queryds=work.inds,
|
||||
filter_summary=permlib.filtsum,
|
||||
filter_detail=permlib.filtdet,
|
||||
|
||||
@@ -7,9 +7,36 @@
|
||||
@li mp_filtervalidate.sas
|
||||
@li mp_assertdsobs.sas
|
||||
@li mp_assert.sas
|
||||
@li mp_assertscope.sas
|
||||
|
||||
**/
|
||||
|
||||
data work.class ;
|
||||
length name $8 sex $1 age height weight 8;
|
||||
infile cards dsd;
|
||||
input Name:$char. Sex :$char. Age Height Weight;
|
||||
datalines4;
|
||||
Alfred,M,14,69,112.5
|
||||
Alice,F,13,56.5,84
|
||||
Barbara,F,13,65.3,98
|
||||
Carol,F,14,62.8,102.5
|
||||
Henry,M,14,63.5,102.5
|
||||
James,M,12,57.3,83
|
||||
Jane,F,12,59.8,84.5
|
||||
Janet,F,15,62.5,112.5
|
||||
Jeffrey,M,13,62.5,84
|
||||
John,M,12,59,99.5
|
||||
Joyce,F,11,51.3,50.5
|
||||
Judy,F,14,64.3,90
|
||||
Louise,F,12,56.3,77
|
||||
Mary,F,15,66.5,112
|
||||
Philip,M,16,72,150
|
||||
Robert,M,12,64.8,128
|
||||
Ronald,M,15,67,133
|
||||
Thomas,M,11,57.5,85
|
||||
William,M,15,66.5,112
|
||||
;;;;
|
||||
run;
|
||||
|
||||
/* valid filter */
|
||||
data work.inds;
|
||||
@@ -21,10 +48,16 @@ AND,AND,1,AGE,>,5
|
||||
AND,AND,1,SEX,NE,"'M'"
|
||||
AND,OR,2,Name,NOT IN,"('Jane','Janet')"
|
||||
AND,OR,2,Weight,>=,84.6
|
||||
AND,AND,3,age,=,.a
|
||||
AND,AND,4,weight,NE,._
|
||||
;;;;
|
||||
run;
|
||||
%mp_filtergenerate(work.inds,outref=myfilter)
|
||||
%mp_filtervalidate(myfilter,sashelp.class,outds=work.results,abort=NO)
|
||||
|
||||
%mp_assertscope(SNAPSHOT)
|
||||
%mp_filtervalidate(myfilter,work.class,outds=work.results,abort=NO)
|
||||
%mp_assertscope(COMPARE)
|
||||
|
||||
%mp_assertdsobs(work.results,
|
||||
desc=Valid filter,
|
||||
test=EMPTY,
|
||||
@@ -40,7 +73,7 @@ datalines4;
|
||||
;;;;
|
||||
run;
|
||||
%mp_filtergenerate(work.inds,outref=myfilter)
|
||||
%mp_filtervalidate(myfilter,sashelp.class,outds=work.results,abort=NO)
|
||||
%mp_filtervalidate(myfilter,work.class,outds=work.results,abort=NO)
|
||||
%mp_assertdsobs(work.results,
|
||||
desc=Empty filter,
|
||||
test=EMPTY,
|
||||
@@ -59,7 +92,7 @@ AND,AND,1,SEX,NE,2
|
||||
;;;;
|
||||
run;
|
||||
%mp_filtergenerate(work.inds,outref=myfilter)
|
||||
%mp_filtervalidate(myfilter,sashelp.class,outds=work.results,abort=NO)
|
||||
%mp_filtervalidate(myfilter,work.class,outds=work.results,abort=NO)
|
||||
%let syscc=0;
|
||||
%let test3=0;
|
||||
data _null_;
|
||||
@@ -84,7 +117,7 @@ AND,AND,1,age,NE,"'M'"
|
||||
;;;;
|
||||
run;
|
||||
%mp_filtergenerate(work.inds,outref=myfilter)
|
||||
%mp_filtervalidate(myfilter,sashelp.class,outds=work.results,abort=NO)
|
||||
%mp_filtervalidate(myfilter,work.class,outds=work.results,abort=NO)
|
||||
%let syscc=0;
|
||||
%let test4=0;
|
||||
data _null_;
|
||||
|
||||
@@ -6,24 +6,52 @@
|
||||
@li mf_nobs.sas
|
||||
@li mp_getconstraints.sas
|
||||
@li mp_assert.sas
|
||||
@li mp_assertscope.sas
|
||||
|
||||
**/
|
||||
|
||||
proc sql;
|
||||
create table work.example(
|
||||
TX_FROM float format=datetime19.,
|
||||
DD_TYPE char(16),
|
||||
DD_SOURCE char(2048),
|
||||
DD_SHORTDESC char(256),
|
||||
constraint pk primary key(tx_from, dd_type,dd_source),
|
||||
constraint unq unique(tx_from, dd_type),
|
||||
constraint nnn not null(DD_SHORTDESC)
|
||||
);
|
||||
|
||||
%mp_getconstraints(lib=work,ds=example,outds=work.constraints)
|
||||
%macro conditional();
|
||||
|
||||
%mp_assert(
|
||||
iftrue=(%mf_nobs(work.constraints)=6),
|
||||
desc=Output table work.constraints created with correct number of records,
|
||||
outds=work.test_results
|
||||
)
|
||||
%if %sysfunc(exist(sashelp.vcncolu,view))=1 %then %do;
|
||||
proc sql;
|
||||
create table work.example(
|
||||
TX_FROM float format=datetime19.,
|
||||
DD_TYPE char(16),
|
||||
DD_SOURCE char(2048),
|
||||
DD_SHORTDESC char(256),
|
||||
constraint pk primary key(tx_from, dd_type,dd_source),
|
||||
constraint unq unique(tx_from, dd_type),
|
||||
constraint nnn not null(DD_SHORTDESC)
|
||||
);
|
||||
%mp_assertscope(SNAPSHOT)
|
||||
%mp_getconstraints(lib=work,ds=example,outds=work.constraints)
|
||||
%mp_assertscope(COMPARE)
|
||||
|
||||
%mp_assert(
|
||||
iftrue=(%mf_nobs(work.constraints)=6),
|
||||
desc=Output table work.constraints created with correct number of records,
|
||||
outds=work.test_results
|
||||
)
|
||||
%end;
|
||||
%else %do;
|
||||
proc sql;
|
||||
create table work.example(
|
||||
TX_FROM float format=datetime19.,
|
||||
DD_TYPE char(16),
|
||||
DD_SOURCE char(2048),
|
||||
DD_SHORTDESC char(256)
|
||||
);
|
||||
%mp_assertscope(SNAPSHOT)
|
||||
%mp_getconstraints(lib=work,ds=example,outds=work.constraints)
|
||||
%mp_assertscope(COMPARE)
|
||||
|
||||
%mp_assert(
|
||||
iftrue=(%mf_nobs(work.constraints)=0),
|
||||
desc=Empty table created as constraints not supported,
|
||||
outds=work.test_results
|
||||
)
|
||||
%end;
|
||||
%mend conditional;
|
||||
|
||||
%conditional()
|
||||
|
||||
@@ -10,10 +10,48 @@
|
||||
|
||||
**/
|
||||
|
||||
data work.class ;
|
||||
attrib
|
||||
Name length= $8
|
||||
Sex length= $1
|
||||
Age length= 8
|
||||
Height length= 8
|
||||
Weight length= 8
|
||||
;
|
||||
infile cards dsd;
|
||||
input
|
||||
Name :$char.
|
||||
Sex :$char.
|
||||
Age
|
||||
Height
|
||||
Weight
|
||||
;
|
||||
datalines4;
|
||||
Alfred,M,14,69,112.5
|
||||
Alice,F,13,56.5,84
|
||||
Barbara,F,13,65.3,98
|
||||
Carol,F,14,62.8,102.5
|
||||
Henry,M,14,63.5,102.5
|
||||
James,M,12,57.3,83
|
||||
Jane,F,12,59.8,84.5
|
||||
Janet,F,15,62.5,112.5
|
||||
Jeffrey,M,13,62.5,84
|
||||
John,M,12,59,99.5
|
||||
Joyce,F,11,51.3,50.5
|
||||
Judy,F,14,64.3,90
|
||||
Louise,F,12,56.3,77
|
||||
Mary,F,15,66.5,112
|
||||
Philip,M,16,72,150
|
||||
Robert,M,12,64.8,128
|
||||
Ronald,M,15,67,133
|
||||
Thomas,M,11,57.5,85
|
||||
William,M,15,66.5,112
|
||||
;;;;
|
||||
run;
|
||||
|
||||
/* regular usage */
|
||||
%mp_assertscope(SNAPSHOT)
|
||||
%mp_getmaxvarlengths(sashelp.class,outds=work.myds)
|
||||
%mp_getmaxvarlengths(work.class,outds=work.myds)
|
||||
%mp_assertscope(COMPARE,desc=checking scope leakage on mp_getmaxvarlengths)
|
||||
%mp_assert(
|
||||
iftrue=(&syscc=0),
|
||||
|
||||
@@ -88,4 +88,50 @@ run;
|
||||
/* constraint capture at library level is functional - uses first 2 tests */
|
||||
%mp_getpk(work,outds=test4)
|
||||
|
||||
%mp_assertdsobs(work.test4,test=ATLEAST 2)
|
||||
%mp_assertdsobs(work.test4,test=ATLEAST 2)
|
||||
|
||||
/* unique & not null INDEX captured */
|
||||
proc sql;
|
||||
create table work.example5(
|
||||
TX_FROM float format=datetime19.,
|
||||
DD_TYPE char(16),
|
||||
DD_SOURCE char(2048),
|
||||
DD_SHORTDESC char(256)
|
||||
);
|
||||
proc datasets lib=work noprint;
|
||||
modify example5;
|
||||
index create tx_from /nomiss unique;
|
||||
quit;
|
||||
%mp_getpk(work,ds=example5,outds=test5)
|
||||
data _null_;
|
||||
set work.test5;
|
||||
call symputx('test5',pk_fields);
|
||||
run;
|
||||
%mp_assert(
|
||||
iftrue=("&test5"="TX_FROM"),
|
||||
desc=mp_getpk captures single column not null unique index,
|
||||
outds=work.test_results
|
||||
)
|
||||
|
||||
/* unique & not null COMPOSITE INDEX captured */
|
||||
proc sql;
|
||||
create table work.example6(
|
||||
TX_FROM float format=datetime19.,
|
||||
DD_TYPE char(16),
|
||||
DD_SOURCE char(2048),
|
||||
DD_SHORTDESC char(256)
|
||||
);
|
||||
proc datasets lib=work noprint;
|
||||
modify example6;
|
||||
index create pk_6=(tx_from dd_type) /nomiss unique;
|
||||
quit;
|
||||
%mp_getpk(work,ds=example6,outds=test6)
|
||||
data _null_;
|
||||
set work.test6;
|
||||
call symputx('test6',pk_fields);
|
||||
run;
|
||||
%mp_assert(
|
||||
iftrue=("&test6"="TX_FROM DD_TYPE"),
|
||||
desc=mp_getpk captures multiple column not null unique index,
|
||||
outds=work.test_results
|
||||
)
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
|
||||
|
||||
%macro gsubtest();
|
||||
%if "%substr(&sysver,1,4)"="V.04" %then %do;
|
||||
%if "%substr(&sysver.XX,1,4)"="V.04" %then %do;
|
||||
%put %str(ERR)OR: Viya 4 does not support the IO library in lua;
|
||||
%return;
|
||||
%end;
|
||||
|
||||
61
tests/crossplatform/mp_hashdataset.test.sas
Normal file
61
tests/crossplatform/mp_hashdataset.test.sas
Normal file
@@ -0,0 +1,61 @@
|
||||
/**
|
||||
@file
|
||||
@brief Testing mp_hashdataset.sas macro
|
||||
|
||||
<h4> SAS Macros </h4>
|
||||
@li mf_nobs.sas
|
||||
@li mp_hashdataset.sas
|
||||
@li mp_assert.sas
|
||||
@li mp_assertscope.sas
|
||||
|
||||
**/
|
||||
|
||||
/* test 1 - regular DS */
|
||||
data work.test;
|
||||
set sashelp.vextfl;
|
||||
missval=.;
|
||||
misscval='';
|
||||
run;
|
||||
|
||||
%mp_assertscope(SNAPSHOT)
|
||||
%mp_hashdataset(test)
|
||||
%mp_assertscope(COMPARE)
|
||||
|
||||
%mp_assert(
|
||||
iftrue=(&syscc=0),
|
||||
desc=Regular test works,
|
||||
outds=work.test_results
|
||||
)
|
||||
|
||||
%mp_hashdataset(test,outds=work.test2)
|
||||
|
||||
%mp_assert(
|
||||
iftrue=(&syscc=0),
|
||||
desc=hash with output runs without errors,
|
||||
outds=work.test_results
|
||||
)
|
||||
|
||||
%mp_assert(
|
||||
iftrue=(%mf_nobs(work.test2)=1),
|
||||
desc=output has 1 row,
|
||||
outds=work.test_results
|
||||
)
|
||||
|
||||
|
||||
data work.test3a;
|
||||
set work.test;
|
||||
stop;
|
||||
run;
|
||||
%mp_hashdataset(test3a,outds=work.test3b)
|
||||
|
||||
%mp_assert(
|
||||
iftrue=(&syscc=0),
|
||||
desc=hash with zero-row input runs without errors,
|
||||
outds=work.test_results
|
||||
)
|
||||
|
||||
%mp_assert(
|
||||
iftrue=(%mf_nobs(work.test3b)=1),
|
||||
desc=test 3 output has 1 row,
|
||||
outds=work.test_results
|
||||
)
|
||||
53
tests/crossplatform/mp_jsonout.test.3.sas
Normal file
53
tests/crossplatform/mp_jsonout.test.3.sas
Normal file
@@ -0,0 +1,53 @@
|
||||
/**
|
||||
@file
|
||||
@brief Testing mp_jsonout.sas macro with non-standard chars
|
||||
|
||||
<h4> SAS Macros </h4>
|
||||
@li mp_jsonout.sas
|
||||
@li mp_assert.sas
|
||||
|
||||
**/
|
||||
|
||||
filename webref temp;
|
||||
|
||||
data demo;
|
||||
do x='"','0A'x,'0D'x,'09'x,'00'x,'0E'x,'0F'x,'01'x,'02'x,'10'x,'11'x,'\';
|
||||
output;
|
||||
end;
|
||||
run;
|
||||
%mp_jsonout(OPEN,jref=webref)
|
||||
%mp_jsonout(OBJ,demo,jref=webref)
|
||||
%mp_jsonout(CLOSE,jref=webref)
|
||||
|
||||
data _null_;
|
||||
infile webref;
|
||||
input;
|
||||
putlog _infile_;
|
||||
run;
|
||||
|
||||
libname web JSON fileref=webref;
|
||||
|
||||
%mp_assert(
|
||||
iftrue=(&syscc=0),
|
||||
desc=Checking for error condition with special chars export,
|
||||
outds=work.test_results
|
||||
)
|
||||
|
||||
/*
|
||||
data _null_;
|
||||
set work.demo (in=start) web.demo (in=end);
|
||||
put (_all_)(=);
|
||||
run;
|
||||
proc sql;
|
||||
describe table work.demo;
|
||||
describe table web.demo;
|
||||
*/
|
||||
|
||||
proc compare base=work.demo compare=web.demo(keep=x);
|
||||
quit;
|
||||
|
||||
%mp_assert(
|
||||
iftrue=(&sysinfo=0),
|
||||
desc=Returned json is identical to input table for all special chars,
|
||||
outds=work.test_results
|
||||
)
|
||||
@@ -3,6 +3,7 @@
|
||||
@brief Testing mp_loadformat.sas macro
|
||||
|
||||
<h4> SAS Macros </h4>
|
||||
@li mddl_dc_difftable.sas
|
||||
@li mp_loadformat.sas
|
||||
@li mp_assert.sas
|
||||
@li mp_assertscope.sas
|
||||
@@ -12,6 +13,8 @@
|
||||
/* prep format catalog */
|
||||
libname perm (work);
|
||||
|
||||
%mddl_dc_difftable(libds=perm.audit)
|
||||
|
||||
data work.loadfmts;
|
||||
length fmtname $32;
|
||||
eexcl='Y';
|
||||
@@ -49,7 +52,7 @@ run;
|
||||
%mp_loadformat(perm.testcat
|
||||
,work.stagedata
|
||||
,loadtarget=YES
|
||||
,auditlibds=0
|
||||
,auditlibds=perm.audit
|
||||
,locklibds=0
|
||||
,delete_col=deleteme
|
||||
,outds_add=add_test1
|
||||
@@ -73,4 +76,18 @@ run;
|
||||
iftrue=(%mf_nobs(mod_test1)=100),
|
||||
desc=Test 1 - mod obs,
|
||||
outds=work.test_results
|
||||
)
|
||||
)
|
||||
%mp_assert(
|
||||
iftrue=(%mf_nobs(perm.audit)=7329),
|
||||
desc=Test 1 - audit table updated,
|
||||
outds=work.test_results
|
||||
)
|
||||
data work.difftest;
|
||||
set perm.audit;
|
||||
where is_diff=1;
|
||||
run;
|
||||
%mp_assert(
|
||||
iftrue=(%mf_nobs(work.difftest)>0),
|
||||
desc=Test 1 - diffs were found,
|
||||
outds=work.test_results
|
||||
)
|
||||
|
||||
@@ -39,7 +39,7 @@ run;
|
||||
%let str=%str(replacewith trailing spaces );
|
||||
%let rep=%str( with more spaces );
|
||||
data _null_;
|
||||
file &test2;
|
||||
file &test2 lrecl=500;
|
||||
put 'blahblah';
|
||||
put "blahblah&str.blah&str. replace &str.X";
|
||||
put "blahbreplacewith&str.spacesahblah";
|
||||
@@ -47,7 +47,7 @@ run;
|
||||
%mp_replace(&test2, findvar=str, replacevar=rep)
|
||||
|
||||
data _null_;
|
||||
infile &test2;
|
||||
infile &test2 lrecl=500;
|
||||
input;
|
||||
if _n_=2 then call symputx('test2resulta',_infile_);
|
||||
if _n_=3 then call symputx('test2resultb',_infile_);
|
||||
@@ -63,3 +63,33 @@ run;
|
||||
desc=Checking second replace 3rd row,
|
||||
outds=work.test_results
|
||||
)
|
||||
|
||||
|
||||
%let test3="&sasjswork/file3.txt";
|
||||
%let str=%str(replace.string.with.dots );
|
||||
%let rep=%str( more.dots);
|
||||
data _null_;
|
||||
file &test3 lrecl=500;
|
||||
put 'blahblah';
|
||||
put "blahblah&str.blah&str. replace &str.X";
|
||||
put "blahbreplacewith&str.spacesahblah";
|
||||
run;
|
||||
%mp_replace(&test3, findvar=str, replacevar=rep)
|
||||
|
||||
data _null_;
|
||||
infile &test3 lrecl=500;
|
||||
input;
|
||||
if _n_=2 then call symputx('test3resulta',_infile_);
|
||||
if _n_=3 then call symputx('test3resultb',_infile_);
|
||||
run;
|
||||
|
||||
%mp_assert(
|
||||
iftrue=("&test3resulta" = "blahblah&rep.blah&rep. replace &rep.X"),
|
||||
desc=Checking third replace 2nd row (dots),
|
||||
outds=work.test_results
|
||||
)
|
||||
%mp_assert(
|
||||
iftrue=("&test3resultb" = "blahbreplacewith&rep.spacesahblah"),
|
||||
desc=Checking third replace 3rd row (dots),
|
||||
outds=work.test_results
|
||||
)
|
||||
|
||||
@@ -55,37 +55,28 @@ run;
|
||||
outlib=testlib1,
|
||||
outref=test1
|
||||
)
|
||||
|
||||
%global test1a test1b test1c test1d;
|
||||
%let test1=FAIL;
|
||||
data _null_;
|
||||
infile test1;
|
||||
input;
|
||||
putlog _n_ _infile_;
|
||||
if _infile_=', "somedata1":' then call symputx('test1a','PASS');
|
||||
if _infile_='{"X":1 ,"Y":" t\"w\"o" ,"Z":"Z" }' then
|
||||
call symputx('test1b','PASS');
|
||||
if _infile_='], "somedata2":' then call symputx('test1c','PASS');
|
||||
if _infile_='{"X":1 ,"Y":" t\"w\"o" ,"Z":"Z" }' then
|
||||
call symputx('test1d','PASS');
|
||||
set testlib1.somedata1;
|
||||
if x=1 and y=' t"w"o' and z="Z" then call symputx('test1','PASS');
|
||||
putlog (_all_)(=);
|
||||
run;
|
||||
|
||||
%let test2=FAIL;
|
||||
data _null_;
|
||||
set testlib1.somedata2;
|
||||
if x=1 and y=' t"w"o' and z="Z" then call symputx('test2','PASS');
|
||||
putlog (_all_)(=);
|
||||
run;
|
||||
|
||||
|
||||
%mp_assert(
|
||||
iftrue=(&test1a=PASS),
|
||||
desc=Test 1 table 1 name,
|
||||
iftrue=(&test1=PASS),
|
||||
desc=somedata1 created correctly,
|
||||
outds=work.test_results
|
||||
)
|
||||
%mp_assert(
|
||||
iftrue=(&test1b=PASS),
|
||||
desc=Test 1 table 1 values,
|
||||
iftrue=(&test2=PASS),
|
||||
desc=somedata2 created correctly,
|
||||
outds=work.test_results
|
||||
)
|
||||
%mp_assert(
|
||||
iftrue=(&test1c=PASS),
|
||||
desc=Test 1 table 2 name,
|
||||
outds=work.test_results
|
||||
)
|
||||
%mp_assert(
|
||||
iftrue=(&test1d=PASS),
|
||||
desc=Test 1 table 2 values,
|
||||
outds=work.test_results
|
||||
)
|
||||
41
tests/sas9only/mm_spkexport.test.sas
Normal file
41
tests/sas9only/mm_spkexport.test.sas
Normal file
@@ -0,0 +1,41 @@
|
||||
/**
|
||||
@file
|
||||
@brief Testing mm_webout macro
|
||||
|
||||
<h4> SAS Macros </h4>
|
||||
@li mm_spkexport.sas
|
||||
@li mp_assert.sas
|
||||
@li mp_as
|
||||
|
||||
**/
|
||||
|
||||
|
||||
%* create sample text file as input to the macro;
|
||||
filename tmp temp;
|
||||
data _null_;
|
||||
file tmp;
|
||||
put '%let mmxuser="sasdemo";';
|
||||
put '%let mmxpass="Mars321";';
|
||||
run;
|
||||
|
||||
filename myref "%sysfunc(pathname(work))/mmxexport.sh"
|
||||
permission='A::u::rwx,A::g::r-x,A::o::---';
|
||||
%mp_assertscope(SNAPSHOT)
|
||||
%mm_spkexport(metaloc=%str(/Shared Data)
|
||||
,outref=myref
|
||||
,secureref=tmp
|
||||
,cmdoutloc=%str(/tmp)
|
||||
)
|
||||
%mp_assertscope(COMPARE)
|
||||
|
||||
data _null_;
|
||||
infile tmp;
|
||||
input;
|
||||
putlog _infile_;
|
||||
call symputx('nobs',_n_);
|
||||
run;
|
||||
|
||||
%mp_assert(
|
||||
iftrue=(&nobs>2),
|
||||
desc=Check if content was created
|
||||
)
|
||||
@@ -9,11 +9,12 @@
|
||||
|
||||
**/
|
||||
|
||||
%let orig_sasjs_stpsrv_header_loc=&sasjs_stpsrv_header_loc;
|
||||
%let sasjs_stpsrv_header_loc=%sysfunc(pathname(work))/header.txt;
|
||||
|
||||
%mp_assertscope(SNAPSHOT)
|
||||
%mfs_httpheader(Content-type,application/csv)
|
||||
%mp_assertscope(COMPARE)
|
||||
%mfs_httpheader(Content-Type,application/csv)
|
||||
%mp_assertscope(COMPARE,ignorelist=sasjs_stpsrv_header_loc)
|
||||
|
||||
data _null_;
|
||||
infile "&sasjs_stpsrv_header_loc";
|
||||
@@ -27,12 +28,12 @@ run;
|
||||
outds=work.test_results
|
||||
)
|
||||
%mp_assert(
|
||||
iftrue=("&test1"="Content-type: application/csv"),
|
||||
iftrue=("&test1"="Content-Type: application/csv"),
|
||||
desc=Checking line was created,
|
||||
outds=work.test_results
|
||||
)
|
||||
|
||||
%mfs_httpheader(Content-type,application/text)
|
||||
%mfs_httpheader(Content-Type,application/text)
|
||||
%let test2=0;
|
||||
data _null_;
|
||||
infile "&sasjs_stpsrv_header_loc";
|
||||
@@ -46,7 +47,11 @@ run;
|
||||
outds=work.test_results
|
||||
)
|
||||
%mp_assert(
|
||||
iftrue=("&test2"="Content-type: application/text"),
|
||||
iftrue=("&test2"="Content-Type: application/text"),
|
||||
desc=Checking line was created,
|
||||
outds=work.test_results
|
||||
)
|
||||
|
||||
|
||||
/* reset header so the test will pass */
|
||||
%let sasjs_stpsrv_header_loc=&orig_sasjs_stpsrv_header_loc;
|
||||
50
tests/serveronly/ms_createuser.test.sas
Normal file
50
tests/serveronly/ms_createuser.test.sas
Normal file
@@ -0,0 +1,50 @@
|
||||
/**
|
||||
@file
|
||||
@brief Testing ms_createuser.sas macro
|
||||
|
||||
<h4> SAS Macros </h4>
|
||||
@li mf_getuniquename.sas
|
||||
@li mp_assert.sas
|
||||
@li mp_assertscope.sas
|
||||
@li ms_createuser.sas
|
||||
@li ms_getusers.sas
|
||||
|
||||
**/
|
||||
|
||||
%let user=%substr(%mf_getuniquename(),1,8);
|
||||
|
||||
%mp_assertscope(SNAPSHOT)
|
||||
%ms_createuser(&user,passwrd,outds=test1,mdebug=&sasjs_mdebug)
|
||||
%mp_assertscope(COMPARE
|
||||
,ignorelist=MCLIB0_JADP1LEN MCLIB0_JADPNUM MCLIB0_JADVLEN
|
||||
)
|
||||
|
||||
%let id=0;
|
||||
data _null_;
|
||||
set work.test1;
|
||||
call symputx('id',id);
|
||||
run;
|
||||
%mp_assert(
|
||||
iftrue=(&id>0),
|
||||
desc=Checking that user was created with an ID,
|
||||
outds=work.test_results
|
||||
)
|
||||
|
||||
/* double check by querying the list of users */
|
||||
%ms_getusers(outds=work.test2)
|
||||
%let checkid=0;
|
||||
data _null_;
|
||||
set work.test2;
|
||||
where username="&user";
|
||||
call symputx('checkid',id);
|
||||
run;
|
||||
%mp_assert(
|
||||
iftrue=(&checkid=&id),
|
||||
desc=Checking that fetched user exists and has the same ID,
|
||||
outds=work.test_results
|
||||
)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
28
tests/serveronly/ms_createwebservice.test.sas
Normal file
28
tests/serveronly/ms_createwebservice.test.sas
Normal file
@@ -0,0 +1,28 @@
|
||||
/**
|
||||
@file
|
||||
@brief Testing ms_createwebservice.sas macro
|
||||
|
||||
<h4> SAS Macros </h4>
|
||||
@li mf_getuniquefileref.sas
|
||||
@li mp_assertscope.sas
|
||||
@li ms_createwebservice.sas
|
||||
|
||||
**/
|
||||
|
||||
%let path=&mcTestAppLoc/ms_createwebservice;
|
||||
%let name=myservice;
|
||||
%let fref=%mf_getuniquefileref();
|
||||
|
||||
data _null_;
|
||||
file &fref lrecl=3000;
|
||||
put '%put hello world;';
|
||||
run;
|
||||
|
||||
%mp_assertscope(SNAPSHOT)
|
||||
%ms_createwebservice(path=&path,name=&name,code=&fref,mdebug=&sasjs_mdebug)
|
||||
%mp_assertscope(COMPARE)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
61
tests/serveronly/ms_deletefile.test.sas
Normal file
61
tests/serveronly/ms_deletefile.test.sas
Normal file
@@ -0,0 +1,61 @@
|
||||
/**
|
||||
@file
|
||||
@brief Testing ms_deletefile.sas macro
|
||||
|
||||
<h4> SAS Macros </h4>
|
||||
@li ms_createfile.sas
|
||||
@li ms_deletefile.sas
|
||||
@li ms_getfile.sas
|
||||
@li mp_assert.sas
|
||||
@li mp_assertscope.sas
|
||||
|
||||
**/
|
||||
|
||||
|
||||
/* first make a remote file */
|
||||
filename stpcode temp;
|
||||
%let fname=%mf_getuniquename();
|
||||
data _null_;
|
||||
file stpcode;
|
||||
put "data &fname;run;";
|
||||
run;
|
||||
%ms_createfile(/sasjs/tests/&fname..sas
|
||||
,inref=stpcode
|
||||
,mdebug=1
|
||||
)
|
||||
|
||||
%ms_getfile(/sasjs/tests/&fname..sas,outref=testref)
|
||||
|
||||
%let test1=0;
|
||||
data _null_;
|
||||
infile testref;
|
||||
input;
|
||||
call symputx('test1',_infile_);
|
||||
run;
|
||||
|
||||
%mp_assert(
|
||||
iftrue=("&test1"="data &fname;run;"),
|
||||
desc=Make sure the file was created,
|
||||
outds=work.test_results
|
||||
)
|
||||
|
||||
%mp_assertscope(SNAPSHOT)
|
||||
%ms_deletefile(/sasjs/tests/&fname..sas,mdebug=1)
|
||||
%mp_assertscope(COMPARE)
|
||||
|
||||
%ms_getfile(/sasjs/tests/&fname..sas,outref=testref2)
|
||||
|
||||
%let test2=0;
|
||||
data _null_;
|
||||
infile testref2;
|
||||
input;
|
||||
call symputx('test2',_infile_);
|
||||
run;
|
||||
|
||||
%mp_assert(
|
||||
iftrue=("&test2"="%str(Err)or: File doesn't exist."),
|
||||
desc=Make sure the file was deleted,
|
||||
outds=work.test_results
|
||||
)
|
||||
|
||||
|
||||
23
tests/serveronly/ms_getusers.test.sas
Normal file
23
tests/serveronly/ms_getusers.test.sas
Normal file
@@ -0,0 +1,23 @@
|
||||
/**
|
||||
@file
|
||||
@brief Testing ms_getusers.sas macro
|
||||
|
||||
<h4> SAS Macros </h4>
|
||||
@li ms_getusers.sas
|
||||
@li mp_assertdsobs.sas
|
||||
@li mp_assertscope.sas
|
||||
|
||||
**/
|
||||
|
||||
|
||||
%mp_assertscope(SNAPSHOT)
|
||||
%ms_getusers(outds=work.test1,mdebug=&sasjs_mdebug)
|
||||
%mp_assertscope(COMPARE
|
||||
,ignorelist=MCLIB0_JADP1LEN MCLIB0_JADPNUM MCLIB0_JADVLEN
|
||||
)
|
||||
|
||||
%mp_assertdsobs(work.test1,test=ATLEAST 1)
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -3,14 +3,32 @@
|
||||
@brief Testing ms_runstp.sas macro
|
||||
|
||||
<h4> SAS Macros </h4>
|
||||
@li ms_runstp.sas
|
||||
@li mf_getuniquename.sas
|
||||
@li mp_assert.sas
|
||||
@li mp_assertscope.sas
|
||||
@li ms_createfile.sas
|
||||
@li ms_runstp.sas
|
||||
|
||||
**/
|
||||
|
||||
/* first, create an STP to run */
|
||||
filename stpcode temp;
|
||||
data _null_;
|
||||
file stpcode;
|
||||
put '%put hello world;';
|
||||
put '%put _all_;';
|
||||
run;
|
||||
|
||||
options mprint;
|
||||
%let fname=%mf_getuniquename();
|
||||
|
||||
%ms_createfile(/sasjs/tests/&fname..sas
|
||||
,inref=stpcode
|
||||
,mdebug=1
|
||||
)
|
||||
|
||||
%mp_assertscope(SNAPSHOT)
|
||||
%ms_runstp(/Public/app/frs/allan/services/common/appinit
|
||||
%ms_runstp(/sasjs/tests/&fname
|
||||
,debug=131
|
||||
,outref=weboot
|
||||
)
|
||||
@@ -24,20 +42,16 @@ data _null_;
|
||||
putlog _infile_;
|
||||
run;
|
||||
|
||||
data work.httpheaders;
|
||||
set webeen.httpheaders;
|
||||
call symputx('test1',content_type);
|
||||
run;
|
||||
|
||||
%let test1=0;
|
||||
data work.log;
|
||||
set webeen.log;
|
||||
put (_all_)(=);
|
||||
if _n_>10 then stop;
|
||||
if _n_>10 then call symputx('test1',1);
|
||||
run;
|
||||
|
||||
%mp_assert(
|
||||
iftrue=("&test1"="application/json"),
|
||||
desc=Checking line was created,
|
||||
iftrue=("&test1"="1"),
|
||||
desc=Checking log was returned,
|
||||
outds=work.test_results
|
||||
)
|
||||
|
||||
|
||||
87
tests/serveronly/ms_testservice.test.sas
Normal file
87
tests/serveronly/ms_testservice.test.sas
Normal file
@@ -0,0 +1,87 @@
|
||||
/**
|
||||
@file
|
||||
@brief Testing ms_testservice.sas macro
|
||||
|
||||
<h4> SAS Macros </h4>
|
||||
@li ms_createwebservice.sas
|
||||
@li ms_testservice.sas
|
||||
@li mp_assert.sas
|
||||
|
||||
**/
|
||||
|
||||
filename ft15f001 temp;
|
||||
parmcards4;
|
||||
%put Initialising sendObj: ;
|
||||
%put _all_;
|
||||
%webout(FETCH)
|
||||
%webout(OPEN)
|
||||
%macro x();
|
||||
%if (%symexist(sasjs_tables) and %length(&sasjs_tables)>0)
|
||||
%then %do i=1 %to %sysfunc(countw(&sasjs_tables));
|
||||
%let table=%scan(&sasjs_tables,&i);
|
||||
%webout(OBJ,&table,missing=STRING)
|
||||
%end;
|
||||
%else %do i=1 %to &_webin_file_count;
|
||||
%webout(OBJ,&&_webin_name&i,missing=STRING)
|
||||
%end;
|
||||
%mend x; %x()
|
||||
%webout(CLOSE)
|
||||
;;;;
|
||||
%put creating web service: &mcTestAppLoc/services;
|
||||
%ms_createwebservice(
|
||||
path=&mcTestAppLoc/services,
|
||||
name=sendObj,
|
||||
mdebug=&sasjs_mdebug
|
||||
)
|
||||
%put created web service: &mcTestAppLoc/services;
|
||||
|
||||
%mp_assert(
|
||||
iftrue=(&syscc=0),
|
||||
desc=No errors after service creation,
|
||||
outds=work.test_results
|
||||
)
|
||||
|
||||
/**
|
||||
* Test 1 - send a dataset
|
||||
*/
|
||||
data work.somedata1 work.somedata2;
|
||||
x=1;
|
||||
y=' t"w"o';
|
||||
z=.z;
|
||||
label x='x factor';
|
||||
output;
|
||||
run;
|
||||
|
||||
%ms_testservice(&mcTestAppLoc/services/sendObj,
|
||||
inputdatasets=work.somedata1 work.somedata2,
|
||||
debug=log,
|
||||
mdebug=1,
|
||||
outlib=testlib1,
|
||||
outref=test1
|
||||
)
|
||||
|
||||
%let test1=FAIL;
|
||||
data _null_;
|
||||
set testlib1.somedata1;
|
||||
if x=1 and y=' t"w"o' and z="Z" then call symputx('test1','PASS');
|
||||
putlog (_all_)(=);
|
||||
run;
|
||||
|
||||
%let test2=FAIL;
|
||||
data _null_;
|
||||
set testlib1.somedata2;
|
||||
if x=1 and y=' t"w"o' and z="Z" then call symputx('test2','PASS');
|
||||
putlog (_all_)(=);
|
||||
run;
|
||||
|
||||
|
||||
%mp_assert(
|
||||
iftrue=(&test1=PASS),
|
||||
desc=somedata1 created correctly,
|
||||
outds=work.test_results
|
||||
)
|
||||
%mp_assert(
|
||||
iftrue=(&test2=PASS),
|
||||
desc=somedata2 created correctly,
|
||||
outds=work.test_results
|
||||
)
|
||||
@@ -3,27 +3,34 @@
|
||||
@brief init file for tests
|
||||
|
||||
<h4> SAS Macros </h4>
|
||||
@li mf_uid.sas
|
||||
@li mp_init.sas
|
||||
@li mv_webout.sas
|
||||
@li ms_webout.sas
|
||||
|
||||
**/
|
||||
|
||||
/* location in metadata or SAS Drive for temporary files */
|
||||
%let mcTestAppLoc=/Public/temp/macrocore;
|
||||
%let mcTestAppLoc=/tmp/tests/sasjs/core/%mf_uid();
|
||||
|
||||
/* set defaults */
|
||||
%mp_init()
|
||||
|
||||
%global _debug;
|
||||
options lrecl=80;
|
||||
|
||||
%global _debug sasjs_mdebug;
|
||||
|
||||
%let sasjs_mdebug=0;
|
||||
|
||||
%macro loglevel();
|
||||
%if "&_debug"="2477" or "&_debug"="fields,log,trace" %then %do;
|
||||
%if "&_debug"="2477" or "&_debug"="fields,log,trace" or "&_debug"="131"
|
||||
%then %do;
|
||||
%put debug mode activated;
|
||||
options mprint mprintnest;
|
||||
%let sasjs_mdebug=1;
|
||||
%end;
|
||||
%mend loglevel;
|
||||
|
||||
%loglevel()
|
||||
|
||||
%put Initialised &_program;
|
||||
%put _all_;
|
||||
%put _all_;
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
|
||||
%mp_assert(
|
||||
iftrue=(&syscc=0),
|
||||
desc=Checking final error condition,
|
||||
desc=Checking final err condition,
|
||||
outds=work.test_results
|
||||
)
|
||||
|
||||
|
||||
@@ -1,15 +1,33 @@
|
||||
/**
|
||||
@file
|
||||
@brief Testing mm_webout macro
|
||||
@brief Testing mv_webout macro
|
||||
|
||||
<h4> SAS Macros </h4>
|
||||
@li mf_getuniquefileref.sas
|
||||
@li mv_webout.sas
|
||||
@li mp_assert.sas
|
||||
@li mp_assertdsobs.sas
|
||||
|
||||
**/
|
||||
|
||||
|
||||
/* testing FETCHing (WEB approach) */
|
||||
|
||||
data _null_;
|
||||
call symputx('sasjs1data','area:$char4.'!!'0d0a'x!!'Adak');
|
||||
call symputx('sasjs_tables','areas');
|
||||
run;
|
||||
%put &=sasjs1data;
|
||||
|
||||
%mv_webout(FETCH)
|
||||
|
||||
%mp_assertdsobs(work.areas,
|
||||
desc=Test input table has 1 row,
|
||||
test=EQUALS 1,
|
||||
outds=work.test_results
|
||||
)
|
||||
|
||||
|
||||
%let fref=%mf_getuniquefileref();
|
||||
%global _metaperson;
|
||||
data some datasets;
|
||||
@@ -38,4 +56,4 @@ run;
|
||||
%mp_assert(
|
||||
iftrue=(%str(&checkval)=%str(&sysvlong)),
|
||||
desc=Check if the sysvlong value was created
|
||||
)
|
||||
)
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user