1
0
mirror of https://github.com/sasjs/core.git synced 2025-12-11 14:34:35 +00:00

Compare commits

...

50 Commits

Author SHA1 Message Date
Allan Bowe
f28f6b1530 Merge pull request #236 from sasjs/allanbowe/error-data-set-sashelp-235
fix: conditional logic for mp_getconstraints, test also updated.  Closes #235
2022-05-10 18:00:20 +03:00
Allan Bowe
cb4ea71e81 fix: conditional logic for mp_getconstraints, test also updated. Closes #235 2022-05-10 14:59:31 +00:00
Allan Bowe
fe94d3781a Merge pull request #234 from sasjs/all-contributors/add-eltociear
docs: add eltociear as a contributor for code
2022-05-10 13:54:01 +03:00
Allan Bowe
7c17b39dad Merge pull request #233 from eltociear/patch-1
chore: fix typo in mddl_sas_cntlout.sas
2022-05-10 13:53:26 +03:00
Allan Bowe
73dab4c651 chore: updating all.sas 2022-05-10 10:53:05 +00:00
allcontributors[bot]
5d72843167 docs: update .all-contributorsrc [skip ci] 2022-05-10 10:52:36 +00:00
allcontributors[bot]
f43df47cff docs: update README.md [skip ci] 2022-05-10 10:52:35 +00:00
Ikko Ashimine
aaca26770b chore: fix typo in mddl_sas_cntlout.sas
accomodate -> accommodate
2022-05-10 19:41:31 +09:00
Allan Bowe
4a124d5bd8 Update README.md 2022-05-09 09:58:31 +01:00
Allan Bowe
03cd52a01a chore: readme update and removal of main.dox in .npmignore (to allow doxygen folder descriptions in downstream apps) 2022-05-08 17:37:05 +00:00
Allan Bowe
da79181b00 Merge pull request #232 from sasjs/allanbowe/update-headers-in-sasjs-231
fix: using latest sasjs headers, closes #231
2022-05-08 01:27:02 +03:00
Allan Bowe
a405104052 fix: using latest sasjs headers, closes #231 2022-05-07 22:25:49 +00:00
Allan Bowe
56fdaa65d2 Merge pull request #230 from sasjs/dttmfix
feat: mf_dttm macro and associated test
2022-05-07 21:12:33 +03:00
Allan Bowe
9d60c49c9f chore: incorrect dependency 2022-05-07 18:04:28 +00:00
Allan Bowe
380170d5ba chore: all.sas 2022-05-07 18:02:16 +00:00
Allan Bowe
4b450f2091 fix: implementation of mf_fmtddtm() 2022-05-07 18:02:00 +00:00
Allan Bowe
1b013fbf1c fix: rename to mf_fmtdttm() 2022-05-07 17:49:06 +00:00
Allan Bowe
bf7459bd2d feat: mf_dttm macro and associated test 2022-05-07 17:45:36 +00:00
Allan Bowe
1096db0846 Merge pull request #229 from sasjs/runtimeissues
Improving WPS compatibility
2022-05-03 18:48:18 +03:00
Allan Bowe
fc9b765246 chore: updating all.sas 2022-05-03 15:46:34 +00:00
Allan Bowe
4a8f7bb014 fix: all the fixings 2022-05-03 15:46:15 +00:00
Allan Bowe
e0469be0d8 feat: updating mf_getvarcount to allow filtering by column type 2022-05-03 15:24:22 +00:00
Allan Bowe
e9e576b5ec fix: forcing misstype to NULL in ms_webout where not supported 2022-05-02 23:17:11 +00:00
Allan Bowe
1a32d114f1 fix: conditional execution of mp_init() 2022-05-02 22:32:32 +00:00
Allan Bowe
94e83f6b8d fix: updating mf_existfeature for constraint check 2022-05-02 22:24:56 +00:00
Allan Bowe
35a6dede6f fix: enabling ms_testservice() without inputs 2022-05-02 13:56:22 +00:00
Allan Bowe
039ec397dd chore: local scoping vars in mp_testservice 2022-04-30 19:16:25 +00:00
Allan Bowe
dce4630eb8 Merge pull request #228 from sasjs/allanbowe/add-markdown-as-mime-227
feat: adding MARKDOWN support to `mp_streamfile()`
2022-04-30 18:58:54 +03:00
Allan Bowe
1e142f042b chore: improving docs 2022-04-30 15:58:00 +00:00
Allan Bowe
7caca2f139 chore: typo in docs 2022-04-30 15:56:36 +00:00
Allan Bowe
61556b2de8 feat: adding MARKDOWN support to mp_streamfile(), correcting Content-type case issue also 2022-04-30 15:53:59 +00:00
Allan Bowe
9e12409389 fix: fileref cleanup in SASJS mode for mp_testservice() 2022-04-29 19:18:03 +00:00
Allan Bowe
c8050f5a79 Merge pull request #226 from sasjs/allanbowe/mp-testservice-error-225
3 new macros and updates to several others.  Supporting sasjs/server and options lrecl=80
2022-04-28 23:58:40 +03:00
munja
cb4f71c7cd chore: all.sas generation 2022-04-28 21:47:42 +01:00
munja
a39f4e4eee fix: ensuring test passes for mfs_httpheader 2022-04-28 21:47:23 +01:00
munja
b525b4171d fix: updating ms_runstp to accept parameters and file inputs. Explicitly setting lrecl everywhere. Adding lrecl=80 as default in testinit.sas 2022-04-28 21:06:28 +01:00
munja
f2d80b3b63 feat: ms_testservice.sas macro for testing services on sasjs/server 2022-04-28 21:02:54 +01:00
munja
96dda87f37 chore: all.sas update 2022-04-28 21:01:41 +01:00
munja
3435509eec fix: avoiding lua compilation issues by setting wide enough lrecl 2022-04-28 20:57:44 +01:00
munja
42f2767129 fix: mac var and chop point issue in mp_chop.sas 2022-04-28 19:20:03 +01:00
munja
099a5f7840 fix: ms_createfile will now overwrite if existing 2022-04-28 11:17:25 +01:00
munja
c83ea705a2 fix: ensuring sufficient lrecl in mf_getuniquefilref 2022-04-27 23:04:55 +01:00
munja
9ea6c875f2 fix: fixing tests for mp_chop 2022-04-27 12:14:26 +01:00
Allan Bowe
0728f72c4f fix: adding mp_chop to mp_testservice (WIP) 2022-04-26 15:29:12 +00:00
Allan Bowe
a90a6f00cf chore: adding test for mp_chop() 2022-04-26 15:13:18 +00:00
Allan Bowe
f71e53af8d feat: mp_chop() macro 2022-04-26 15:07:31 +00:00
Allan Bowe
cc1b971e19 fix: setting blank value to 0 in mf_isint() 2022-04-26 14:04:54 +00:00
Allan Bowe
8484c752ed fix: wip - requires new mp_chop() macro to parse long JSON response 2022-04-25 22:19:46 +00:00
Allan Bowe
8bd31e6c97 fix: tests for ms_createwebservice and mp_createwebservice 2022-04-25 21:33:14 +00:00
Allan Bowe
f9b0f87f44 feat: ms_createwebservice macro (and update to build.py) 2022-04-25 21:14:12 +00:00
59 changed files with 3126 additions and 360 deletions

View File

@@ -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,

View File

@@ -6,7 +6,6 @@ sasjs/
.github/
.git-hooks/
.vscode/
main.dox
make_singlefile.sh
*.md
.all-contributorsrc

View File

@@ -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:
@@ -224,12 +224,13 @@ 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 -->
[![All Contributors](https://img.shields.io/badge/all_contributors-11-orange.svg?style=flat-square)](#contributors-)
[![All Contributors](https://img.shields.io/badge/all_contributors-12-orange.svg?style=flat-square)](#contributors-)
<!-- ALL-CONTRIBUTORS-BADGE:END -->
Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)):
@@ -251,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>

1456
all.sas

File diff suppressed because it is too large Load Diff

View File

@@ -30,7 +30,7 @@
%put No feature was requested for detection;
%end;
%else %if &feature=COLCONSTRAINTS %then %do;
%if %substr(&sysver,1,1)=4 %then 0;
%if "%substr(&sysver,1,1)"="4" or "%substr(&sysver,1,1)"="5" %then 0;
%else 1;
%end;
%else %if &feature=PROCLUA %then %do;

42
base/mf_fmtdttm.sas Normal file
View 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;

View File

@@ -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;

View File

@@ -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:

View File

@@ -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;

View File

@@ -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;

View File

@@ -89,7 +89,12 @@
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_;

194
base/mp_chop.sas Normal file
View 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;

View File

@@ -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

View File

@@ -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.

View File

@@ -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));

View File

@@ -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;

View File

@@ -67,7 +67,7 @@ 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;

View File

@@ -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;

View File

@@ -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"

View File

@@ -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

View File

@@ -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

View File

@@ -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);

View File

@@ -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,30 +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;
%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/html');
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;
@@ -134,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;
@@ -149,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;
@@ -167,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))
@@ -176,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;
@@ -186,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;

View File

@@ -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:;

View File

@@ -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()

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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; ';
@@ -314,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 '); ';
@@ -455,25 +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 ' /* 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; ';
/* 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';

View File

@@ -43,7 +43,7 @@
},
{
"name": "sas9",
"serverUrl": "https://sas.analytium.co.uk:8343",
"serverUrl": "",
"serverType": "SAS9",
"httpsAgentOptions": {
"allowInsecureRequests": false
@@ -65,7 +65,7 @@
},
{
"name": "server",
"serverUrl": "https://sas.analytium.co.uk:5006",
"serverUrl": "",
"serverType": "SASJS",
"httpsAgentOptions": {
"allowInsecureRequests": false
@@ -78,7 +78,7 @@
},
{
"name": "docsonly",
"serverType": "SAS9",
"serverType": "SASJS",
"appLoc": "dummy",
"macroFolders": [
"meta",
@@ -86,6 +86,7 @@
"server",
"viya",
"tests/sas9only",
"tests/serveronly",
"tests/viyaonly"
]
},

View File

@@ -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;

View File

@@ -21,6 +21,7 @@
@li mf_getuniquefileref.sas
@li mf_getuniquename.sas
@li mp_abort.sas
@li ms_deletefile.sas
**/
@@ -29,6 +30,9 @@
,mdebug=0
);
/* 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();
@@ -40,8 +44,8 @@
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"';
@@ -64,17 +68,17 @@ data _null_;
file &fname1 lrecl=1000;
infile "&_sasjs_tokenfile" lrecl=1000;
input;
put "Content-Type: multipart/form-data; boundary=&boundary";
put "Authorization: Bearer " _infile_;
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;
infile &fname1 lrecl=32767;
input;
put _infile_;
run;

View File

@@ -87,9 +87,11 @@ data _null_;
file &fref1 lrecl=1000;
infile "&_sasjs_tokenfile" lrecl=1000;
input;
put "Authorization: Bearer " _infile_;
put "Content-Type: application/json";
put "accept: application/json";
if _n_=1 then do;
put "Content-Type: application/json";
put "accept: application/json";
end;
put _infile_;
run;
%if &mdebug=1 %then %do;

View 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;

View File

@@ -33,7 +33,7 @@ data _null_;
file &headref lrecl=1000;
infile "&_sasjs_tokenfile" lrecl=1000;
input;
put "Authorization: Bearer " _infile_;
put _infile_;
run;
proc http method='DELETE' headerin=&headref

View File

@@ -34,7 +34,7 @@ data _null_;
file &headref lrecl=1000;
infile "&_sasjs_tokenfile" lrecl=1000;
input;
put "Authorization: Bearer " _infile_;
put _infile_;
run;
proc http method='GET' out=&binaryfref headerin=&headref

View File

@@ -56,8 +56,8 @@ data _null_;
file &fref0 lrecl=1000;
infile "&_sasjs_tokenfile" lrecl=1000;
input;
put "Authorization: Bearer " _infile_;
put "accept: application/json";
if _n_=1 then put "accept: application/json";
put _infile_;
run;
%if &mdebug=1 %then %do;

View File

@@ -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 lrecl=1000;
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;
/* 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;
put 'Authorization: Bearer ' _infile_;
if _n_=1 then put "Content-Type: multipart/form-data; boundary=&boundary";
put _infile_;
run;
filename &outref temp;
%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
View 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;

View File

@@ -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
)
@@ -135,12 +141,12 @@
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()"" ";

View 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
)

View 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
)

View File

@@ -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
)

View File

@@ -41,7 +41,7 @@ run;
/* multibyte string check */
filename tmp2 temp;
filename tmp2 temp lrecl=500;
data _null_;
file tmp2;
put "'╤', '╔', '╗', '═', '╧', '╚', '╝', '║', '╟', '─', '┼', '║', '╢', '│'";

View 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
)

View 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)

View File

@@ -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;

View File

@@ -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()

View File

@@ -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),

View File

@@ -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;

View File

@@ -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_);
@@ -69,7 +69,7 @@ run;
%let str=%str(replace.string.with.dots );
%let rep=%str( more.dots);
data _null_;
file &test3;
file &test3 lrecl=500;
put 'blahblah';
put "blahblah&str.blah&str. replace &str.X";
put "blahbreplacewith&str.spacesahblah";
@@ -77,7 +77,7 @@ run;
%mp_replace(&test3, findvar=str, replacevar=rep)
data _null_;
infile &test3;
infile &test3 lrecl=500;
input;
if _n_=2 then call symputx('test3resulta',_infile_);
if _n_=3 then call symputx('test3resultb',_infile_);

View File

@@ -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
)

View File

@@ -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;

View 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)

View File

@@ -16,6 +16,7 @@ filename stpcode temp;
data _null_;
file stpcode;
put '%put hello world;';
put '%put _all_;';
run;
options mprint;

View 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
)

View File

@@ -3,23 +3,27 @@
@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()
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;

View File

@@ -9,7 +9,7 @@
%mp_assert(
iftrue=(&syscc=0),
desc=Checking final error condition,
desc=Checking final err condition,
outds=work.test_results
)

View File

@@ -247,7 +247,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; ';
@@ -458,6 +458,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 mv_webout(action,ds,fref=_mvwtemp,dslabel=,fmt=Y,stream=Y,missing=NULL ';
put ' ,showmeta=NO ';
put '); ';
@@ -632,25 +651,6 @@ data _null_;
put '%end; ';
put ' ';
put '%mend mv_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 ' /* 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; ';
/* WEBOUT END */
put '/* if calling viya service with _job param, _program will conflict */';
put '/* so it is provided by SASjs instead as __program */';