mirror of
https://github.com/sasjs/core.git
synced 2025-12-11 06:24:35 +00:00
Compare commits
24 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
86f7876f50 | ||
|
|
46c96bc7ec | ||
|
|
cba3f5972b | ||
|
|
ed48c49964 | ||
|
|
203ff3f80d | ||
|
|
cfe90a8d0d | ||
|
|
0749ea0819 | ||
|
|
e09a39e748 | ||
|
|
20dcefaefd | ||
|
|
4c8347516a | ||
|
|
e497d226a0 | ||
|
|
ccf8f1acc0 | ||
|
|
fe9a2ed979 | ||
|
|
078815e83e | ||
|
|
bb80c7af5a | ||
|
|
842662aae1 | ||
|
|
876fac2332 | ||
|
|
427deca350 | ||
|
|
07bde4b25c | ||
|
|
80b06af581 | ||
|
|
3c026811e9 | ||
|
|
cf547ce7e4 | ||
|
|
6952c79899 | ||
|
|
badf5b5761 |
@@ -117,6 +117,15 @@
|
||||
"contributions": [
|
||||
"bug"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "yabwon",
|
||||
"name": "Bart Jablonski",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/9314894?v=4",
|
||||
"profile": "https://github.com/yabwon",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
}
|
||||
],
|
||||
"contributorsPerLine": 7,
|
||||
|
||||
3
.github/FUNDING.yml
vendored
Normal file
3
.github/FUNDING.yml
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
# These are supported funding model platforms
|
||||
|
||||
github: [sasjs]
|
||||
@@ -47,7 +47,7 @@ Documentation: https://core.sasjs.io
|
||||
|
||||
This library will not be used for storing data entries (such as formats or datalines). Where this becomes necessary in the future, a new repo will be created, in order to keep the NPM bundle size down (for the benefit of those looking to embed purely macros in their applications).
|
||||
|
||||
#### FCMP library (All Platforms)
|
||||
### FCMP library (All Platforms)
|
||||
|
||||
- Function and macro names are identical, except for special cases
|
||||
- Prefixes: _mcf_
|
||||
@@ -204,6 +204,7 @@ When contributing to this library, it is therefore important to ensure that all
|
||||
|
||||
We are currently on major release v4. Breaking changes should be marked with the [deprecated](https://www.doxygen.nl/manual/commands.html#cmddeprecated) doxygen tag. The following changes are planned when the next major/breaking release (v5) becomes necessary:
|
||||
|
||||
* mf_getuniquelibref.sas to have the deprecated maxtried parameter removed (no longer needed)
|
||||
* mp_testservice.sas to be renamed as mp_execute.sas (as it doesn't actually test anything)
|
||||
* `insert_cmplib` option of mcf_xxx macros will be deprecated (the option is now checked automatically with value inserted only if needed)
|
||||
* mcf_xxx macros to have `wrap=` option defaulted to YES for convenience. Set this option explicitly to avoid issues.
|
||||
@@ -217,10 +218,9 @@ If you find this library useful, please leave a [star](https://github.com/sasjs/
|
||||

|
||||
|
||||
|
||||
|
||||
## 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)):
|
||||
|
||||
@@ -241,6 +241,7 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
|
||||
<td align="center"><a href="https://github.com/kkchandok"><img src="https://avatars.githubusercontent.com/u/46090627?v=4?s=100" width="100px;" alt=""/><br /><sub><b>kkchandok</b></sub></a><br /><a href="#ideas-kkchandok" title="Ideas, Planning, & Feedback">🤔</a></td>
|
||||
<td align="center"><a href="https://github.com/VladislavParhomchik"><img src="https://avatars.githubusercontent.com/u/83717836?v=4?s=100" width="100px;" alt=""/><br /><sub><b>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>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
|
||||
677
all.sas
677
all.sas
@@ -970,29 +970,46 @@ or %index(&pgm,/tests/testteardown)
|
||||
|
||||
> mclib3
|
||||
|
||||
@param prefix= first part of libref. Remember that librefs can only be 8 characters,
|
||||
so a 7 letter prefix would mean that maxtries should be 10.
|
||||
@param maxtries= the last part of the libref. Provide an integer value.
|
||||
A blank value is returned if no usable libname is determined.
|
||||
|
||||
@param [in] prefix= (mclib) first part of the returned libref. As librefs can
|
||||
be as long as 8 characters, a maximum length of 7 characters is premitted
|
||||
for this prefix.
|
||||
@param [in] maxtries= Deprecated parameter. Remains here to ensure a
|
||||
non-breaking change. Will be removed in v5.
|
||||
|
||||
@version 9.2
|
||||
@author Allan Bowe
|
||||
**/
|
||||
|
||||
|
||||
%macro mf_getuniquelibref(prefix=mclib,maxtries=1000);
|
||||
%local x libref;
|
||||
%let x=0;
|
||||
%do x=0 %to &maxtries;
|
||||
%if %sysfunc(libref(&prefix&x)) ne 0 %then %do;
|
||||
%let libref=&prefix&x;
|
||||
%let rc=%sysfunc(libname(&libref,%sysfunc(pathname(work))));
|
||||
%if &rc %then %put %sysfunc(sysmsg());
|
||||
&prefix&x
|
||||
%*put &sysmacroname: Libref &libref assigned as WORK and returned;
|
||||
%local x;
|
||||
|
||||
%if ( %length(&prefix) gt 7 ) %then %do;
|
||||
%put %str(ERR)OR: The prefix parameter cannot exceed 7 characters.;
|
||||
0
|
||||
%return;
|
||||
%end;
|
||||
%else %if (%sysfunc(NVALID(&prefix,v7))=0) %then %do;
|
||||
%put %str(ERR)OR: Invalid prefix (&prefix);
|
||||
0
|
||||
%return;
|
||||
%end;
|
||||
%put unable to find available libref in range &prefix.0-&maxtries;
|
||||
|
||||
/* Set maxtries equal to '10 to the power of [# unused characters] - 1' */
|
||||
%let maxtries=%eval(10**(8-%length(&prefix))-1);
|
||||
|
||||
%do x = 0 %to &maxtries;
|
||||
%if %sysfunc(libref(&prefix&x)) ne 0 %then %do;
|
||||
&prefix&x
|
||||
%return;
|
||||
%end;
|
||||
%let x = %eval(&x + 1);
|
||||
%end;
|
||||
|
||||
%put %str(ERR)OR: No usable libref in range &prefix.0-&maxtries;
|
||||
%put %str(ERR)OR- Try reducing the prefix or deleting some libraries!;
|
||||
0
|
||||
%mend mf_getuniquelibref;/**
|
||||
@file mf_getuniquename.sas
|
||||
@brief Returns a shortened (32 char) GUID as a valid SAS name
|
||||
@@ -3116,7 +3133,8 @@ run;
|
||||
outds=work.test_results
|
||||
)/*/STORE SOURCE*/;
|
||||
%local ds test_result test_comments del add mod ilist;
|
||||
%let ilist=%upcase(&sasjs_prefix._FUNCTIONS &ignorelist);
|
||||
%let ilist=%upcase(&sasjs_prefix._FUNCTIONS SYS_PROCHTTP_STATUS_CODE
|
||||
SYS_PROCHTTP_STATUS_CODE SYS_PROCHTTP_STATUS_PHRASE &ignorelist);
|
||||
|
||||
/**
|
||||
* this sets up the global vars, it will also enter STRICT mode. If this
|
||||
@@ -3131,7 +3149,7 @@ run;
|
||||
create table &scopeds as
|
||||
select name,offset,value
|
||||
from dictionary.macros
|
||||
where scope="&scope" and name not in (%mf_getquotedstr(&ilist))
|
||||
where scope="&scope" and upcase(name) not in (%mf_getquotedstr(&ilist))
|
||||
order by name,offset;
|
||||
%end;
|
||||
%else %if &action=COMPARE %then %do;
|
||||
@@ -3145,7 +3163,9 @@ run;
|
||||
|
||||
%let ds=&syslast;
|
||||
|
||||
proc compare base=&scopeds compare=&ds;
|
||||
proc compare
|
||||
base=&scopeds(where=(upcase(name) not in (%mf_getquotedstr(&ilist))))
|
||||
compare=&ds;
|
||||
run;
|
||||
|
||||
%if &sysinfo=0 %then %do;
|
||||
@@ -3183,7 +3203,8 @@ run;
|
||||
drop table &ds;
|
||||
%end;
|
||||
|
||||
%mend mp_assertscope;/**
|
||||
%mend mp_assertscope;
|
||||
/**
|
||||
@file
|
||||
@brief Convert a file to/from base64 format
|
||||
@details Creates a new version of a file either encoded or decoded using
|
||||
@@ -8872,7 +8893,8 @@ options ibufsize=&ibufsize;
|
||||
%mend mp_loadformat;/**
|
||||
@file
|
||||
@brief Mechanism for locking tables to prevent parallel modifications
|
||||
@details Uses a control table to enable ANY table to be locked for updates.
|
||||
@details Uses a control table to enable ANY table to be locked for updates
|
||||
(not just SAS datasets).
|
||||
Only useful if every update uses the macro! Used heavily within
|
||||
[Data Controller for SAS](https://datacontroller.io).
|
||||
|
||||
@@ -8886,7 +8908,7 @@ options ibufsize=&ibufsize;
|
||||
length is 200 characters.
|
||||
@param [out] ctl_ds= (0) The control table which controls the actual locking.
|
||||
Should already be assigned and available. The definition is available by
|
||||
running mp_coretable.sas as follows: `%mp_coretable(LOCKTABLE)`.
|
||||
running the mddl_dc_locktable.sas macro.
|
||||
|
||||
@param [in] loops= (25) Number of times to check for a lock.
|
||||
@param [in] loop_secs= (1) Seconds to wait between each lock attempt
|
||||
@@ -9594,6 +9616,155 @@ insert into &outds select distinct * from &append_ds;
|
||||
%end;
|
||||
|
||||
%mend mp_recursivejoin;
|
||||
/**
|
||||
@file
|
||||
@brief Performs a text substitution on a file
|
||||
@details Performs a find and replace on a file, either in place or to a new
|
||||
file. Can be used on files where lines are longer than 32767.
|
||||
|
||||
Works by reading in the file byte by byte, then marking the beginning and end
|
||||
of each matched string, before finally doing the replace.
|
||||
|
||||
Full credit for this highly efficient and syntactically satisfying SAS logic
|
||||
goes to [Bartosz Jabłoński](https://www.linkedin.com/in/yabwon), founder of
|
||||
the [SAS Packages](https://github.com/yabwon/SAS_PACKAGES) framework.
|
||||
|
||||
Usage:
|
||||
|
||||
%let file="%sysfunc(pathname(work))/file.txt";
|
||||
%let str=replace/me;
|
||||
%let rep=with/this;
|
||||
data _null_;
|
||||
file &file;
|
||||
put 'blahblah';
|
||||
put "blahblah&str.blah";
|
||||
put 'blahblahblah';
|
||||
run;
|
||||
%mp_replace(&file, findvar=str, replacevar=rep)
|
||||
data _null_;
|
||||
infile &file;
|
||||
input;
|
||||
list;
|
||||
run;
|
||||
|
||||
Note - if you are running a version of SAS that will allow the io package in
|
||||
LUA, you can also use this macro: mp_gsubfile.sas
|
||||
|
||||
@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
|
||||
avoid overwriting the first file).
|
||||
|
||||
<h4> SAS Macros </h4>
|
||||
@li mf_getuniquefileref.sas
|
||||
@li mf_getuniquename.sas
|
||||
|
||||
<h4> Related Macros </h4>
|
||||
@li mp_gsubfile.test.sas
|
||||
|
||||
@version 9.4
|
||||
@author Bartosz Jabłoński
|
||||
@author Allan Bowe
|
||||
**/
|
||||
|
||||
%macro mp_replace(infile,
|
||||
findvar=,
|
||||
replacevar=,
|
||||
outfile=0
|
||||
)/*/STORE SOURCE*/;
|
||||
|
||||
%local inref dttm ds1;
|
||||
%let inref=%mf_getuniquefileref();
|
||||
%let outref=%mf_getuniquefileref();
|
||||
%if &outfile=0 %then %let outfile=&infile;
|
||||
%let ds1=%mf_getuniquename(prefix=allchars);
|
||||
%let ds2=%mf_getuniquename(prefix=startmark);
|
||||
|
||||
/* START */
|
||||
%let dttm=%sysfunc(datetime());
|
||||
|
||||
filename &inref &infile lrecl=1 recfm=n;
|
||||
|
||||
data &ds1;
|
||||
infile &inref;
|
||||
input sourcechar $ 1. @@;
|
||||
format sourcechar hex2.;
|
||||
run;
|
||||
|
||||
data &ds2;
|
||||
/* set find string to length in bytes to cover trailing spaces */
|
||||
length string $ %length(%superq(&findvar));
|
||||
string =symget("&findvar");
|
||||
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;
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
stop;
|
||||
keep START STOP;
|
||||
run;
|
||||
|
||||
data &ds1;
|
||||
declare hash HS(dataset:"&ds2(keep=start)");
|
||||
HS.defineKey("start");
|
||||
HS.defineDone();
|
||||
declare hash HE(dataset:"&ds2(keep=stop)");
|
||||
HE.defineKey("stop");
|
||||
HE.defineDone();
|
||||
do until(eof);
|
||||
set &ds1 end=eof curobs =n;
|
||||
start = ^HS.check(key:n);
|
||||
stop = ^HE.check(key:n);
|
||||
length strt $ 1;
|
||||
strt =put(start,best. -L);
|
||||
retain out 1;
|
||||
if out then output;
|
||||
if start then out=0;
|
||||
if stop then out=1;
|
||||
end;
|
||||
stop;
|
||||
keep sourcechar strt;
|
||||
run;
|
||||
|
||||
filename &outref &outfile recfm=n;
|
||||
|
||||
data _null_;
|
||||
length replace $ %length(%superq(&replacevar));
|
||||
replace=symget("&replacevar");
|
||||
file &outref;
|
||||
do until(eof);
|
||||
set &ds1 end=eof;
|
||||
if strt ="1" then put replace char.;
|
||||
else put sourcechar char1.;
|
||||
end;
|
||||
stop;
|
||||
run;
|
||||
|
||||
/* END */
|
||||
%put &sysmacroname took %sysevalf(%sysfunc(datetime())-&dttm) seconds to run;
|
||||
|
||||
%mend mp_replace;
|
||||
/**
|
||||
@file
|
||||
@brief Reset when an err condition occurs
|
||||
@@ -11261,7 +11432,7 @@ create table &outds as
|
||||
|
||||
%mp_streamfile(contenttype=csv,inloc=/some/where.txt,outname=myfile.txt)
|
||||
|
||||
@param [in] contenttype= (TEXTS) Either TEXT, ZIP, CSV, EXCEL
|
||||
@param [in] contenttype= (TEXT) Either TEXT, ZIP, CSV, EXCEL
|
||||
@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.
|
||||
@@ -11271,6 +11442,7 @@ create table &outds as
|
||||
|
||||
<h4> SAS Macros </h4>
|
||||
@li mf_getplatform.sas
|
||||
@li mfs_httpheader.sas
|
||||
@li mp_binarycopy.sas
|
||||
|
||||
@author Allan Bowe
|
||||
@@ -11304,7 +11476,7 @@ data _null_;
|
||||
run;
|
||||
|
||||
%if &contentype=CSV %then %do;
|
||||
%if (&platform=SASMETA and &streamweb=1) or &platform=SASJS %then %do;
|
||||
%if (&platform=SASMETA and &streamweb=1) %then %do;
|
||||
data _null_;
|
||||
rc=stpsrv_header('Content-type','application/csv');
|
||||
rc=stpsrv_header('Content-disposition',"attachment; filename=&outname");
|
||||
@@ -11315,10 +11487,14 @@ run;
|
||||
contenttype='application/csv'
|
||||
contentdisp="attachment; filename=&outname";
|
||||
%end;
|
||||
%else %if &platform=SASJS %then %do;
|
||||
%mfs_httpheader(Content-type,application/csv)
|
||||
%mfs_httpheader(Content-disposition,%str(attachment; filename=&outname))
|
||||
%end;
|
||||
%end;
|
||||
%else %if &contentype=EXCEL %then %do;
|
||||
/* suitable for XLS format */
|
||||
%if (&platform=SASMETA and &streamweb=1) or &platform=SASJS %then %do;
|
||||
%if (&platform=SASMETA and &streamweb=1) %then %do;
|
||||
data _null_;
|
||||
rc=stpsrv_header('Content-type','application/vnd.ms-excel');
|
||||
rc=stpsrv_header('Content-disposition',"attachment; filename=&outname");
|
||||
@@ -11329,9 +11505,13 @@ run;
|
||||
contenttype='application/vnd.ms-excel'
|
||||
contentdisp="attachment; filename=&outname";
|
||||
%end;
|
||||
%else %if &platform=SASJS %then %do;
|
||||
%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) or &platform=SASJS %then %do;
|
||||
%if (&platform=SASMETA and &streamweb=1) %then %do;
|
||||
data _null_;
|
||||
rc=stpsrv_header('Content-type',"image/%lowcase(&contenttype)");
|
||||
run;
|
||||
@@ -11340,15 +11520,21 @@ run;
|
||||
filename &outref filesrvc parenturi="&SYS_JES_JOB_URI"
|
||||
contenttype="image/%lowcase(&contenttype)";
|
||||
%end;
|
||||
%else %if &platform=SASJS %then %do;
|
||||
%mfs_httpheader(Content-type,image/%lowcase(&contenttype))
|
||||
%end;
|
||||
%end;
|
||||
%else %if &contentype=HTML %then %do;
|
||||
%if &platform=SASVIYA %then %do;
|
||||
filename &outref filesrvc parenturi="&SYS_JES_JOB_URI" name="_webout.json"
|
||||
contenttype="text/html";
|
||||
%end;
|
||||
%else %if &platform=SASJS %then %do;
|
||||
%mfs_httpheader(Content-type,text/html)
|
||||
%end;
|
||||
%end;
|
||||
%else %if &contentype=TEXT %then %do;
|
||||
%if (&platform=SASMETA and &streamweb=1) or &platform=SASJS %then %do;
|
||||
%if (&platform=SASMETA and &streamweb=1) %then %do;
|
||||
data _null_;
|
||||
rc=stpsrv_header('Content-type','application/text');
|
||||
rc=stpsrv_header('Content-disposition',"attachment; filename=&outname");
|
||||
@@ -11359,9 +11545,13 @@ run;
|
||||
contenttype='application/text'
|
||||
contentdisp="attachment; filename=&outname";
|
||||
%end;
|
||||
%else %if &platform=SASJS %then %do;
|
||||
%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) or &platform=SASJS %then %do;
|
||||
%if (&platform=SASMETA and &streamweb=1) %then %do;
|
||||
data _null_;
|
||||
rc=stpsrv_header('Content-type',"font/%lowcase(&contenttype)");
|
||||
run;
|
||||
@@ -11370,9 +11560,12 @@ run;
|
||||
filename &outref filesrvc parenturi="&SYS_JES_JOB_URI"
|
||||
contenttype="font/%lowcase(&contenttype)";
|
||||
%end;
|
||||
%else %if &platform=SASJS %then %do;
|
||||
%mfs_httpheader(Content-type,font/%lowcase(&contenttype))
|
||||
%end;
|
||||
%end;
|
||||
%else %if &contentype=XLSX %then %do;
|
||||
%if (&platform=SASMETA and &streamweb=1) or &platform=SASJS %then %do;
|
||||
%if (&platform=SASMETA and &streamweb=1) %then %do;
|
||||
data _null_;
|
||||
rc=stpsrv_header('Content-type',
|
||||
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
|
||||
@@ -11385,9 +11578,15 @@ run;
|
||||
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
|
||||
contentdisp="attachment; filename=&outname";
|
||||
%end;
|
||||
%else %if &platform=SASJS %then %do;
|
||||
%mfs_httpheader(Content-type
|
||||
,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
|
||||
)
|
||||
%mfs_httpheader(Content-disposition,%str(attachment; filename=&outname))
|
||||
%end;
|
||||
%end;
|
||||
%else %if &contentype=ZIP %then %do;
|
||||
%if (&platform=SASMETA and &streamweb=1) or &platform=SASJS %then %do;
|
||||
%if (&platform=SASMETA and &streamweb=1) %then %do;
|
||||
data _null_;
|
||||
rc=stpsrv_header('Content-type','application/zip');
|
||||
rc=stpsrv_header('Content-disposition',"attachment; filename=&outname");
|
||||
@@ -11398,6 +11597,10 @@ run;
|
||||
contenttype='application/zip'
|
||||
contentdisp="attachment; filename=&outname";
|
||||
%end;
|
||||
%else %if &platform=SASJS %then %do;
|
||||
%mfs_httpheader(Content-type,application/zip)
|
||||
%mfs_httpheader(Content-disposition,%str(attachment; filename=&outname))
|
||||
%end;
|
||||
%end;
|
||||
%else %do;
|
||||
%put %str(ERR)OR: Content Type &contenttype NOT SUPPORTED by &sysmacroname!;
|
||||
@@ -13134,6 +13337,12 @@ run;
|
||||
/* now try and assign it */
|
||||
if libname("&libref",,'meta',cats('liburi="',liburi,'";')) ne 0 then do;
|
||||
putlog "&libref could not be assigned";
|
||||
putlog liburi=;
|
||||
/**
|
||||
* Fetch the system message for display in the abort modal. This is
|
||||
* 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');
|
||||
if "&mabort"='HARD' then call symputx('mp_abort',1,'l');
|
||||
end;
|
||||
@@ -13155,7 +13364,7 @@ run;
|
||||
|
||||
%if &mp_abort=1 %then %do;
|
||||
%mp_abort(iftrue= (&mp_abort=1)
|
||||
,mac=&sysmacroname
|
||||
,mac=mm_assignlib.sas
|
||||
,msg=&msg
|
||||
)
|
||||
%return;
|
||||
@@ -15177,17 +15386,22 @@ run;
|
||||
%mend mm_deletestp;
|
||||
/**
|
||||
@file mm_getauthinfo.sas
|
||||
@brief extracts authentication info
|
||||
@details usage:
|
||||
@brief Extracts authentication info for each user in metadata
|
||||
@details
|
||||
Usage:
|
||||
|
||||
%mm_getauthinfo(outds=auths)
|
||||
%mm_getauthinfo(outds=auths)
|
||||
|
||||
@param outds= the ONE LEVEL work dataset to create
|
||||
|
||||
@param [in] mdebug= (0) Set to 1 to enable DEBUG messages and preserve outputs
|
||||
@param [out] outds= (mm_getauthinfo) The output dataset to create
|
||||
|
||||
<h4> SAS Macros </h4>
|
||||
@li mm_getobjects.sas
|
||||
@li mf_getuniquefileref.sas
|
||||
@li mf_getuniquename.sas
|
||||
@li mm_getdetails.sas
|
||||
@li mm_getobjects.sas
|
||||
|
||||
|
||||
@version 9.4
|
||||
@author Allan Bowe
|
||||
@@ -15195,67 +15409,69 @@ run;
|
||||
**/
|
||||
|
||||
%macro mm_getauthinfo(outds=mm_getauthinfo
|
||||
,mdebug=0
|
||||
)/*/STORE SOURCE*/;
|
||||
%local prefix fileref;
|
||||
%let prefix=%substr(%mf_getuniquename(),1,25);
|
||||
|
||||
%if %length(&outds)>30 %then %do;
|
||||
%put %str(ERR)OR: Temp tables are created with the &outds prefix, which
|
||||
therefore needs to be 30 characters or less;
|
||||
%return;
|
||||
%end;
|
||||
%if %index(&outds,'.')>0 %then %do;
|
||||
%put %str(ERR)OR: Table &outds should be ONE LEVEL (no library);
|
||||
%return;
|
||||
%end;
|
||||
|
||||
%mm_getobjects(type=Login,outds=&outds.0)
|
||||
%mm_getobjects(type=Login,outds=&prefix.0)
|
||||
|
||||
%local fileref;
|
||||
%let fileref=%mf_getuniquefileref();
|
||||
|
||||
data _null_;
|
||||
file &fileref;
|
||||
set &outds.0 end=last;
|
||||
set &prefix.0 end=last;
|
||||
/* run macro */
|
||||
str=cats('%mm_getdetails(uri=',id,",outattrs=&outds.d",_n_
|
||||
,",outassocs=&outds.a",_n_,")");
|
||||
str=cats('%mm_getdetails(uri=',id,",outattrs=&prefix.d",_n_
|
||||
,",outassocs=&prefix.a",_n_,")");
|
||||
put str;
|
||||
/* transpose attributes */
|
||||
str=cats("proc transpose data=&outds.d",_n_,"(drop=type) out=&outds.da"
|
||||
str=cats("proc transpose data=&prefix.d",_n_,"(drop=type) out=&prefix.da"
|
||||
,_n_,"(drop=_name_);var value;id name;run;");
|
||||
put str;
|
||||
/* add extra info to attributes */
|
||||
str=cats("data &outds.da",_n_,";length login_id login_name $256; login_id="
|
||||
,quote(trim(id)),";set &outds.da",_n_
|
||||
str=cats("data &prefix.da",_n_,";length login_id login_name $256; login_id="
|
||||
,quote(trim(id)),";set &prefix.da",_n_
|
||||
,";login_name=trim(subpad(name,1,256));drop name;run;");
|
||||
put str;
|
||||
/* add extra info to associations */
|
||||
str=cats("data &outds.a",_n_,";length login_id login_name $256; login_id="
|
||||
str=cats("data &prefix.a",_n_,";length login_id login_name $256; login_id="
|
||||
,quote(trim(id)),";login_name=",quote(trim(name))
|
||||
,";set &outds.a",_n_,";run;");
|
||||
,";set &prefix.a",_n_,";run;");
|
||||
put str;
|
||||
if last then do;
|
||||
/* collate attributes */
|
||||
str=cats("data &outds._logat; set &outds.da1-&outds.da",_n_,";run;");
|
||||
str=cats("data &prefix._logat; set &prefix.da1-&prefix.da",_n_,";run;");
|
||||
put str;
|
||||
/* collate associations */
|
||||
str=cats("data &outds._logas; set &outds.a1-&outds.a",_n_,";run;");
|
||||
str=cats("data &prefix._logas; set &prefix.a1-&prefix.a",_n_,";run;");
|
||||
put str;
|
||||
/* tidy up */
|
||||
str=cats("proc delete data=&outds.da1-&outds.da",_n_,";run;");
|
||||
str=cats("proc delete data=&prefix.da1-&prefix.da",_n_,";run;");
|
||||
put str;
|
||||
str=cats("proc delete data=&outds.d1-&outds.d",_n_,";run;");
|
||||
str=cats("proc delete data=&prefix.d1-&prefix.d",_n_,";run;");
|
||||
put str;
|
||||
str=cats("proc delete data=&outds.a1-&outds.a",_n_,";run;");
|
||||
str=cats("proc delete data=&prefix.a1-&prefix.a",_n_,";run;");
|
||||
put str;
|
||||
end;
|
||||
run;
|
||||
|
||||
%if &mdebug=1 %then %do;
|
||||
data _null_;
|
||||
infile &fileref;
|
||||
if _n_=1 then putlog // "Now executing the following code:" //;
|
||||
input; putlog _infile_;
|
||||
run;
|
||||
%end;
|
||||
%inc &fileref;
|
||||
filename &fileref clear;
|
||||
|
||||
/* get libraries */
|
||||
proc sort data=&outds._logas(where=(assoc='Libraries')) out=&outds._temp;
|
||||
proc sort data=&prefix._logas(where=(assoc='Libraries')) out=&prefix._temp;
|
||||
by login_id;
|
||||
data &outds._temp;
|
||||
set &outds._temp;
|
||||
data &prefix._temp;
|
||||
set &prefix._temp;
|
||||
by login_id;
|
||||
length library_list $32767;
|
||||
retain library_list;
|
||||
@@ -15263,32 +15479,28 @@ data &outds._temp;
|
||||
else library_list=catx(' !! ',library_list,name);
|
||||
proc sql;
|
||||
/* get auth domain */
|
||||
create table &outds._dom as
|
||||
create table &prefix._dom as
|
||||
select login_id,name as domain
|
||||
from &outds._logas
|
||||
from &prefix._logas
|
||||
where assoc='Domain';
|
||||
create unique index login_id on &outds._dom(login_id);
|
||||
create unique index login_id on &prefix._dom(login_id);
|
||||
/* join it all together */
|
||||
create table &outds._logins as
|
||||
create table &outds as
|
||||
select a.*
|
||||
,c.domain
|
||||
,b.library_list
|
||||
from &outds._logat (drop=ishidden lockedby usageversion publictype) a
|
||||
left join &outds._temp b
|
||||
from &prefix._logat (drop=ishidden lockedby usageversion publictype) a
|
||||
left join &prefix._temp b
|
||||
on a.login_id=b.login_id
|
||||
left join &outds._dom c
|
||||
left join &prefix._dom c
|
||||
on a.login_id=c.login_id;
|
||||
drop table &outds._temp;
|
||||
drop table &outds._logat;
|
||||
drop table &outds._logas;
|
||||
|
||||
data _null_;
|
||||
infile &fileref;
|
||||
if _n_=1 then putlog // "Now executing the following code:" //;
|
||||
input; putlog _infile_;
|
||||
run;
|
||||
%if &mdebug=0 %then %do;
|
||||
proc datasets lib=work;
|
||||
delete &prefix:;
|
||||
run;
|
||||
%end;
|
||||
|
||||
filename &fileref clear;
|
||||
|
||||
%mend mm_getauthinfo;/**
|
||||
@file
|
||||
@@ -18554,6 +18766,203 @@ run;
|
||||
|
||||
%mend mfs_httpheader;
|
||||
/**
|
||||
@file
|
||||
@brief Creates a file on SASjs Drive
|
||||
@details Creates a file on SASjs Drive. To use the file as a Stored Program,
|
||||
it must have a ".sas" extension.
|
||||
|
||||
Example:
|
||||
|
||||
filename stpcode temp;
|
||||
data _null_;
|
||||
file stpcode;
|
||||
put '%put hello world;';
|
||||
run;
|
||||
%ms_createfile(/some/stored/program.sas, inref=stpcode)
|
||||
|
||||
@param [in] driveloc The full path to the file in SASjs Drive
|
||||
@param [in] inref= (0) The fileref containing the file to create.
|
||||
@param [in] mdebug= (0) Set to 1 to enable DEBUG messages
|
||||
|
||||
<h4> SAS Macros </h4>
|
||||
@li mf_getuniquefileref.sas
|
||||
@li mf_getuniquename.sas
|
||||
@li mp_abort.sas
|
||||
|
||||
**/
|
||||
|
||||
%macro ms_createfile(driveloc
|
||||
,inref=0
|
||||
,mdebug=0
|
||||
);
|
||||
|
||||
%local fname0 fname1 boundary fname statcd msg;
|
||||
%let fname0=%mf_getuniquefileref();
|
||||
%let fname1=%mf_getuniquefileref();
|
||||
%let boundary=%mf_getuniquename();
|
||||
|
||||
data _null_;
|
||||
file &fname0 termstr=crlf;
|
||||
infile &inref end=eof;
|
||||
if _n_ = 1 then do;
|
||||
put "--&boundary.";
|
||||
put 'Content-Disposition: form-data; name="filePath"';
|
||||
put ;
|
||||
put "&driveloc";
|
||||
put "--&boundary";
|
||||
put 'Content-Disposition: form-data; name="file"; filename="ignore.sas"';
|
||||
put "Content-Type: text/plain";
|
||||
put ;
|
||||
end;
|
||||
input;
|
||||
put _infile_; /* add the actual file to be sent */
|
||||
if eof then do;
|
||||
put ;
|
||||
put "--&boundary--";
|
||||
end;
|
||||
run;
|
||||
|
||||
%if &mdebug=1 %then %do;
|
||||
data _null_;
|
||||
infile &fname0;
|
||||
input;
|
||||
put _infile_;
|
||||
run;
|
||||
%end;
|
||||
|
||||
proc http method='POST' in=&fname0 out=&fname1
|
||||
url="&_sasjs_apiserverurl/SASjsApi/drive/file";
|
||||
headers "Content-Type"="multipart/form-data; boundary=&boundary";
|
||||
%if &mdebug=1 %then %do;
|
||||
debug level=1;
|
||||
%end;
|
||||
run;
|
||||
|
||||
%let statcd=0;
|
||||
data _null_;
|
||||
infile &fname1;
|
||||
input;
|
||||
putlog _infile_;
|
||||
if _infile_='{"status":"success"}' then call symputx('statcd',1,'l');
|
||||
else call symputx('msg',_infile_,'l');
|
||||
run;
|
||||
|
||||
%mp_abort(
|
||||
iftrue=(&statcd=0)
|
||||
,mac=ms_createfile.sas
|
||||
,msg=%superq(msg)
|
||||
)
|
||||
|
||||
%mend ms_createfile;
|
||||
/**
|
||||
@file
|
||||
@brief Gets a file from SASjs Drive
|
||||
@details Fetches a file on SASjs Drive and stores it in the output fileref.
|
||||
|
||||
Example:
|
||||
|
||||
%ms_getfile(/some/stored/file.ext, 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
|
||||
|
||||
|
||||
**/
|
||||
|
||||
%macro ms_getfile(driveloc
|
||||
,outref=msgetfil
|
||||
,mdebug=0
|
||||
);
|
||||
|
||||
filename &outref temp;
|
||||
|
||||
proc http method='GET' out=&outref
|
||||
url="&_sasjs_apiserverurl/SASjsApi/drive/file?filePath=&driveloc";
|
||||
%if &mdebug=1 %then %do;
|
||||
debug level=2;
|
||||
%end;
|
||||
run;
|
||||
|
||||
|
||||
%mend ms_getfile;
|
||||
/**
|
||||
@file
|
||||
@brief Executes a SASjs Server Stored Program
|
||||
@details Runs a Stored Program (using POST method) and extracts the webout and
|
||||
log from the response JSON.
|
||||
|
||||
Example:
|
||||
|
||||
%ms_runstp(/some/stored/program
|
||||
,debug=131
|
||||
,outref=weboot
|
||||
)
|
||||
|
||||
@param [in] pgm The full path to the Stored Program in SASjs Drive (_program
|
||||
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 [out] outref= (outweb) The output fileref to contain the response JSON
|
||||
(will be created using temp engine)
|
||||
|
||||
<h4> SAS Macros </h4>
|
||||
@li mf_getuniquefileref.sas
|
||||
@li mp_abort.sas
|
||||
|
||||
**/
|
||||
|
||||
%macro ms_runstp(pgm
|
||||
,debug=131
|
||||
,outref=outweb
|
||||
,mdebug=0
|
||||
);
|
||||
%local dbg fname1;
|
||||
%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)
|
||||
)
|
||||
|
||||
data _null_;
|
||||
file &fname1;
|
||||
infile "&_sasjs_tokenfile";
|
||||
input;
|
||||
put 'Authorization: Bearer' _infile_;
|
||||
run;
|
||||
|
||||
filename &outref temp;
|
||||
|
||||
/* prepare request*/
|
||||
proc http method='POST' headerin=&fname1 out=&outref
|
||||
url="&_sasjs_apiserverurl.&_sasjs_apipath?_program=&pgm%str(&)_debug=131";
|
||||
run;
|
||||
%if (&SYS_PROCHTTP_STATUS_CODE ne 200 and &SYS_PROCHTTP_STATUS_CODE ne 201)
|
||||
or &mdebug=1 %then %do;
|
||||
data _null_;infile &outref;input;putlog _infile_;run;
|
||||
%end;
|
||||
%mp_abort(
|
||||
iftrue=(&SYS_PROCHTTP_STATUS_CODE ne 200 and &SYS_PROCHTTP_STATUS_CODE ne 201)
|
||||
,mac=&sysmacroname
|
||||
,msg=%str(&SYS_PROCHTTP_STATUS_CODE &SYS_PROCHTTP_STATUS_PHRASE)
|
||||
)
|
||||
|
||||
|
||||
%if &mdebug=1 %then %do;
|
||||
%put &sysmacroname exit vars:;
|
||||
%put _local_;
|
||||
%end;
|
||||
%else %do;
|
||||
/* clear refs */
|
||||
filename &fname1 clear;
|
||||
%end;
|
||||
%mend ms_runstp;/**
|
||||
@file
|
||||
@brief Send data to/from @sasjs/server
|
||||
@details This macro should be added to the start of each web service,
|
||||
@@ -23101,10 +23510,10 @@ run;
|
||||
%mend mv_jobwaitfor;/**
|
||||
@file mv_registerclient.sas
|
||||
@brief Register Client and Secret (admin task)
|
||||
@details When building apps on SAS Viya, an client id and secret are sometimes
|
||||
required. In order to generate them, filesystem access to the Consul Token
|
||||
is needed (it is not enough to be in the SASAdministrator group in SAS
|
||||
Environment Manager).
|
||||
@details When building apps on SAS Viya, a client id and secret are usually
|
||||
required. In order to generate them, the Consul Token is required. To access
|
||||
this token, you need to be a system administrator (it is not enough to be in
|
||||
the SASAdministrator group in SAS Environment Manager).
|
||||
|
||||
If you are registering a lot of clients / secrets, you may find it more
|
||||
convenient to use the [Viya Token Generator]
|
||||
@@ -23125,62 +23534,69 @@ run;
|
||||
"https://raw.githubusercontent.com/sasjs/core/main/all.sas";
|
||||
%inc mc;
|
||||
|
||||
%* generate random client using consul token as input parameter;
|
||||
%mv_registerclient(consul_token=12x34sa43v2345n234lasd)
|
||||
|
||||
%* generate random client details with all scopes;
|
||||
%mv_registerclient(scopes=openid *)
|
||||
|
||||
%* specific client with just openid scope;
|
||||
%mv_registerclient(client_id=YourClient
|
||||
,client_secret=YourSecret
|
||||
,scopes=openid
|
||||
)
|
||||
|
||||
%* generate random client details with all scopes;
|
||||
%mv_registerclient(scopes=openid *)
|
||||
|
||||
%* generate random client with 90/180 second access/refresh token expiry;
|
||||
%mv_registerclient(scopes=openid *
|
||||
,access_token_validity=90
|
||||
,refresh_token_validity=180
|
||||
)
|
||||
|
||||
@param client_id= The client name. Auto generated if blank.
|
||||
@param client_secret= Client secret. Auto generated if client is blank.
|
||||
@param scopes=(openid) List of space-seperated unquoted scopes
|
||||
@param grant_type=(authorization_code|refresh_token) Valid values are
|
||||
"password" or "authorization_code" (unquoted)
|
||||
@param outds=(mv_registerclient) The dataset to contain the registered client
|
||||
id and secret
|
||||
@param access_token_validity=(DEFAULT) The duration of validity of the access
|
||||
token in seconds. A value of DEFAULT will omit the entry (and use system
|
||||
default)
|
||||
@param refresh_token_validity=(DEFAULT) The duration of validity of the
|
||||
@param [in,out] client_id= The client name. Auto generated if blank.
|
||||
@param [in,out] client_secret= Client secret. Auto generated if client is
|
||||
blank.
|
||||
@param [in] consul_token= (0) Provide the actual consul token value here if
|
||||
using Viya 4 or above.
|
||||
@param [in] scopes= (openid) List of space-seperated unquoted scopes
|
||||
@param [in] grant_type= (authorization_code|refresh_token) Valid values are
|
||||
"password" or "authorization_code" (unquoted). Pipe seperated.
|
||||
@param [out] outds=(mv_registerclient) The dataset to contain the registered
|
||||
client id and secret
|
||||
@param [in] access_token_validity= (DEFAULT) The access token duration in
|
||||
seconds. A value of DEFAULT will omit the entry (and use system default)
|
||||
@param [in] refresh_token_validity= (DEFAULT) The duration of validity of the
|
||||
refresh token in seconds. A value of DEFAULT will omit the entry (and use
|
||||
system default)
|
||||
@param name= An optional, human readable name for the client
|
||||
@param required_user_groups= A list of group names. If a user does not belong
|
||||
to all the required groups, the user will not be authenticated and no tokens
|
||||
are issued to this client for that user. If this field is not specified,
|
||||
authentication and token issuance proceeds normally.
|
||||
@param autoapprove= During the auth step the user can choose which scope to
|
||||
apply. Setting this to true will autoapprove all the client scopes.
|
||||
@param use_session= If true, access tokens issued to this client will be
|
||||
@param [in] client_name= (DEFAULT) An optional, human readable name for the
|
||||
client.
|
||||
@param [in] required_user_groups= A list of group names. If a user does not
|
||||
belong to all the required groups, the user will not be authenticated and no
|
||||
tokens are issued to this client for that user. If this field is not
|
||||
specified, authentication and token issuance proceeds normally.
|
||||
@param [in] autoapprove= During the auth step the user can choose which scope
|
||||
to apply. Setting this to true will autoapprove all the client scopes.
|
||||
@param [in] use_session= If true, access tokens issued to this client will be
|
||||
associated with an HTTP session and revoked upon logout or time-out.
|
||||
@param outjson= (_null_) A dataset containing the lines of JSON submitted.
|
||||
Useful for debugging.
|
||||
@param [out] outjson= (_null_) A dataset containing the lines of JSON
|
||||
submitted. Useful for debugging.
|
||||
|
||||
@version VIYA V.03.04
|
||||
@author Allan Bowe, source: https://github.com/sasjs/core
|
||||
|
||||
<h4> SAS Macros </h4>
|
||||
@li mp_abort.sas
|
||||
@li mf_getplatform.sas
|
||||
@li mf_getuniquefileref.sas
|
||||
@li mf_getuniquelibref.sas
|
||||
@li mf_loc.sas
|
||||
@li mf_getquotedstr.sas
|
||||
@li mf_getuser.sas
|
||||
@li mp_abort.sas
|
||||
|
||||
**/
|
||||
|
||||
%macro mv_registerclient(client_id=
|
||||
,client_secret=
|
||||
,consul_token=0
|
||||
,client_name=DEFAULT
|
||||
,scopes=openid
|
||||
,grant_type=authorization_code|refresh_token
|
||||
@@ -23192,33 +23608,41 @@ run;
|
||||
,refresh_token_validity=DEFAULT
|
||||
,outjson=_null_
|
||||
);
|
||||
%local consul_token fname1 fname2 fname3 libref access_token url tokloc;
|
||||
%local fname1 fname2 fname3 libref access_token url tokloc msg;
|
||||
|
||||
%if client_name=DEFAULT %then %let client_name=
|
||||
Generated by %mf_getuser() on %sysfunc(datetime(),datetime19.) using SASjs;
|
||||
Generated by %mf_getuser() (&sysuserid) on %sysfunc(datetime(),datetime19.
|
||||
) using SASjs;
|
||||
|
||||
options noquotelenmax;
|
||||
/* first, get consul token needed to get client id / secret */
|
||||
%let tokloc=/etc/SASSecurityCertificateFramework/tokens/consul/default;
|
||||
%let tokloc=%mf_loc(VIYACONFIG)&tokloc/client.token;
|
||||
|
||||
%if "&consul_token"="0" %then %do;
|
||||
/* first, get consul token needed to get client id / secret */
|
||||
%let tokloc=/etc/SASSecurityCertificateFramework/tokens/consul/default;
|
||||
%let tokloc=%mf_loc(VIYACONFIG)&tokloc/client.token;
|
||||
|
||||
%mp_abort(iftrue=(%sysfunc(fileexist(&tokloc))=0)
|
||||
,mac=&sysmacroname
|
||||
,msg=%str(Unable to access the consul token at &tokloc)
|
||||
)
|
||||
%if %sysfunc(fileexist(&tokloc))=0 %then %do;
|
||||
%let msg=Unable to access the consul token at &tokloc;
|
||||
%put &sysmacroname: &msg;
|
||||
%put Try passing the value in the consul= macro parameter;
|
||||
%put See docs: https://core.sasjs.io/mv__registerclient_8sas.html;
|
||||
%mp_abort(mac=mv_registerclient,msg=%str(&msg))
|
||||
%end;
|
||||
|
||||
%let consul_token=0;
|
||||
data _null_;
|
||||
infile "&tokloc";
|
||||
input token:$64.;
|
||||
call symputx('consul_token',token);
|
||||
run;
|
||||
data _null_;
|
||||
infile "&tokloc";
|
||||
input token:$64.;
|
||||
call symputx('consul_token',token);
|
||||
run;
|
||||
|
||||
%mp_abort(iftrue=("&consul_token"="0")
|
||||
,mac=&sysmacroname
|
||||
,msg=%str(Unable to source the consul token from &tokloc)
|
||||
)
|
||||
%if "&consul_token"="0" %then %do;
|
||||
%put &sysmacroname: Unable to source the consul token from &tokloc;
|
||||
%put It seems your account (&sysuserid) does not have admin rights;
|
||||
%put Please speak with your platform adminstrator;
|
||||
%put Docs: https://core.sasjs.io/mv__registerclient_8sas.html;
|
||||
%abort;
|
||||
%end;
|
||||
%end;
|
||||
|
||||
%local base_uri; /* location of rest apis */
|
||||
%let base_uri=%mf_getplatform(VIYARESTAPI);
|
||||
@@ -23231,6 +23655,9 @@ proc http method='POST' out=&fname1
|
||||
headers "X-Consul-Token"="&consul_token";
|
||||
run;
|
||||
|
||||
%put &=SYS_PROCHTTP_STATUS_CODE;
|
||||
%put &=SYS_PROCHTTP_STATUS_PHRASE;
|
||||
|
||||
%let libref=%mf_getuniquelibref();
|
||||
libname &libref JSON fileref=&fname1;
|
||||
|
||||
|
||||
@@ -14,27 +14,44 @@
|
||||
|
||||
> mclib3
|
||||
|
||||
@param prefix= first part of libref. Remember that librefs can only be 8 characters,
|
||||
so a 7 letter prefix would mean that maxtries should be 10.
|
||||
@param maxtries= the last part of the libref. Provide an integer value.
|
||||
A blank value is returned if no usable libname is determined.
|
||||
|
||||
@param [in] prefix= (mclib) first part of the returned libref. As librefs can
|
||||
be as long as 8 characters, a maximum length of 7 characters is premitted
|
||||
for this prefix.
|
||||
@param [in] maxtries= Deprecated parameter. Remains here to ensure a
|
||||
non-breaking change. Will be removed in v5.
|
||||
|
||||
@version 9.2
|
||||
@author Allan Bowe
|
||||
**/
|
||||
|
||||
|
||||
%macro mf_getuniquelibref(prefix=mclib,maxtries=1000);
|
||||
%local x libref;
|
||||
%let x=0;
|
||||
%do x=0 %to &maxtries;
|
||||
%if %sysfunc(libref(&prefix&x)) ne 0 %then %do;
|
||||
%let libref=&prefix&x;
|
||||
%let rc=%sysfunc(libname(&libref,%sysfunc(pathname(work))));
|
||||
%if &rc %then %put %sysfunc(sysmsg());
|
||||
&prefix&x
|
||||
%*put &sysmacroname: Libref &libref assigned as WORK and returned;
|
||||
%local x;
|
||||
|
||||
%if ( %length(&prefix) gt 7 ) %then %do;
|
||||
%put %str(ERR)OR: The prefix parameter cannot exceed 7 characters.;
|
||||
0
|
||||
%return;
|
||||
%end;
|
||||
%else %if (%sysfunc(NVALID(&prefix,v7))=0) %then %do;
|
||||
%put %str(ERR)OR: Invalid prefix (&prefix);
|
||||
0
|
||||
%return;
|
||||
%end;
|
||||
%put unable to find available libref in range &prefix.0-&maxtries;
|
||||
|
||||
/* Set maxtries equal to '10 to the power of [# unused characters] - 1' */
|
||||
%let maxtries=%eval(10**(8-%length(&prefix))-1);
|
||||
|
||||
%do x = 0 %to &maxtries;
|
||||
%if %sysfunc(libref(&prefix&x)) ne 0 %then %do;
|
||||
&prefix&x
|
||||
%return;
|
||||
%end;
|
||||
%let x = %eval(&x + 1);
|
||||
%end;
|
||||
|
||||
%put %str(ERR)OR: No usable libref in range &prefix.0-&maxtries;
|
||||
%put %str(ERR)OR- Try reducing the prefix or deleting some libraries!;
|
||||
0
|
||||
%mend mf_getuniquelibref;
|
||||
@@ -74,7 +74,8 @@
|
||||
outds=work.test_results
|
||||
)/*/STORE SOURCE*/;
|
||||
%local ds test_result test_comments del add mod ilist;
|
||||
%let ilist=%upcase(&sasjs_prefix._FUNCTIONS &ignorelist);
|
||||
%let ilist=%upcase(&sasjs_prefix._FUNCTIONS SYS_PROCHTTP_STATUS_CODE
|
||||
SYS_PROCHTTP_STATUS_CODE SYS_PROCHTTP_STATUS_PHRASE &ignorelist);
|
||||
|
||||
/**
|
||||
* this sets up the global vars, it will also enter STRICT mode. If this
|
||||
@@ -89,7 +90,7 @@
|
||||
create table &scopeds as
|
||||
select name,offset,value
|
||||
from dictionary.macros
|
||||
where scope="&scope" and name not in (%mf_getquotedstr(&ilist))
|
||||
where scope="&scope" and upcase(name) not in (%mf_getquotedstr(&ilist))
|
||||
order by name,offset;
|
||||
%end;
|
||||
%else %if &action=COMPARE %then %do;
|
||||
@@ -103,7 +104,9 @@
|
||||
|
||||
%let ds=&syslast;
|
||||
|
||||
proc compare base=&scopeds compare=&ds;
|
||||
proc compare
|
||||
base=&scopeds(where=(upcase(name) not in (%mf_getquotedstr(&ilist))))
|
||||
compare=&ds;
|
||||
run;
|
||||
|
||||
%if &sysinfo=0 %then %do;
|
||||
@@ -141,4 +144,4 @@
|
||||
drop table &ds;
|
||||
%end;
|
||||
|
||||
%mend mp_assertscope;
|
||||
%mend mp_assertscope;
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
/**
|
||||
@file
|
||||
@brief Mechanism for locking tables to prevent parallel modifications
|
||||
@details Uses a control table to enable ANY table to be locked for updates.
|
||||
@details Uses a control table to enable ANY table to be locked for updates
|
||||
(not just SAS datasets).
|
||||
Only useful if every update uses the macro! Used heavily within
|
||||
[Data Controller for SAS](https://datacontroller.io).
|
||||
|
||||
@@ -15,7 +16,7 @@
|
||||
length is 200 characters.
|
||||
@param [out] ctl_ds= (0) The control table which controls the actual locking.
|
||||
Should already be assigned and available. The definition is available by
|
||||
running mp_coretable.sas as follows: `%mp_coretable(LOCKTABLE)`.
|
||||
running the mddl_dc_locktable.sas macro.
|
||||
|
||||
@param [in] loops= (25) Number of times to check for a lock.
|
||||
@param [in] loop_secs= (1) Seconds to wait between each lock attempt
|
||||
|
||||
149
base/mp_replace.sas
Normal file
149
base/mp_replace.sas
Normal file
@@ -0,0 +1,149 @@
|
||||
/**
|
||||
@file
|
||||
@brief Performs a text substitution on a file
|
||||
@details Performs a find and replace on a file, either in place or to a new
|
||||
file. Can be used on files where lines are longer than 32767.
|
||||
|
||||
Works by reading in the file byte by byte, then marking the beginning and end
|
||||
of each matched string, before finally doing the replace.
|
||||
|
||||
Full credit for this highly efficient and syntactically satisfying SAS logic
|
||||
goes to [Bartosz Jabłoński](https://www.linkedin.com/in/yabwon), founder of
|
||||
the [SAS Packages](https://github.com/yabwon/SAS_PACKAGES) framework.
|
||||
|
||||
Usage:
|
||||
|
||||
%let file="%sysfunc(pathname(work))/file.txt";
|
||||
%let str=replace/me;
|
||||
%let rep=with/this;
|
||||
data _null_;
|
||||
file &file;
|
||||
put 'blahblah';
|
||||
put "blahblah&str.blah";
|
||||
put 'blahblahblah';
|
||||
run;
|
||||
%mp_replace(&file, findvar=str, replacevar=rep)
|
||||
data _null_;
|
||||
infile &file;
|
||||
input;
|
||||
list;
|
||||
run;
|
||||
|
||||
Note - if you are running a version of SAS that will allow the io package in
|
||||
LUA, you can also use this macro: mp_gsubfile.sas
|
||||
|
||||
@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
|
||||
avoid overwriting the first file).
|
||||
|
||||
<h4> SAS Macros </h4>
|
||||
@li mf_getuniquefileref.sas
|
||||
@li mf_getuniquename.sas
|
||||
|
||||
<h4> Related Macros </h4>
|
||||
@li mp_gsubfile.test.sas
|
||||
|
||||
@version 9.4
|
||||
@author Bartosz Jabłoński
|
||||
@author Allan Bowe
|
||||
**/
|
||||
|
||||
%macro mp_replace(infile,
|
||||
findvar=,
|
||||
replacevar=,
|
||||
outfile=0
|
||||
)/*/STORE SOURCE*/;
|
||||
|
||||
%local inref dttm ds1;
|
||||
%let inref=%mf_getuniquefileref();
|
||||
%let outref=%mf_getuniquefileref();
|
||||
%if &outfile=0 %then %let outfile=&infile;
|
||||
%let ds1=%mf_getuniquename(prefix=allchars);
|
||||
%let ds2=%mf_getuniquename(prefix=startmark);
|
||||
|
||||
/* START */
|
||||
%let dttm=%sysfunc(datetime());
|
||||
|
||||
filename &inref &infile lrecl=1 recfm=n;
|
||||
|
||||
data &ds1;
|
||||
infile &inref;
|
||||
input sourcechar $ 1. @@;
|
||||
format sourcechar hex2.;
|
||||
run;
|
||||
|
||||
data &ds2;
|
||||
/* set find string to length in bytes to cover trailing spaces */
|
||||
length string $ %length(%superq(&findvar));
|
||||
string =symget("&findvar");
|
||||
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;
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
stop;
|
||||
keep START STOP;
|
||||
run;
|
||||
|
||||
data &ds1;
|
||||
declare hash HS(dataset:"&ds2(keep=start)");
|
||||
HS.defineKey("start");
|
||||
HS.defineDone();
|
||||
declare hash HE(dataset:"&ds2(keep=stop)");
|
||||
HE.defineKey("stop");
|
||||
HE.defineDone();
|
||||
do until(eof);
|
||||
set &ds1 end=eof curobs =n;
|
||||
start = ^HS.check(key:n);
|
||||
stop = ^HE.check(key:n);
|
||||
length strt $ 1;
|
||||
strt =put(start,best. -L);
|
||||
retain out 1;
|
||||
if out then output;
|
||||
if start then out=0;
|
||||
if stop then out=1;
|
||||
end;
|
||||
stop;
|
||||
keep sourcechar strt;
|
||||
run;
|
||||
|
||||
filename &outref &outfile recfm=n;
|
||||
|
||||
data _null_;
|
||||
length replace $ %length(%superq(&replacevar));
|
||||
replace=symget("&replacevar");
|
||||
file &outref;
|
||||
do until(eof);
|
||||
set &ds1 end=eof;
|
||||
if strt ="1" then put replace char.;
|
||||
else put sourcechar char1.;
|
||||
end;
|
||||
stop;
|
||||
run;
|
||||
|
||||
/* END */
|
||||
%put &sysmacroname took %sysevalf(%sysfunc(datetime())-&dttm) seconds to run;
|
||||
|
||||
%mend mp_replace;
|
||||
@@ -12,7 +12,7 @@
|
||||
|
||||
%mp_streamfile(contenttype=csv,inloc=/some/where.txt,outname=myfile.txt)
|
||||
|
||||
@param [in] contenttype= (TEXTS) Either TEXT, ZIP, CSV, EXCEL
|
||||
@param [in] contenttype= (TEXT) Either TEXT, ZIP, CSV, EXCEL
|
||||
@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.
|
||||
@@ -22,6 +22,7 @@
|
||||
|
||||
<h4> SAS Macros </h4>
|
||||
@li mf_getplatform.sas
|
||||
@li mfs_httpheader.sas
|
||||
@li mp_binarycopy.sas
|
||||
|
||||
@author Allan Bowe
|
||||
@@ -55,7 +56,7 @@ data _null_;
|
||||
run;
|
||||
|
||||
%if &contentype=CSV %then %do;
|
||||
%if (&platform=SASMETA and &streamweb=1) or &platform=SASJS %then %do;
|
||||
%if (&platform=SASMETA and &streamweb=1) %then %do;
|
||||
data _null_;
|
||||
rc=stpsrv_header('Content-type','application/csv');
|
||||
rc=stpsrv_header('Content-disposition',"attachment; filename=&outname");
|
||||
@@ -66,10 +67,14 @@ run;
|
||||
contenttype='application/csv'
|
||||
contentdisp="attachment; filename=&outname";
|
||||
%end;
|
||||
%else %if &platform=SASJS %then %do;
|
||||
%mfs_httpheader(Content-type,application/csv)
|
||||
%mfs_httpheader(Content-disposition,%str(attachment; filename=&outname))
|
||||
%end;
|
||||
%end;
|
||||
%else %if &contentype=EXCEL %then %do;
|
||||
/* suitable for XLS format */
|
||||
%if (&platform=SASMETA and &streamweb=1) or &platform=SASJS %then %do;
|
||||
%if (&platform=SASMETA and &streamweb=1) %then %do;
|
||||
data _null_;
|
||||
rc=stpsrv_header('Content-type','application/vnd.ms-excel');
|
||||
rc=stpsrv_header('Content-disposition',"attachment; filename=&outname");
|
||||
@@ -80,9 +85,13 @@ run;
|
||||
contenttype='application/vnd.ms-excel'
|
||||
contentdisp="attachment; filename=&outname";
|
||||
%end;
|
||||
%else %if &platform=SASJS %then %do;
|
||||
%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) or &platform=SASJS %then %do;
|
||||
%if (&platform=SASMETA and &streamweb=1) %then %do;
|
||||
data _null_;
|
||||
rc=stpsrv_header('Content-type',"image/%lowcase(&contenttype)");
|
||||
run;
|
||||
@@ -91,15 +100,21 @@ run;
|
||||
filename &outref filesrvc parenturi="&SYS_JES_JOB_URI"
|
||||
contenttype="image/%lowcase(&contenttype)";
|
||||
%end;
|
||||
%else %if &platform=SASJS %then %do;
|
||||
%mfs_httpheader(Content-type,image/%lowcase(&contenttype))
|
||||
%end;
|
||||
%end;
|
||||
%else %if &contentype=HTML %then %do;
|
||||
%if &platform=SASVIYA %then %do;
|
||||
filename &outref filesrvc parenturi="&SYS_JES_JOB_URI" name="_webout.json"
|
||||
contenttype="text/html";
|
||||
%end;
|
||||
%else %if &platform=SASJS %then %do;
|
||||
%mfs_httpheader(Content-type,text/html)
|
||||
%end;
|
||||
%end;
|
||||
%else %if &contentype=TEXT %then %do;
|
||||
%if (&platform=SASMETA and &streamweb=1) or &platform=SASJS %then %do;
|
||||
%if (&platform=SASMETA and &streamweb=1) %then %do;
|
||||
data _null_;
|
||||
rc=stpsrv_header('Content-type','application/text');
|
||||
rc=stpsrv_header('Content-disposition',"attachment; filename=&outname");
|
||||
@@ -110,9 +125,13 @@ run;
|
||||
contenttype='application/text'
|
||||
contentdisp="attachment; filename=&outname";
|
||||
%end;
|
||||
%else %if &platform=SASJS %then %do;
|
||||
%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) or &platform=SASJS %then %do;
|
||||
%if (&platform=SASMETA and &streamweb=1) %then %do;
|
||||
data _null_;
|
||||
rc=stpsrv_header('Content-type',"font/%lowcase(&contenttype)");
|
||||
run;
|
||||
@@ -121,9 +140,12 @@ run;
|
||||
filename &outref filesrvc parenturi="&SYS_JES_JOB_URI"
|
||||
contenttype="font/%lowcase(&contenttype)";
|
||||
%end;
|
||||
%else %if &platform=SASJS %then %do;
|
||||
%mfs_httpheader(Content-type,font/%lowcase(&contenttype))
|
||||
%end;
|
||||
%end;
|
||||
%else %if &contentype=XLSX %then %do;
|
||||
%if (&platform=SASMETA and &streamweb=1) or &platform=SASJS %then %do;
|
||||
%if (&platform=SASMETA and &streamweb=1) %then %do;
|
||||
data _null_;
|
||||
rc=stpsrv_header('Content-type',
|
||||
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
|
||||
@@ -136,9 +158,15 @@ run;
|
||||
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
|
||||
contentdisp="attachment; filename=&outname";
|
||||
%end;
|
||||
%else %if &platform=SASJS %then %do;
|
||||
%mfs_httpheader(Content-type
|
||||
,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
|
||||
)
|
||||
%mfs_httpheader(Content-disposition,%str(attachment; filename=&outname))
|
||||
%end;
|
||||
%end;
|
||||
%else %if &contentype=ZIP %then %do;
|
||||
%if (&platform=SASMETA and &streamweb=1) or &platform=SASJS %then %do;
|
||||
%if (&platform=SASMETA and &streamweb=1) %then %do;
|
||||
data _null_;
|
||||
rc=stpsrv_header('Content-type','application/zip');
|
||||
rc=stpsrv_header('Content-disposition',"attachment; filename=&outname");
|
||||
@@ -149,6 +177,10 @@ run;
|
||||
contenttype='application/zip'
|
||||
contentdisp="attachment; filename=&outname";
|
||||
%end;
|
||||
%else %if &platform=SASJS %then %do;
|
||||
%mfs_httpheader(Content-type,application/zip)
|
||||
%mfs_httpheader(Content-disposition,%str(attachment; filename=&outname))
|
||||
%end;
|
||||
%end;
|
||||
%else %do;
|
||||
%put %str(ERR)OR: Content Type &contenttype NOT SUPPORTED by &sysmacroname!;
|
||||
|
||||
@@ -40,6 +40,12 @@
|
||||
/* now try and assign it */
|
||||
if libname("&libref",,'meta',cats('liburi="',liburi,'";')) ne 0 then do;
|
||||
putlog "&libref could not be assigned";
|
||||
putlog liburi=;
|
||||
/**
|
||||
* Fetch the system message for display in the abort modal. This is
|
||||
* 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');
|
||||
if "&mabort"='HARD' then call symputx('mp_abort',1,'l');
|
||||
end;
|
||||
@@ -61,7 +67,7 @@
|
||||
|
||||
%if &mp_abort=1 %then %do;
|
||||
%mp_abort(iftrue= (&mp_abort=1)
|
||||
,mac=&sysmacroname
|
||||
,mac=mm_assignlib.sas
|
||||
,msg=&msg
|
||||
)
|
||||
%return;
|
||||
|
||||
@@ -1,16 +1,21 @@
|
||||
/**
|
||||
@file mm_getauthinfo.sas
|
||||
@brief extracts authentication info
|
||||
@details usage:
|
||||
@brief Extracts authentication info for each user in metadata
|
||||
@details
|
||||
Usage:
|
||||
|
||||
%mm_getauthinfo(outds=auths)
|
||||
%mm_getauthinfo(outds=auths)
|
||||
|
||||
@param outds= the ONE LEVEL work dataset to create
|
||||
|
||||
@param [in] mdebug= (0) Set to 1 to enable DEBUG messages and preserve outputs
|
||||
@param [out] outds= (mm_getauthinfo) The output dataset to create
|
||||
|
||||
<h4> SAS Macros </h4>
|
||||
@li mm_getobjects.sas
|
||||
@li mf_getuniquefileref.sas
|
||||
@li mf_getuniquename.sas
|
||||
@li mm_getdetails.sas
|
||||
@li mm_getobjects.sas
|
||||
|
||||
|
||||
@version 9.4
|
||||
@author Allan Bowe
|
||||
@@ -18,67 +23,69 @@
|
||||
**/
|
||||
|
||||
%macro mm_getauthinfo(outds=mm_getauthinfo
|
||||
,mdebug=0
|
||||
)/*/STORE SOURCE*/;
|
||||
%local prefix fileref;
|
||||
%let prefix=%substr(%mf_getuniquename(),1,25);
|
||||
|
||||
%if %length(&outds)>30 %then %do;
|
||||
%put %str(ERR)OR: Temp tables are created with the &outds prefix, which
|
||||
therefore needs to be 30 characters or less;
|
||||
%return;
|
||||
%end;
|
||||
%if %index(&outds,'.')>0 %then %do;
|
||||
%put %str(ERR)OR: Table &outds should be ONE LEVEL (no library);
|
||||
%return;
|
||||
%end;
|
||||
|
||||
%mm_getobjects(type=Login,outds=&outds.0)
|
||||
%mm_getobjects(type=Login,outds=&prefix.0)
|
||||
|
||||
%local fileref;
|
||||
%let fileref=%mf_getuniquefileref();
|
||||
|
||||
data _null_;
|
||||
file &fileref;
|
||||
set &outds.0 end=last;
|
||||
set &prefix.0 end=last;
|
||||
/* run macro */
|
||||
str=cats('%mm_getdetails(uri=',id,",outattrs=&outds.d",_n_
|
||||
,",outassocs=&outds.a",_n_,")");
|
||||
str=cats('%mm_getdetails(uri=',id,",outattrs=&prefix.d",_n_
|
||||
,",outassocs=&prefix.a",_n_,")");
|
||||
put str;
|
||||
/* transpose attributes */
|
||||
str=cats("proc transpose data=&outds.d",_n_,"(drop=type) out=&outds.da"
|
||||
str=cats("proc transpose data=&prefix.d",_n_,"(drop=type) out=&prefix.da"
|
||||
,_n_,"(drop=_name_);var value;id name;run;");
|
||||
put str;
|
||||
/* add extra info to attributes */
|
||||
str=cats("data &outds.da",_n_,";length login_id login_name $256; login_id="
|
||||
,quote(trim(id)),";set &outds.da",_n_
|
||||
str=cats("data &prefix.da",_n_,";length login_id login_name $256; login_id="
|
||||
,quote(trim(id)),";set &prefix.da",_n_
|
||||
,";login_name=trim(subpad(name,1,256));drop name;run;");
|
||||
put str;
|
||||
/* add extra info to associations */
|
||||
str=cats("data &outds.a",_n_,";length login_id login_name $256; login_id="
|
||||
str=cats("data &prefix.a",_n_,";length login_id login_name $256; login_id="
|
||||
,quote(trim(id)),";login_name=",quote(trim(name))
|
||||
,";set &outds.a",_n_,";run;");
|
||||
,";set &prefix.a",_n_,";run;");
|
||||
put str;
|
||||
if last then do;
|
||||
/* collate attributes */
|
||||
str=cats("data &outds._logat; set &outds.da1-&outds.da",_n_,";run;");
|
||||
str=cats("data &prefix._logat; set &prefix.da1-&prefix.da",_n_,";run;");
|
||||
put str;
|
||||
/* collate associations */
|
||||
str=cats("data &outds._logas; set &outds.a1-&outds.a",_n_,";run;");
|
||||
str=cats("data &prefix._logas; set &prefix.a1-&prefix.a",_n_,";run;");
|
||||
put str;
|
||||
/* tidy up */
|
||||
str=cats("proc delete data=&outds.da1-&outds.da",_n_,";run;");
|
||||
str=cats("proc delete data=&prefix.da1-&prefix.da",_n_,";run;");
|
||||
put str;
|
||||
str=cats("proc delete data=&outds.d1-&outds.d",_n_,";run;");
|
||||
str=cats("proc delete data=&prefix.d1-&prefix.d",_n_,";run;");
|
||||
put str;
|
||||
str=cats("proc delete data=&outds.a1-&outds.a",_n_,";run;");
|
||||
str=cats("proc delete data=&prefix.a1-&prefix.a",_n_,";run;");
|
||||
put str;
|
||||
end;
|
||||
run;
|
||||
|
||||
%if &mdebug=1 %then %do;
|
||||
data _null_;
|
||||
infile &fileref;
|
||||
if _n_=1 then putlog // "Now executing the following code:" //;
|
||||
input; putlog _infile_;
|
||||
run;
|
||||
%end;
|
||||
%inc &fileref;
|
||||
filename &fileref clear;
|
||||
|
||||
/* get libraries */
|
||||
proc sort data=&outds._logas(where=(assoc='Libraries')) out=&outds._temp;
|
||||
proc sort data=&prefix._logas(where=(assoc='Libraries')) out=&prefix._temp;
|
||||
by login_id;
|
||||
data &outds._temp;
|
||||
set &outds._temp;
|
||||
data &prefix._temp;
|
||||
set &prefix._temp;
|
||||
by login_id;
|
||||
length library_list $32767;
|
||||
retain library_list;
|
||||
@@ -86,31 +93,27 @@ data &outds._temp;
|
||||
else library_list=catx(' !! ',library_list,name);
|
||||
proc sql;
|
||||
/* get auth domain */
|
||||
create table &outds._dom as
|
||||
create table &prefix._dom as
|
||||
select login_id,name as domain
|
||||
from &outds._logas
|
||||
from &prefix._logas
|
||||
where assoc='Domain';
|
||||
create unique index login_id on &outds._dom(login_id);
|
||||
create unique index login_id on &prefix._dom(login_id);
|
||||
/* join it all together */
|
||||
create table &outds._logins as
|
||||
create table &outds as
|
||||
select a.*
|
||||
,c.domain
|
||||
,b.library_list
|
||||
from &outds._logat (drop=ishidden lockedby usageversion publictype) a
|
||||
left join &outds._temp b
|
||||
from &prefix._logat (drop=ishidden lockedby usageversion publictype) a
|
||||
left join &prefix._temp b
|
||||
on a.login_id=b.login_id
|
||||
left join &outds._dom c
|
||||
left join &prefix._dom c
|
||||
on a.login_id=c.login_id;
|
||||
drop table &outds._temp;
|
||||
drop table &outds._logat;
|
||||
drop table &outds._logas;
|
||||
|
||||
data _null_;
|
||||
infile &fileref;
|
||||
if _n_=1 then putlog // "Now executing the following code:" //;
|
||||
input; putlog _infile_;
|
||||
run;
|
||||
%if &mdebug=0 %then %do;
|
||||
proc datasets lib=work;
|
||||
delete &prefix:;
|
||||
run;
|
||||
%end;
|
||||
|
||||
filename &fileref clear;
|
||||
|
||||
%mend mm_getauthinfo;
|
||||
@@ -73,17 +73,24 @@
|
||||
"name": "server",
|
||||
"serverUrl": "https://sas.analytium.co.uk:5000",
|
||||
"serverType": "SASJS",
|
||||
"appLoc": "/Shared Data/temp/macrocore",
|
||||
"httpsAgentOptions": {
|
||||
"allowInsecureRequests": false
|
||||
},
|
||||
"appLoc": "/sasjs/core",
|
||||
"macroFolders": [
|
||||
"tests/serveronly"
|
||||
],
|
||||
"programFolders": [],
|
||||
"binaryFolders": [],
|
||||
"deployConfig": {
|
||||
"deployServicePack": true
|
||||
"deployServicePack": true,
|
||||
"deployScripts": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "docsonly",
|
||||
"serverType": "SAS9",
|
||||
"appLoc": "dummy",
|
||||
"macroFolders": [
|
||||
"tests/sas9only",
|
||||
"tests/viyaonly"
|
||||
|
||||
89
server/ms_createfile.sas
Normal file
89
server/ms_createfile.sas
Normal file
@@ -0,0 +1,89 @@
|
||||
/**
|
||||
@file
|
||||
@brief Creates a file on SASjs Drive
|
||||
@details Creates a file on SASjs Drive. To use the file as a Stored Program,
|
||||
it must have a ".sas" extension.
|
||||
|
||||
Example:
|
||||
|
||||
filename stpcode temp;
|
||||
data _null_;
|
||||
file stpcode;
|
||||
put '%put hello world;';
|
||||
run;
|
||||
%ms_createfile(/some/stored/program.sas, inref=stpcode)
|
||||
|
||||
@param [in] driveloc The full path to the file in SASjs Drive
|
||||
@param [in] inref= (0) The fileref containing the file to create.
|
||||
@param [in] mdebug= (0) Set to 1 to enable DEBUG messages
|
||||
|
||||
<h4> SAS Macros </h4>
|
||||
@li mf_getuniquefileref.sas
|
||||
@li mf_getuniquename.sas
|
||||
@li mp_abort.sas
|
||||
|
||||
**/
|
||||
|
||||
%macro ms_createfile(driveloc
|
||||
,inref=0
|
||||
,mdebug=0
|
||||
);
|
||||
|
||||
%local fname0 fname1 boundary fname statcd msg;
|
||||
%let fname0=%mf_getuniquefileref();
|
||||
%let fname1=%mf_getuniquefileref();
|
||||
%let boundary=%mf_getuniquename();
|
||||
|
||||
data _null_;
|
||||
file &fname0 termstr=crlf;
|
||||
infile &inref end=eof;
|
||||
if _n_ = 1 then do;
|
||||
put "--&boundary.";
|
||||
put 'Content-Disposition: form-data; name="filePath"';
|
||||
put ;
|
||||
put "&driveloc";
|
||||
put "--&boundary";
|
||||
put 'Content-Disposition: form-data; name="file"; filename="ignore.sas"';
|
||||
put "Content-Type: text/plain";
|
||||
put ;
|
||||
end;
|
||||
input;
|
||||
put _infile_; /* add the actual file to be sent */
|
||||
if eof then do;
|
||||
put ;
|
||||
put "--&boundary--";
|
||||
end;
|
||||
run;
|
||||
|
||||
%if &mdebug=1 %then %do;
|
||||
data _null_;
|
||||
infile &fname0;
|
||||
input;
|
||||
put _infile_;
|
||||
run;
|
||||
%end;
|
||||
|
||||
proc http method='POST' in=&fname0 out=&fname1
|
||||
url="&_sasjs_apiserverurl/SASjsApi/drive/file";
|
||||
headers "Content-Type"="multipart/form-data; boundary=&boundary";
|
||||
%if &mdebug=1 %then %do;
|
||||
debug level=1;
|
||||
%end;
|
||||
run;
|
||||
|
||||
%let statcd=0;
|
||||
data _null_;
|
||||
infile &fname1;
|
||||
input;
|
||||
putlog _infile_;
|
||||
if _infile_='{"status":"success"}' then call symputx('statcd',1,'l');
|
||||
else call symputx('msg',_infile_,'l');
|
||||
run;
|
||||
|
||||
%mp_abort(
|
||||
iftrue=(&statcd=0)
|
||||
,mac=ms_createfile.sas
|
||||
,msg=%superq(msg)
|
||||
)
|
||||
|
||||
%mend ms_createfile;
|
||||
32
server/ms_getfile.sas
Normal file
32
server/ms_getfile.sas
Normal file
@@ -0,0 +1,32 @@
|
||||
/**
|
||||
@file
|
||||
@brief Gets a file from SASjs Drive
|
||||
@details Fetches a file on SASjs Drive and stores it in the output fileref.
|
||||
|
||||
Example:
|
||||
|
||||
%ms_getfile(/some/stored/file.ext, 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
|
||||
|
||||
|
||||
**/
|
||||
|
||||
%macro ms_getfile(driveloc
|
||||
,outref=msgetfil
|
||||
,mdebug=0
|
||||
);
|
||||
|
||||
filename &outref temp;
|
||||
|
||||
proc http method='GET' out=&outref
|
||||
url="&_sasjs_apiserverurl/SASjsApi/drive/file?filePath=&driveloc";
|
||||
%if &mdebug=1 %then %do;
|
||||
debug level=2;
|
||||
%end;
|
||||
run;
|
||||
|
||||
|
||||
%mend ms_getfile;
|
||||
77
server/ms_runstp.sas
Normal file
77
server/ms_runstp.sas
Normal file
@@ -0,0 +1,77 @@
|
||||
/**
|
||||
@file
|
||||
@brief Executes a SASjs Server Stored Program
|
||||
@details Runs a Stored Program (using POST method) and extracts the webout and
|
||||
log from the response JSON.
|
||||
|
||||
Example:
|
||||
|
||||
%ms_runstp(/some/stored/program
|
||||
,debug=131
|
||||
,outref=weboot
|
||||
)
|
||||
|
||||
@param [in] pgm The full path to the Stored Program in SASjs Drive (_program
|
||||
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 [out] outref= (outweb) The output fileref to contain the response JSON
|
||||
(will be created using temp engine)
|
||||
|
||||
<h4> SAS Macros </h4>
|
||||
@li mf_getuniquefileref.sas
|
||||
@li mp_abort.sas
|
||||
|
||||
**/
|
||||
|
||||
%macro ms_runstp(pgm
|
||||
,debug=131
|
||||
,outref=outweb
|
||||
,mdebug=0
|
||||
);
|
||||
%local dbg fname1;
|
||||
%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)
|
||||
)
|
||||
|
||||
data _null_;
|
||||
file &fname1;
|
||||
infile "&_sasjs_tokenfile";
|
||||
input;
|
||||
put 'Authorization: Bearer' _infile_;
|
||||
run;
|
||||
|
||||
filename &outref temp;
|
||||
|
||||
/* prepare request*/
|
||||
proc http method='POST' headerin=&fname1 out=&outref
|
||||
url="&_sasjs_apiserverurl.&_sasjs_apipath?_program=&pgm%str(&)_debug=131";
|
||||
run;
|
||||
%if (&SYS_PROCHTTP_STATUS_CODE ne 200 and &SYS_PROCHTTP_STATUS_CODE ne 201)
|
||||
or &mdebug=1 %then %do;
|
||||
data _null_;infile &outref;input;putlog _infile_;run;
|
||||
%end;
|
||||
%mp_abort(
|
||||
iftrue=(&SYS_PROCHTTP_STATUS_CODE ne 200 and &SYS_PROCHTTP_STATUS_CODE ne 201)
|
||||
,mac=&sysmacroname
|
||||
,msg=%str(&SYS_PROCHTTP_STATUS_CODE &SYS_PROCHTTP_STATUS_PHRASE)
|
||||
)
|
||||
|
||||
|
||||
%if &mdebug=1 %then %do;
|
||||
%put &sysmacroname exit vars:;
|
||||
%put _local_;
|
||||
%end;
|
||||
%else %do;
|
||||
/* clear refs */
|
||||
filename &fname1 clear;
|
||||
%end;
|
||||
%mend ms_runstp;
|
||||
54
tests/crossplatform/mf_getuniquelibref.test.sas
Normal file
54
tests/crossplatform/mf_getuniquelibref.test.sas
Normal file
@@ -0,0 +1,54 @@
|
||||
/**
|
||||
@file
|
||||
@brief Testing mf_getuniquelibref macro
|
||||
@details To test performance you can also use the following macro:
|
||||
|
||||
<h4> SAS Macros </h4>
|
||||
@li mf_getuniquelibref.sas
|
||||
@li mp_assert.sas
|
||||
@li mp_assertscope.sas
|
||||
|
||||
**/
|
||||
|
||||
/* check valid libs */
|
||||
%mp_assertscope(SNAPSHOT)
|
||||
%let libshort=%mf_getuniquelibref(prefix=lib);
|
||||
%mp_assertscope(COMPARE,ignorelist=LIBSHORT)
|
||||
libname &libshort (work);
|
||||
%mp_assert(
|
||||
iftrue=(&syscc=0),
|
||||
desc=Checking for valid libref &libshort,
|
||||
outds=work.test_results
|
||||
)
|
||||
|
||||
%let lib7=%mf_getuniquelibref(prefix=libref7);
|
||||
libname &lib7 (work);
|
||||
%mp_assert(
|
||||
iftrue=(&syscc=0),
|
||||
desc=Checking for valid libref &lib7,
|
||||
outds=work.test_results
|
||||
)
|
||||
|
||||
|
||||
/* check for invalid libs */
|
||||
|
||||
%let lib8=%mf_getuniquelibref(prefix=lib8char);
|
||||
%mp_assert(
|
||||
iftrue=(&lib8=0),
|
||||
desc=Invalid prefix (8 chars),
|
||||
outds=work.test_results
|
||||
)
|
||||
|
||||
%let liblong=%mf_getuniquelibref(prefix=invalidlib);
|
||||
%mp_assert(
|
||||
iftrue=(&liblong=0),
|
||||
desc=Checking for invalid libref (long),
|
||||
outds=work.test_results
|
||||
)
|
||||
|
||||
%let badlib=%mf_getuniquelibref(prefix=8adlib);
|
||||
%mp_assert(
|
||||
iftrue=(&badlib=0),
|
||||
desc=Checking for invalid libref (8adlib),
|
||||
outds=work.test_results
|
||||
)
|
||||
@@ -3,6 +3,7 @@
|
||||
@brief Testing mp_cntlout.sas macro
|
||||
|
||||
<h4> SAS Macros </h4>
|
||||
@li mf_nobs.sas
|
||||
@li mp_cntlout.sas
|
||||
@li mp_assert.sas
|
||||
@li mp_assertscope.sas
|
||||
|
||||
65
tests/crossplatform/mp_replace.test.sas
Normal file
65
tests/crossplatform/mp_replace.test.sas
Normal file
@@ -0,0 +1,65 @@
|
||||
/**
|
||||
@file
|
||||
@brief Testing mp_replace.sas macro
|
||||
|
||||
<h4> SAS Macros </h4>
|
||||
@li mp_replace.sas
|
||||
@li mp_assert.sas
|
||||
@li mp_assertscope.sas
|
||||
|
||||
**/
|
||||
|
||||
|
||||
%let test1="&sasjswork/file.txt";
|
||||
%let str=replace/me;
|
||||
%let rep=with/this;
|
||||
data _null_;
|
||||
file &test1;
|
||||
put 'blahblah';
|
||||
put "blahblah&str.blah";
|
||||
put 'blahblahblah';
|
||||
run;
|
||||
%mp_assertscope(SNAPSHOT)
|
||||
%mp_replace(&test1, findvar=str, replacevar=rep)
|
||||
%mp_assertscope(COMPARE)
|
||||
data _null_;
|
||||
infile &test1;
|
||||
input;
|
||||
if _n_=2 then call symputx('test1result',_infile_);
|
||||
run;
|
||||
|
||||
%mp_assert(
|
||||
iftrue=("&test1result" = "blahblah&rep.blah"),
|
||||
desc=Checking first replace,
|
||||
outds=work.test_results
|
||||
)
|
||||
|
||||
|
||||
%let test2="&sasjswork/file2.txt";
|
||||
%let str=%str(replacewith trailing spaces );
|
||||
%let rep=%str( with more spaces );
|
||||
data _null_;
|
||||
file &test2;
|
||||
put 'blahblah';
|
||||
put "blahblah&str.blah&str. replace &str.X";
|
||||
put "blahbreplacewith&str.spacesahblah";
|
||||
run;
|
||||
%mp_replace(&test2, findvar=str, replacevar=rep)
|
||||
|
||||
data _null_;
|
||||
infile &test2;
|
||||
input;
|
||||
if _n_=2 then call symputx('test2resulta',_infile_);
|
||||
if _n_=3 then call symputx('test2resultb',_infile_);
|
||||
run;
|
||||
|
||||
%mp_assert(
|
||||
iftrue=("&test2resulta" = "blahblah&rep.blah&rep. replace &rep.X"),
|
||||
desc=Checking second replace 2nd row,
|
||||
outds=work.test_results
|
||||
)
|
||||
%mp_assert(
|
||||
iftrue=("&test2resultb" = "blahbreplacewith&rep.spacesahblah"),
|
||||
desc=Checking second replace 3rd row,
|
||||
outds=work.test_results
|
||||
)
|
||||
21
tests/sas9only/mm_getauthinfo.test.sas
Normal file
21
tests/sas9only/mm_getauthinfo.test.sas
Normal file
@@ -0,0 +1,21 @@
|
||||
/**
|
||||
@file
|
||||
@brief Testing mm_getauthinfo macro
|
||||
|
||||
<h4> SAS Macros </h4>
|
||||
@li mf_existds.sas
|
||||
@li mm_getauthinfo.sas
|
||||
@li mp_assertscope.sas
|
||||
|
||||
**/
|
||||
|
||||
|
||||
%mp_assertscope(SNAPSHOT)
|
||||
%mm_getauthinfo(outds=auths)
|
||||
%mp_assertscope(COMPARE)
|
||||
|
||||
|
||||
%mp_assert(
|
||||
iftrue=(%mf_existds(work.auths)=1),
|
||||
desc=Check if the auths dataset was created
|
||||
)
|
||||
@@ -5,12 +5,16 @@
|
||||
<h4> SAS Macros </h4>
|
||||
@li mfs_httpheader.sas
|
||||
@li mp_assert.sas
|
||||
@li mp_assertscope.sas
|
||||
|
||||
**/
|
||||
|
||||
%let sasjs_stpsrv_header_loc=%sysfunc(pathname(work))/header.txt;
|
||||
|
||||
%mp_assertscope(SNAPSHOT)
|
||||
%mfs_httpheader(Content-type,application/csv)
|
||||
%mp_assertscope(COMPARE)
|
||||
|
||||
data _null_;
|
||||
infile "&sasjs_stpsrv_header_loc";
|
||||
input;
|
||||
|
||||
30
tests/serveronly/ms_createfile.test.sas
Normal file
30
tests/serveronly/ms_createfile.test.sas
Normal file
@@ -0,0 +1,30 @@
|
||||
/**
|
||||
@file
|
||||
@brief Testing ms_createfile.sas macro
|
||||
|
||||
<h4> SAS Macros </h4>
|
||||
@li ms_createfile.sas
|
||||
@li mp_assert.sas
|
||||
@li mp_assertscope.sas
|
||||
|
||||
**/
|
||||
|
||||
|
||||
filename stpcode temp;
|
||||
data _null_;
|
||||
file stpcode;
|
||||
put '%put hello world;';
|
||||
run;
|
||||
|
||||
options mprint;
|
||||
%let fname=%mf_getuniquename();
|
||||
|
||||
%mp_assertscope(SNAPSHOT)
|
||||
%ms_createfile(/sasjs/tests/&fname..sas
|
||||
,inref=stpcode
|
||||
,mdebug=1
|
||||
)
|
||||
%mp_assertscope(COMPARE)
|
||||
|
||||
|
||||
|
||||
45
tests/serveronly/ms_getfile.test.sas
Normal file
45
tests/serveronly/ms_getfile.test.sas
Normal file
@@ -0,0 +1,45 @@
|
||||
/**
|
||||
@file
|
||||
@brief Testing ms_getfile.sas macro
|
||||
|
||||
<h4> SAS Macros </h4>
|
||||
@li ms_createfile.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
|
||||
)
|
||||
|
||||
%mp_assertscope(SNAPSHOT)
|
||||
%ms_getfile(/sasjs/tests/&fname..sas,outref=testref)
|
||||
%mp_assertscope(COMPARE)
|
||||
|
||||
%let test1=0;
|
||||
data _null_;
|
||||
infile testref;
|
||||
input;
|
||||
call symputx('test1',_infile_);
|
||||
run;
|
||||
|
||||
%mp_assert(
|
||||
iftrue=("&test1"="data &fname;run;"),
|
||||
desc=Checking file was created with the same content,
|
||||
outds=work.test_results
|
||||
)
|
||||
|
||||
|
||||
|
||||
|
||||
44
tests/serveronly/ms_runstp.test.sas
Normal file
44
tests/serveronly/ms_runstp.test.sas
Normal file
@@ -0,0 +1,44 @@
|
||||
/**
|
||||
@file
|
||||
@brief Testing ms_runstp.sas macro
|
||||
|
||||
<h4> SAS Macros </h4>
|
||||
@li ms_runstp.sas
|
||||
@li mp_assert.sas
|
||||
@li mp_assertscope.sas
|
||||
|
||||
**/
|
||||
|
||||
%mp_assertscope(SNAPSHOT)
|
||||
%ms_runstp(/Public/app/frs/allan/services/common/appinit
|
||||
,debug=131
|
||||
,outref=weboot
|
||||
)
|
||||
%mp_assertscope(COMPARE)
|
||||
|
||||
libname webeen json (weboot);
|
||||
|
||||
data _null_;
|
||||
infile weboot;
|
||||
input;
|
||||
putlog _infile_;
|
||||
run;
|
||||
|
||||
data work.httpheaders;
|
||||
set webeen.httpheaders;
|
||||
call symputx('test1',content_type);
|
||||
run;
|
||||
|
||||
data work.log;
|
||||
set webeen.log;
|
||||
put (_all_)(=);
|
||||
if _n_>10 then stop;
|
||||
run;
|
||||
|
||||
%mp_assert(
|
||||
iftrue=("&test1"="application/json"),
|
||||
desc=Checking line was created,
|
||||
outds=work.test_results
|
||||
)
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
/**
|
||||
@file mv_registerclient.sas
|
||||
@brief Register Client and Secret (admin task)
|
||||
@details When building apps on SAS Viya, an client id and secret are sometimes
|
||||
required. In order to generate them, filesystem access to the Consul Token
|
||||
is needed (it is not enough to be in the SASAdministrator group in SAS
|
||||
Environment Manager).
|
||||
@details When building apps on SAS Viya, a client id and secret are usually
|
||||
required. In order to generate them, the Consul Token is required. To access
|
||||
this token, you need to be a system administrator (it is not enough to be in
|
||||
the SASAdministrator group in SAS Environment Manager).
|
||||
|
||||
If you are registering a lot of clients / secrets, you may find it more
|
||||
convenient to use the [Viya Token Generator]
|
||||
@@ -25,62 +25,69 @@
|
||||
"https://raw.githubusercontent.com/sasjs/core/main/all.sas";
|
||||
%inc mc;
|
||||
|
||||
%* generate random client using consul token as input parameter;
|
||||
%mv_registerclient(consul_token=12x34sa43v2345n234lasd)
|
||||
|
||||
%* generate random client details with all scopes;
|
||||
%mv_registerclient(scopes=openid *)
|
||||
|
||||
%* specific client with just openid scope;
|
||||
%mv_registerclient(client_id=YourClient
|
||||
,client_secret=YourSecret
|
||||
,scopes=openid
|
||||
)
|
||||
|
||||
%* generate random client details with all scopes;
|
||||
%mv_registerclient(scopes=openid *)
|
||||
|
||||
%* generate random client with 90/180 second access/refresh token expiry;
|
||||
%mv_registerclient(scopes=openid *
|
||||
,access_token_validity=90
|
||||
,refresh_token_validity=180
|
||||
)
|
||||
|
||||
@param client_id= The client name. Auto generated if blank.
|
||||
@param client_secret= Client secret. Auto generated if client is blank.
|
||||
@param scopes=(openid) List of space-seperated unquoted scopes
|
||||
@param grant_type=(authorization_code|refresh_token) Valid values are
|
||||
"password" or "authorization_code" (unquoted)
|
||||
@param outds=(mv_registerclient) The dataset to contain the registered client
|
||||
id and secret
|
||||
@param access_token_validity=(DEFAULT) The duration of validity of the access
|
||||
token in seconds. A value of DEFAULT will omit the entry (and use system
|
||||
default)
|
||||
@param refresh_token_validity=(DEFAULT) The duration of validity of the
|
||||
@param [in,out] client_id= The client name. Auto generated if blank.
|
||||
@param [in,out] client_secret= Client secret. Auto generated if client is
|
||||
blank.
|
||||
@param [in] consul_token= (0) Provide the actual consul token value here if
|
||||
using Viya 4 or above.
|
||||
@param [in] scopes= (openid) List of space-seperated unquoted scopes
|
||||
@param [in] grant_type= (authorization_code|refresh_token) Valid values are
|
||||
"password" or "authorization_code" (unquoted). Pipe seperated.
|
||||
@param [out] outds=(mv_registerclient) The dataset to contain the registered
|
||||
client id and secret
|
||||
@param [in] access_token_validity= (DEFAULT) The access token duration in
|
||||
seconds. A value of DEFAULT will omit the entry (and use system default)
|
||||
@param [in] refresh_token_validity= (DEFAULT) The duration of validity of the
|
||||
refresh token in seconds. A value of DEFAULT will omit the entry (and use
|
||||
system default)
|
||||
@param name= An optional, human readable name for the client
|
||||
@param required_user_groups= A list of group names. If a user does not belong
|
||||
to all the required groups, the user will not be authenticated and no tokens
|
||||
are issued to this client for that user. If this field is not specified,
|
||||
authentication and token issuance proceeds normally.
|
||||
@param autoapprove= During the auth step the user can choose which scope to
|
||||
apply. Setting this to true will autoapprove all the client scopes.
|
||||
@param use_session= If true, access tokens issued to this client will be
|
||||
@param [in] client_name= (DEFAULT) An optional, human readable name for the
|
||||
client.
|
||||
@param [in] required_user_groups= A list of group names. If a user does not
|
||||
belong to all the required groups, the user will not be authenticated and no
|
||||
tokens are issued to this client for that user. If this field is not
|
||||
specified, authentication and token issuance proceeds normally.
|
||||
@param [in] autoapprove= During the auth step the user can choose which scope
|
||||
to apply. Setting this to true will autoapprove all the client scopes.
|
||||
@param [in] use_session= If true, access tokens issued to this client will be
|
||||
associated with an HTTP session and revoked upon logout or time-out.
|
||||
@param outjson= (_null_) A dataset containing the lines of JSON submitted.
|
||||
Useful for debugging.
|
||||
@param [out] outjson= (_null_) A dataset containing the lines of JSON
|
||||
submitted. Useful for debugging.
|
||||
|
||||
@version VIYA V.03.04
|
||||
@author Allan Bowe, source: https://github.com/sasjs/core
|
||||
|
||||
<h4> SAS Macros </h4>
|
||||
@li mp_abort.sas
|
||||
@li mf_getplatform.sas
|
||||
@li mf_getuniquefileref.sas
|
||||
@li mf_getuniquelibref.sas
|
||||
@li mf_loc.sas
|
||||
@li mf_getquotedstr.sas
|
||||
@li mf_getuser.sas
|
||||
@li mp_abort.sas
|
||||
|
||||
**/
|
||||
|
||||
%macro mv_registerclient(client_id=
|
||||
,client_secret=
|
||||
,consul_token=0
|
||||
,client_name=DEFAULT
|
||||
,scopes=openid
|
||||
,grant_type=authorization_code|refresh_token
|
||||
@@ -92,33 +99,41 @@
|
||||
,refresh_token_validity=DEFAULT
|
||||
,outjson=_null_
|
||||
);
|
||||
%local consul_token fname1 fname2 fname3 libref access_token url tokloc;
|
||||
%local fname1 fname2 fname3 libref access_token url tokloc msg;
|
||||
|
||||
%if client_name=DEFAULT %then %let client_name=
|
||||
Generated by %mf_getuser() on %sysfunc(datetime(),datetime19.) using SASjs;
|
||||
Generated by %mf_getuser() (&sysuserid) on %sysfunc(datetime(),datetime19.
|
||||
) using SASjs;
|
||||
|
||||
options noquotelenmax;
|
||||
/* first, get consul token needed to get client id / secret */
|
||||
%let tokloc=/etc/SASSecurityCertificateFramework/tokens/consul/default;
|
||||
%let tokloc=%mf_loc(VIYACONFIG)&tokloc/client.token;
|
||||
|
||||
%if "&consul_token"="0" %then %do;
|
||||
/* first, get consul token needed to get client id / secret */
|
||||
%let tokloc=/etc/SASSecurityCertificateFramework/tokens/consul/default;
|
||||
%let tokloc=%mf_loc(VIYACONFIG)&tokloc/client.token;
|
||||
|
||||
%mp_abort(iftrue=(%sysfunc(fileexist(&tokloc))=0)
|
||||
,mac=&sysmacroname
|
||||
,msg=%str(Unable to access the consul token at &tokloc)
|
||||
)
|
||||
%if %sysfunc(fileexist(&tokloc))=0 %then %do;
|
||||
%let msg=Unable to access the consul token at &tokloc;
|
||||
%put &sysmacroname: &msg;
|
||||
%put Try passing the value in the consul= macro parameter;
|
||||
%put See docs: https://core.sasjs.io/mv__registerclient_8sas.html;
|
||||
%mp_abort(mac=mv_registerclient,msg=%str(&msg))
|
||||
%end;
|
||||
|
||||
%let consul_token=0;
|
||||
data _null_;
|
||||
infile "&tokloc";
|
||||
input token:$64.;
|
||||
call symputx('consul_token',token);
|
||||
run;
|
||||
data _null_;
|
||||
infile "&tokloc";
|
||||
input token:$64.;
|
||||
call symputx('consul_token',token);
|
||||
run;
|
||||
|
||||
%mp_abort(iftrue=("&consul_token"="0")
|
||||
,mac=&sysmacroname
|
||||
,msg=%str(Unable to source the consul token from &tokloc)
|
||||
)
|
||||
%if "&consul_token"="0" %then %do;
|
||||
%put &sysmacroname: Unable to source the consul token from &tokloc;
|
||||
%put It seems your account (&sysuserid) does not have admin rights;
|
||||
%put Please speak with your platform adminstrator;
|
||||
%put Docs: https://core.sasjs.io/mv__registerclient_8sas.html;
|
||||
%abort;
|
||||
%end;
|
||||
%end;
|
||||
|
||||
%local base_uri; /* location of rest apis */
|
||||
%let base_uri=%mf_getplatform(VIYARESTAPI);
|
||||
@@ -131,6 +146,9 @@ proc http method='POST' out=&fname1
|
||||
headers "X-Consul-Token"="&consul_token";
|
||||
run;
|
||||
|
||||
%put &=SYS_PROCHTTP_STATUS_CODE;
|
||||
%put &=SYS_PROCHTTP_STATUS_PHRASE;
|
||||
|
||||
%let libref=%mf_getuniquelibref();
|
||||
libname &libref JSON fileref=&fname1;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user