mirror of
https://github.com/sasjs/core.git
synced 2025-12-11 06:24:35 +00:00
Compare commits
13 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
117503f214 | ||
|
|
5cc5fae750 | ||
|
|
d93032e1a9 | ||
|
|
507557b2cb | ||
|
|
ee5c3c185a | ||
|
|
e5592a2eb2 | ||
|
|
59200a6e73 | ||
|
|
f468f60ae1 | ||
|
|
9f60d827b6 | ||
|
|
5c936ddb65 | ||
|
|
d0bde62594 | ||
|
|
ada9192337 | ||
|
|
6161f588a9 |
338
all.sas
338
all.sas
@@ -312,13 +312,25 @@ https://github.com/yabwon/SAS_PACKAGES/blob/main/packages/baseplus.md#functionex
|
||||
|
||||
%macro mf_getapploc(pgm);
|
||||
%if "&pgm"="" %then %do;
|
||||
%put &sysmacroname: No value provided;
|
||||
%return;
|
||||
%if %symexist(_program) %then %let pgm=&_program;
|
||||
%else %do;
|
||||
%put &sysmacroname: No value provided and no _program variable available;
|
||||
%return;
|
||||
%end;
|
||||
%end;
|
||||
%local root;
|
||||
|
||||
/**
|
||||
* move up two levels to avoid matches on subfolder or service name
|
||||
* First check we are not in the tests/macros folder (which has no subfolders)
|
||||
*/
|
||||
%if %index(&pgm,/tests/macros/) %then %do;
|
||||
%let root=%substr(&pgm,1,%index(&pgm,/tests/macros)-1);
|
||||
&root
|
||||
%return;
|
||||
%end;
|
||||
|
||||
/**
|
||||
* Next, move up two levels to avoid matches on subfolder or service name
|
||||
*/
|
||||
%let root=%substr(&pgm,1,%length(&pgm)-%length(%scan(&pgm,-1,/))-1);
|
||||
%let root=%substr(&root,1,%length(&root)-%length(%scan(&root,-1,/))-1);
|
||||
@@ -1630,24 +1642,47 @@ Usage:
|
||||
recognise this and fetch the log of the parent session instead)
|
||||
@li STP environments must finish cleanly to avoid the log being sent to
|
||||
_webout. To assist with this, we also run stpsrvset('program error', 0)
|
||||
and set SYSCC=0. For 9.4m3 we take a unique approach - we open a macro
|
||||
but don't close it! This provides a graceful abort, EXCEPT when called
|
||||
called within a %include within a macro (and that macro contains additional
|
||||
logic). See mp_abort.test.nofix.sas for the example case.
|
||||
If you know of another way to gracefully abort a 9.4m3 STP session, we'd
|
||||
love to hear about it!
|
||||
and set SYSCC=0. We take a unique "soft abort" approach - we open a macro
|
||||
but don't close it! This works everywhere EXCEPT inside a \%include inside
|
||||
a macro. For that, we recommend you use mp_include.sas to perform the
|
||||
include, and then call \%mp_abort(mode=INCLUDE) from the source program (ie,
|
||||
OUTSIDE of the top-parent macro).
|
||||
|
||||
|
||||
@param mac= to contain the name of the calling macro
|
||||
@param msg= message to be returned
|
||||
@param iftrue= supply a condition under which the macro should be executed.
|
||||
@param errds= (work.mp_abort_errds) There is no clean way to end a process
|
||||
within a %include called within a macro. Furthermore, there is no way to
|
||||
test if a macro is called within a %include. To handle this particular
|
||||
scenario, the %include should be switched for the mp_include.sas macro.
|
||||
This provides an indicator that we are running a macro within a \%include
|
||||
(`_SYSINCLUDEFILEDEVICE`) and allows us to provide a dataset with the abort
|
||||
values (msg, mac).
|
||||
We can then run an abort cancel FILE to stop the include running, and pass
|
||||
the dataset back to the calling program to run a regular \%mp_abort().
|
||||
The dataset will contain the following fields:
|
||||
@li iftrue (1=1)
|
||||
@li msg (the message)
|
||||
@li mac (the mac param)
|
||||
|
||||
@version 9.4M3
|
||||
@param mode= (REGULAR) If mode=INCLUDE then the &errds dataset is checked for
|
||||
an abort status.
|
||||
Valid values:
|
||||
@li REGULAR (default)
|
||||
@li INCLUDE
|
||||
|
||||
<h4> Related Macros </h4>
|
||||
@li mp_include.sas
|
||||
|
||||
@version 9.4
|
||||
@author Allan Bowe
|
||||
@cond
|
||||
**/
|
||||
|
||||
%macro mp_abort(mac=mp_abort.sas, type=, msg=, iftrue=%str(1=1)
|
||||
, errds=work.mp_abort_errds
|
||||
, mode=REGULAR
|
||||
)/*/STORE SOURCE*/;
|
||||
|
||||
%global sysprocessmode sysprocessname;
|
||||
@@ -1658,9 +1693,38 @@ Usage:
|
||||
%if %length(&mac)>0 %then %put NOTE- called by &mac;
|
||||
%put NOTE - &msg;
|
||||
|
||||
%if %symexist(_SYSINCLUDEFILEDEVICE) %then %do;
|
||||
%if "*&_SYSINCLUDEFILEDEVICE*" ne "**" %then %do;
|
||||
data &errds;
|
||||
iftrue='1=1';
|
||||
length mac $100 msg $5000;
|
||||
mac=symget('mac');
|
||||
msg=symget('msg');
|
||||
run;
|
||||
data _null_;
|
||||
abort cancel FILE;
|
||||
run;
|
||||
%return;
|
||||
%end;
|
||||
%end;
|
||||
|
||||
/* Stored Process Server web app context */
|
||||
%if %symexist(_metaperson) or "&SYSPROCESSNAME "="Compute Server " %then %do;
|
||||
%if %symexist(_metaperson)
|
||||
or "&SYSPROCESSNAME "="Compute Server "
|
||||
or &mode=INCLUDE
|
||||
%then %do;
|
||||
options obs=max replace nosyntaxcheck mprint;
|
||||
%if &mode=INCLUDE %then %do;
|
||||
data _null_;
|
||||
set &errds;
|
||||
call symputx('iftrue',iftrue,'l');
|
||||
call symputx('mac',mac,'l');
|
||||
call symputx('msg',msg,'l');
|
||||
putlog (_all_)(=);
|
||||
run;
|
||||
%if (&iftrue)=0 %then %return;
|
||||
%end;
|
||||
|
||||
/* extract log errs / warns, if exist */
|
||||
%local logloc logline;
|
||||
%global logmsg; /* capture global messages */
|
||||
@@ -1747,7 +1811,9 @@ Usage:
|
||||
put ',"_PROGRAM" : ' _PROGRAM ;
|
||||
put ",""SYSCC"" : ""&syscc"" ";
|
||||
put ",""SYSERRORTEXT"" : ""&syserrortext"" ";
|
||||
put ",""SYSHOSTNAME"" : ""&syshostname"" ";
|
||||
put ",""SYSJOBID"" : ""&sysjobid"" ";
|
||||
put ",""SYSSITE"" : ""&syssite"" ";
|
||||
sysvlong=quote(trim(symget('sysvlong')));
|
||||
put ',"SYSVLONG" : ' sysvlong;
|
||||
put ",""SYSWARNINGTEXT"" : ""&syswarningtext"" ";
|
||||
@@ -1764,11 +1830,22 @@ Usage:
|
||||
rc=stpsrvset('program error', 0);
|
||||
call symputx("syscc",0,"g");
|
||||
run;
|
||||
%if "%substr(&sysvlong.xxxxxxxxx,1,9)" ne "9.04.01M3" %then %do;
|
||||
%put NOTE: Ending SAS session due to:;
|
||||
%put NOTE- &msg;
|
||||
endsas;
|
||||
%end;
|
||||
/**
|
||||
* endsas kills 9.4m3 deployments by orphaning multibridges.
|
||||
* Abort variants are ungraceful (non zero return code)
|
||||
* This approach lets SAS run silently until the end :-)
|
||||
* Caution - fails when called within a %include within a macro
|
||||
* Use mp_include() to handle this.
|
||||
*/
|
||||
filename skip temp;
|
||||
data _null_;
|
||||
file skip;
|
||||
put '%macro skip();';
|
||||
comment '%mend skip; -> fix lint ';
|
||||
put '%macro skippy();';
|
||||
comment '%mend skippy; -> fix lint ';
|
||||
run;
|
||||
%inc skip;
|
||||
%end;
|
||||
%else %if "&sysprocessmode " = "SAS Compute Server " %then %do;
|
||||
/* endsas kills the session making it harder to fetch results */
|
||||
@@ -1784,24 +1861,6 @@ Usage:
|
||||
abort cancel nolist;
|
||||
run;
|
||||
%end;
|
||||
%else %if "%substr(&sysvlong.xxxxxxxxx,1,9)" = "9.04.01M3" %then %do;
|
||||
/**
|
||||
* endsas kills 9.4m3 deployments by orphaning multibridges.
|
||||
* Abort variants are ungraceful (non zero return code)
|
||||
* This approach lets SAS run silently until the end :-)
|
||||
* Caution - fails when called within a %include within a macro
|
||||
* See tests/mp_abort.test.1 for an example case.
|
||||
*/
|
||||
filename skip temp;
|
||||
data _null_;
|
||||
file skip;
|
||||
put '%macro skip();';
|
||||
comment '%mend skip; -> fix lint ';
|
||||
put '%macro skippy();';
|
||||
comment '%mend skippy; -> fix lint ';
|
||||
run;
|
||||
%inc skip;
|
||||
%end;
|
||||
%else %do;
|
||||
%abort cancel;
|
||||
%end;
|
||||
@@ -2401,10 +2460,29 @@ run;
|
||||
|
||||
%mp_binarycopy(inloc="/home/me/blah.txt", outref=_webout)
|
||||
|
||||
@param inloc full, quoted "path/and/filename.ext" of the object to be copied
|
||||
@param outloc full, quoted "path/and/filename.ext" of object to be created
|
||||
@param inref can override default input fileref to avoid naming clash
|
||||
@param outref an override default output fileref to avoid naming clash
|
||||
To append to a file, use the mode option, eg:
|
||||
|
||||
filename tmp1 temp;
|
||||
filename tmp2 temp;
|
||||
data _null_;
|
||||
file tmp1;
|
||||
put 'stacking';
|
||||
run;
|
||||
|
||||
%mp_binarycopy(inref=tmp1, outref=tmp2, mode=APPEND)
|
||||
%mp_binarycopy(inref=tmp1, outref=tmp2, mode=APPEND)
|
||||
|
||||
|
||||
@param [in] inloc quoted "path/and/filename.ext" of the file to be copied
|
||||
@param [out] outloc quoted "path/and/filename.ext" of the file to be created
|
||||
@param [in] inref (____in) If provided, this fileref will take precedence over
|
||||
the `inloc` parameter
|
||||
@param [out] outref (____in) If provided, this fileref will take precedence
|
||||
over the `outloc` parameter. It must already exist!
|
||||
@param [in] mode (CREATE) Valid values:
|
||||
@li CREATE - Create the file (even if it already exists)
|
||||
@li APPEND - Append to the file (don't overwrite)
|
||||
|
||||
@returns nothing
|
||||
|
||||
@version 9.2
|
||||
@@ -2416,20 +2494,29 @@ run;
|
||||
,outloc= /* full path and filename of object to be created */
|
||||
,inref=____in /* override default to use own filerefs */
|
||||
,outref=____out /* override default to use own filerefs */
|
||||
,mode=CREATE
|
||||
)/*/STORE SOURCE*/;
|
||||
%local mod outmode;
|
||||
%if &mode=APPEND %then %do;
|
||||
%let mod=mod;
|
||||
%let outmode='a';
|
||||
%end;
|
||||
%else %do;
|
||||
%let outmode='o';
|
||||
%end;
|
||||
/* these IN and OUT filerefs can point to anything */
|
||||
%if &inref = ____in %then %do;
|
||||
filename &inref &inloc lrecl=1048576 ;
|
||||
%end;
|
||||
%if &outref=____out %then %do;
|
||||
filename &outref &outloc lrecl=1048576 ;
|
||||
filename &outref &outloc lrecl=1048576 &mod;
|
||||
%end;
|
||||
|
||||
/* copy the file byte-for-byte */
|
||||
data _null_;
|
||||
length filein 8 fileid 8;
|
||||
filein = fopen("&inref",'I',1,'B');
|
||||
fileid = fopen("&outref",'O',1,'B');
|
||||
fileid = fopen("&outref",&outmode,1,'B');
|
||||
rec = '20'x;
|
||||
do while(fread(filein)=0);
|
||||
rc = fget(filein,rec,1);
|
||||
@@ -3551,10 +3638,13 @@ run;
|
||||
options:
|
||||
@li SAS (default) - suitable for regular proc sql
|
||||
@li PGSQL - Used for Postgres databases
|
||||
@param [in] applydttm= (YES) If YES, any columns using datetime formats will
|
||||
be converted to native DB datetime literals
|
||||
|
||||
<h4> SAS Macros </h4>
|
||||
@li mf_existfileref.sas
|
||||
@li mf_getvarcount.sas
|
||||
@li mf_getvarformat.sas
|
||||
@li mf_getvarlist.sas
|
||||
@li mf_getvartype.sas
|
||||
|
||||
@@ -3563,6 +3653,7 @@ run;
|
||||
**/
|
||||
|
||||
%macro mp_ds2inserts(ds, outref=0,schema=0,outds=0,flavour=SAS,maxobs=max
|
||||
,applydttm=YES
|
||||
)/*/STORE SOURCE*/;
|
||||
|
||||
%if not %sysfunc(exist(&ds)) %then %do;
|
||||
@@ -3640,10 +3731,11 @@ data _null_;
|
||||
length _____str $32767;
|
||||
format _numeric_ best.;
|
||||
format _character_ ;
|
||||
%local i comma var vtype;
|
||||
%local i comma var vtype vfmt;
|
||||
%do i=1 %to %sysfunc(countw(&varlist));
|
||||
%let var=%scan(&varlist,&i);
|
||||
%let vtype=%mf_getvartype(&ds,&var);
|
||||
%let vfmt=%upcase(%mf_getvarformat(&ds,&var,force=1));
|
||||
%if &i=1 %then %do;
|
||||
%if &flavour=SAS %then %do;
|
||||
put "insert into &schema.&outds set ";
|
||||
@@ -3673,7 +3765,13 @@ data _null_;
|
||||
%end;
|
||||
%else %if &flavour=PGSQL %then %do;
|
||||
if missing(&var) then put 'NULL';
|
||||
else put &var;
|
||||
%if &applydttm=YES and "%substr(&vfmt.xxxxxxxx,1,8)"="DATETIME"
|
||||
%then %do;
|
||||
else put "TIMESTAMP '" &var E8601DT25.6 "'";
|
||||
%end;
|
||||
%else %do;
|
||||
else put &var;
|
||||
%end;
|
||||
%end;
|
||||
%end;
|
||||
%else %do;
|
||||
@@ -4076,7 +4174,7 @@ run;
|
||||
|
||||
data &outds;
|
||||
if &sqlrc or &syscc or &syserr then do;
|
||||
REASON_CD='VALIDATION_ERROR: '!!
|
||||
REASON_CD='VALIDATION_ERR'!!'OR: '!!
|
||||
coalescec(symget('SYSERRORTEXT'),symget('SYSWARNINGTEXT'));
|
||||
output;
|
||||
end;
|
||||
@@ -5346,12 +5444,115 @@ create table &outds (rename=(
|
||||
run;
|
||||
%end;
|
||||
%mend mp_hashdataset;/**
|
||||
@file
|
||||
@brief Performs a wrapped \%include
|
||||
@details This macro wrapper is necessary if you need your included code to
|
||||
know that it is being \%included.
|
||||
|
||||
If you are using %include in a regular program, you could make use of the
|
||||
following macro variables:
|
||||
|
||||
@li SYSINCLUDEFILEDEVICE
|
||||
@li SYSINCLUDEFILEDIR
|
||||
@li SYSINCLUDEFILEFILEREF
|
||||
@li SYSINCLUDEFILENAME
|
||||
|
||||
However these variables are NOT available inside a macro, as documented here:
|
||||
https://documentation.sas.com/doc/en/pgmsascdc/9.4_3.5/mcrolref/n1j5tcc0n2xczyn1kg1o0606gsv9.htm
|
||||
|
||||
This macro can be used in place of the %include statement, and will insert
|
||||
the following (equivalent) global variables:
|
||||
|
||||
@li _SYSINCLUDEFILEDEVICE
|
||||
@li _SYSINCLUDEFILEDIR
|
||||
@li _SYSINCLUDEFILEFILEREF
|
||||
@li _SYSINCLUDEFILENAME
|
||||
|
||||
These can be used whenever testing _within a macro_. Outside of the macro,
|
||||
the regular automatic variables will still be available (thanks to a
|
||||
concatenated file list in the include statement).
|
||||
|
||||
Example usage:
|
||||
|
||||
filename example temp;
|
||||
data _null_;
|
||||
file example;
|
||||
put '%macro test();';
|
||||
put '%put &=_SYSINCLUDEFILEFILEREF;';
|
||||
put '%put &=SYSINCLUDEFILEFILEREF;';
|
||||
put '%mend; %test()';
|
||||
put '%put &=SYSINCLUDEFILEFILEREF;';
|
||||
run;
|
||||
%mp_include(example)
|
||||
|
||||
@param [in] fileref The fileref of the file to be included. Must be provided.
|
||||
@param [in] prefix= (_) The prefix to apply to the global variables.
|
||||
@param [in] opts= (SOURCE2) The options to apply to the %inc statement
|
||||
@param [in] errds= (work.mp_abort_errds) There is no clean way to end a
|
||||
process within a %include called within a macro. Furthermore, there is no
|
||||
way to test if a macro is called within a %include. To handle this
|
||||
particular scenario, the %mp_abort() macro will test for the existence of
|
||||
the `_SYSINCLUDEFILEDEVICE` variable and return the outputs (msg,mac) inside
|
||||
this dataset.
|
||||
It will then run an abort cancel FILE to stop the include running, and pass
|
||||
the dataset back.
|
||||
NOTE - it is NOT possible to read this dataset as part of _this_ macro -
|
||||
when running abort cancel FILE, ALL macros are closed, so instead it is
|
||||
necessary to invoke "%mp_abort(mode=INCLUDE)" OUTSIDE of any macro wrappers.
|
||||
|
||||
|
||||
@version 9.4
|
||||
@author Allan Bowe
|
||||
|
||||
<h4> SAS Macros </h4>
|
||||
@li mf_getuniquefileref.sas
|
||||
@li mp_abort.sas
|
||||
|
||||
**/
|
||||
|
||||
%macro mp_include(fileref
|
||||
,prefix=_
|
||||
,opts=SOURCE2
|
||||
,errds=work.mp_abort_errds
|
||||
)/*/STORE SOURCE*/;
|
||||
|
||||
/* prepare precode */
|
||||
%local tempref;
|
||||
%let tempref=%mf_getuniquefileref();
|
||||
data _null_;
|
||||
file &tempref;
|
||||
set sashelp.vextfl(where=(fileref="%upcase(&fileref)"));
|
||||
put '%let _SYSINCLUDEFILEDEVICE=' xengine ';';
|
||||
name=scan(xpath,-1,'/\');
|
||||
put '%let _SYSINCLUDEFILENAME=' name ';';
|
||||
path=subpad(xpath,1,length(xpath)-length(name)-1);
|
||||
put '%let _SYSINCLUDEFILEDIR=' path ';';
|
||||
put '%let _SYSINCLUDEFILEFILEREF=' "&fileref;";
|
||||
run;
|
||||
|
||||
/* prepare the errds */
|
||||
data &errds;
|
||||
length msg mac $1000;
|
||||
iftrue='1=0';
|
||||
run;
|
||||
|
||||
/* include the include */
|
||||
%inc &tempref &fileref/&opts;
|
||||
|
||||
%mp_abort(iftrue= (&syscc ne 0)
|
||||
,mac=%str(&_SYSINCLUDEFILEDIR/&_SYSINCLUDEFILENAME)
|
||||
,msg=%str(syscc=&syscc after executing &_SYSINCLUDEFILENAME)
|
||||
)
|
||||
|
||||
filename &tempref clear;
|
||||
|
||||
%mend mp_include;/**
|
||||
@file mp_jsonout.sas
|
||||
@brief Writes JSON in SASjs format to a fileref
|
||||
@details PROC JSON is faster but will produce errs like the ones below if
|
||||
special chars are encountered.
|
||||
|
||||
> ERROR: Some code points did not transcode.
|
||||
> (ERR)OR: Some code points did not transcode.
|
||||
|
||||
> An object or array close is not valid at this point in the JSON text.
|
||||
|
||||
@@ -5685,6 +5886,8 @@ select distinct lowcase(memname)
|
||||
@param [out] outref= Output fileref in which to create the insert statements.
|
||||
If it exists, it will be appended to, otherwise it will be created.
|
||||
@param [out] schema= (0) The schema of the target database, or the libref.
|
||||
@param [in] applydttm= (YES) If YES, any columns using datetime formats will
|
||||
be converted to native DB datetime literals
|
||||
|
||||
@version 9.2
|
||||
@author Allan Bowe
|
||||
@@ -5695,6 +5898,7 @@ select distinct lowcase(memname)
|
||||
,outref=0
|
||||
,schema=0
|
||||
,maxobs=max
|
||||
,applydttm=YES
|
||||
)/*/STORE SOURCE*/;
|
||||
|
||||
/* Find the tables */
|
||||
@@ -5724,6 +5928,7 @@ select distinct lowcase(memname)
|
||||
,outds=&ds
|
||||
,flavour=&flavour
|
||||
,maxobs=&maxobs
|
||||
,applydttm=&applydttm
|
||||
)
|
||||
%end;
|
||||
|
||||
@@ -15382,24 +15587,27 @@ filename &fname1 clear;
|
||||
libname &libref1 clear;
|
||||
*/
|
||||
%mend mv_getclients;/**
|
||||
@file mv_getfoldermembers.sas
|
||||
@brief Gets a list of folders (and ids) for a given root
|
||||
@details Works for both root level and below, oauth or password. Default is
|
||||
oauth, and the token is expected in a global ACCESS_TOKEN variable.
|
||||
@file
|
||||
@brief Gets a list of folder members (and ids) for a given root
|
||||
@details Returns all members for a particular Viya folder. Works at both root
|
||||
level and below, and results are created in an output dataset.
|
||||
|
||||
%mv_getfoldermembers(root=/Public)
|
||||
%mv_getfoldermembers(root=/Public, outds=work.mymembers)
|
||||
|
||||
|
||||
@param root= The path for which to return the list of folders
|
||||
@param outds= The output dataset to create (default is work.mv_getfolders). Format:
|
||||
@param [in] root= (/) The path for which to return the list of folders
|
||||
@param [out] outds= (work.mv_getfolders) The output dataset to create. Format:
|
||||
|ordinal_root|ordinal_items|creationTimeStamp| modifiedTimeStamp|createdBy|modifiedBy|id| uri|added| type|name|description|
|
||||
|---|---|---|---|---|---|---|---|---|---|---|---|
|
||||
|1|1|2021-05-25T11:15:04.204Z|2021-05-25T11:15:04.204Z|allbow|allbow|4f1e3945-9655-462b-90f2-c31534b3ca47|/folders/folders/ed701ff3-77e8-468d-a4f5-8c43dec0fd9e|2021-05-25T11:15:04.212Z|child|my_folder_name|My folder Description|
|
||||
|
||||
@param access_token_var= The global macro variable to contain the access token
|
||||
@param grant_type= valid values are "password" or "authorization_code" (unquoted).
|
||||
The default is authorization_code.
|
||||
|
||||
@param [in] access_token_var= (ACCESS_TOKEN) The global macro variable to
|
||||
contain the access token
|
||||
@param [in] grant_type= (sas_services) Valid values are:
|
||||
@li password
|
||||
@li authorization_code
|
||||
@li detect
|
||||
@li sas_services
|
||||
|
||||
@version VIYA V.03.04
|
||||
@author Allan Bowe, source: https://github.com/sasjs/core
|
||||
@@ -15411,6 +15619,12 @@ libname &libref1 clear;
|
||||
@li mf_getuniquelibref.sas
|
||||
@li mf_isblank.sas
|
||||
|
||||
<h4> Related Macros </h4>
|
||||
@li mv_createfolder.sas
|
||||
@li mv_deletefoldermember.sas
|
||||
@li mv_deleteviyafolder.sas
|
||||
@li mv_getfoldermembers.test.sas
|
||||
|
||||
**/
|
||||
|
||||
%macro mv_getfoldermembers(root=/
|
||||
@@ -15450,7 +15664,7 @@ options noquotelenmax;
|
||||
%if "&root"="/" %then %do;
|
||||
/* if root just list root folders */
|
||||
proc http method='GET' out=&fname1 &oauth_bearer
|
||||
url="&base_uri/folders/rootFolders";
|
||||
url="&base_uri/folders/rootFolders?limit=1000";
|
||||
%if &grant_type=authorization_code %then %do;
|
||||
headers "Authorization"="Bearer &&&access_token_var";
|
||||
%end;
|
||||
@@ -15471,13 +15685,17 @@ options noquotelenmax;
|
||||
/*data _null_;infile &fname1;input;putlog _infile_;run;*/
|
||||
libname &libref1 JSON fileref=&fname1;
|
||||
/* now get the followon link to list members */
|
||||
%local href;
|
||||
%let href=0;
|
||||
%local href cnt;
|
||||
%let cnt=0;
|
||||
data _null_;
|
||||
set &libref1..links;
|
||||
if rel='members' then call symputx('href',quote("&base_uri"!!trim(href)),'l');
|
||||
if rel='members' then do;
|
||||
url=cats("'","&base_uri",href,"?limit=10000'");
|
||||
call symputx('href',url,'l');
|
||||
call symputx('cnt',1,'l');
|
||||
end;
|
||||
run;
|
||||
%if &href=0 %then %do;
|
||||
%if &cnt=0 %then %do;
|
||||
%put NOTE:;%put NOTE- No members found in &root!!;%put NOTE-;
|
||||
%return;
|
||||
%end;
|
||||
|
||||
@@ -36,13 +36,25 @@
|
||||
|
||||
%macro mf_getapploc(pgm);
|
||||
%if "&pgm"="" %then %do;
|
||||
%put &sysmacroname: No value provided;
|
||||
%return;
|
||||
%if %symexist(_program) %then %let pgm=&_program;
|
||||
%else %do;
|
||||
%put &sysmacroname: No value provided and no _program variable available;
|
||||
%return;
|
||||
%end;
|
||||
%end;
|
||||
%local root;
|
||||
|
||||
/**
|
||||
* move up two levels to avoid matches on subfolder or service name
|
||||
* First check we are not in the tests/macros folder (which has no subfolders)
|
||||
*/
|
||||
%if %index(&pgm,/tests/macros/) %then %do;
|
||||
%let root=%substr(&pgm,1,%index(&pgm,/tests/macros)-1);
|
||||
&root
|
||||
%return;
|
||||
%end;
|
||||
|
||||
/**
|
||||
* Next, move up two levels to avoid matches on subfolder or service name
|
||||
*/
|
||||
%let root=%substr(&pgm,1,%length(&pgm)-%length(%scan(&pgm,-1,/))-1);
|
||||
%let root=%substr(&root,1,%length(&root)-%length(%scan(&root,-1,/))-1);
|
||||
|
||||
@@ -15,24 +15,47 @@
|
||||
recognise this and fetch the log of the parent session instead)
|
||||
@li STP environments must finish cleanly to avoid the log being sent to
|
||||
_webout. To assist with this, we also run stpsrvset('program error', 0)
|
||||
and set SYSCC=0. For 9.4m3 we take a unique approach - we open a macro
|
||||
but don't close it! This provides a graceful abort, EXCEPT when called
|
||||
called within a %include within a macro (and that macro contains additional
|
||||
logic). See mp_abort.test.nofix.sas for the example case.
|
||||
If you know of another way to gracefully abort a 9.4m3 STP session, we'd
|
||||
love to hear about it!
|
||||
and set SYSCC=0. We take a unique "soft abort" approach - we open a macro
|
||||
but don't close it! This works everywhere EXCEPT inside a \%include inside
|
||||
a macro. For that, we recommend you use mp_include.sas to perform the
|
||||
include, and then call \%mp_abort(mode=INCLUDE) from the source program (ie,
|
||||
OUTSIDE of the top-parent macro).
|
||||
|
||||
|
||||
@param mac= to contain the name of the calling macro
|
||||
@param msg= message to be returned
|
||||
@param iftrue= supply a condition under which the macro should be executed.
|
||||
@param errds= (work.mp_abort_errds) There is no clean way to end a process
|
||||
within a %include called within a macro. Furthermore, there is no way to
|
||||
test if a macro is called within a %include. To handle this particular
|
||||
scenario, the %include should be switched for the mp_include.sas macro.
|
||||
This provides an indicator that we are running a macro within a \%include
|
||||
(`_SYSINCLUDEFILEDEVICE`) and allows us to provide a dataset with the abort
|
||||
values (msg, mac).
|
||||
We can then run an abort cancel FILE to stop the include running, and pass
|
||||
the dataset back to the calling program to run a regular \%mp_abort().
|
||||
The dataset will contain the following fields:
|
||||
@li iftrue (1=1)
|
||||
@li msg (the message)
|
||||
@li mac (the mac param)
|
||||
|
||||
@version 9.4M3
|
||||
@param mode= (REGULAR) If mode=INCLUDE then the &errds dataset is checked for
|
||||
an abort status.
|
||||
Valid values:
|
||||
@li REGULAR (default)
|
||||
@li INCLUDE
|
||||
|
||||
<h4> Related Macros </h4>
|
||||
@li mp_include.sas
|
||||
|
||||
@version 9.4
|
||||
@author Allan Bowe
|
||||
@cond
|
||||
**/
|
||||
|
||||
%macro mp_abort(mac=mp_abort.sas, type=, msg=, iftrue=%str(1=1)
|
||||
, errds=work.mp_abort_errds
|
||||
, mode=REGULAR
|
||||
)/*/STORE SOURCE*/;
|
||||
|
||||
%global sysprocessmode sysprocessname;
|
||||
@@ -43,9 +66,38 @@
|
||||
%if %length(&mac)>0 %then %put NOTE- called by &mac;
|
||||
%put NOTE - &msg;
|
||||
|
||||
%if %symexist(_SYSINCLUDEFILEDEVICE) %then %do;
|
||||
%if "*&_SYSINCLUDEFILEDEVICE*" ne "**" %then %do;
|
||||
data &errds;
|
||||
iftrue='1=1';
|
||||
length mac $100 msg $5000;
|
||||
mac=symget('mac');
|
||||
msg=symget('msg');
|
||||
run;
|
||||
data _null_;
|
||||
abort cancel FILE;
|
||||
run;
|
||||
%return;
|
||||
%end;
|
||||
%end;
|
||||
|
||||
/* Stored Process Server web app context */
|
||||
%if %symexist(_metaperson) or "&SYSPROCESSNAME "="Compute Server " %then %do;
|
||||
%if %symexist(_metaperson)
|
||||
or "&SYSPROCESSNAME "="Compute Server "
|
||||
or &mode=INCLUDE
|
||||
%then %do;
|
||||
options obs=max replace nosyntaxcheck mprint;
|
||||
%if &mode=INCLUDE %then %do;
|
||||
data _null_;
|
||||
set &errds;
|
||||
call symputx('iftrue',iftrue,'l');
|
||||
call symputx('mac',mac,'l');
|
||||
call symputx('msg',msg,'l');
|
||||
putlog (_all_)(=);
|
||||
run;
|
||||
%if (&iftrue)=0 %then %return;
|
||||
%end;
|
||||
|
||||
/* extract log errs / warns, if exist */
|
||||
%local logloc logline;
|
||||
%global logmsg; /* capture global messages */
|
||||
@@ -132,7 +184,9 @@
|
||||
put ',"_PROGRAM" : ' _PROGRAM ;
|
||||
put ",""SYSCC"" : ""&syscc"" ";
|
||||
put ",""SYSERRORTEXT"" : ""&syserrortext"" ";
|
||||
put ",""SYSHOSTNAME"" : ""&syshostname"" ";
|
||||
put ",""SYSJOBID"" : ""&sysjobid"" ";
|
||||
put ",""SYSSITE"" : ""&syssite"" ";
|
||||
sysvlong=quote(trim(symget('sysvlong')));
|
||||
put ',"SYSVLONG" : ' sysvlong;
|
||||
put ",""SYSWARNINGTEXT"" : ""&syswarningtext"" ";
|
||||
@@ -149,11 +203,22 @@
|
||||
rc=stpsrvset('program error', 0);
|
||||
call symputx("syscc",0,"g");
|
||||
run;
|
||||
%if "%substr(&sysvlong.xxxxxxxxx,1,9)" ne "9.04.01M3" %then %do;
|
||||
%put NOTE: Ending SAS session due to:;
|
||||
%put NOTE- &msg;
|
||||
endsas;
|
||||
%end;
|
||||
/**
|
||||
* endsas kills 9.4m3 deployments by orphaning multibridges.
|
||||
* Abort variants are ungraceful (non zero return code)
|
||||
* This approach lets SAS run silently until the end :-)
|
||||
* Caution - fails when called within a %include within a macro
|
||||
* Use mp_include() to handle this.
|
||||
*/
|
||||
filename skip temp;
|
||||
data _null_;
|
||||
file skip;
|
||||
put '%macro skip();';
|
||||
comment '%mend skip; -> fix lint ';
|
||||
put '%macro skippy();';
|
||||
comment '%mend skippy; -> fix lint ';
|
||||
run;
|
||||
%inc skip;
|
||||
%end;
|
||||
%else %if "&sysprocessmode " = "SAS Compute Server " %then %do;
|
||||
/* endsas kills the session making it harder to fetch results */
|
||||
@@ -169,24 +234,6 @@
|
||||
abort cancel nolist;
|
||||
run;
|
||||
%end;
|
||||
%else %if "%substr(&sysvlong.xxxxxxxxx,1,9)" = "9.04.01M3" %then %do;
|
||||
/**
|
||||
* endsas kills 9.4m3 deployments by orphaning multibridges.
|
||||
* Abort variants are ungraceful (non zero return code)
|
||||
* This approach lets SAS run silently until the end :-)
|
||||
* Caution - fails when called within a %include within a macro
|
||||
* See tests/mp_abort.test.1 for an example case.
|
||||
*/
|
||||
filename skip temp;
|
||||
data _null_;
|
||||
file skip;
|
||||
put '%macro skip();';
|
||||
comment '%mend skip; -> fix lint ';
|
||||
put '%macro skippy();';
|
||||
comment '%mend skippy; -> fix lint ';
|
||||
run;
|
||||
%inc skip;
|
||||
%end;
|
||||
%else %do;
|
||||
%abort cancel;
|
||||
%end;
|
||||
|
||||
@@ -9,10 +9,29 @@
|
||||
|
||||
%mp_binarycopy(inloc="/home/me/blah.txt", outref=_webout)
|
||||
|
||||
@param inloc full, quoted "path/and/filename.ext" of the object to be copied
|
||||
@param outloc full, quoted "path/and/filename.ext" of object to be created
|
||||
@param inref can override default input fileref to avoid naming clash
|
||||
@param outref an override default output fileref to avoid naming clash
|
||||
To append to a file, use the mode option, eg:
|
||||
|
||||
filename tmp1 temp;
|
||||
filename tmp2 temp;
|
||||
data _null_;
|
||||
file tmp1;
|
||||
put 'stacking';
|
||||
run;
|
||||
|
||||
%mp_binarycopy(inref=tmp1, outref=tmp2, mode=APPEND)
|
||||
%mp_binarycopy(inref=tmp1, outref=tmp2, mode=APPEND)
|
||||
|
||||
|
||||
@param [in] inloc quoted "path/and/filename.ext" of the file to be copied
|
||||
@param [out] outloc quoted "path/and/filename.ext" of the file to be created
|
||||
@param [in] inref (____in) If provided, this fileref will take precedence over
|
||||
the `inloc` parameter
|
||||
@param [out] outref (____in) If provided, this fileref will take precedence
|
||||
over the `outloc` parameter. It must already exist!
|
||||
@param [in] mode (CREATE) Valid values:
|
||||
@li CREATE - Create the file (even if it already exists)
|
||||
@li APPEND - Append to the file (don't overwrite)
|
||||
|
||||
@returns nothing
|
||||
|
||||
@version 9.2
|
||||
@@ -24,20 +43,29 @@
|
||||
,outloc= /* full path and filename of object to be created */
|
||||
,inref=____in /* override default to use own filerefs */
|
||||
,outref=____out /* override default to use own filerefs */
|
||||
,mode=CREATE
|
||||
)/*/STORE SOURCE*/;
|
||||
%local mod outmode;
|
||||
%if &mode=APPEND %then %do;
|
||||
%let mod=mod;
|
||||
%let outmode='a';
|
||||
%end;
|
||||
%else %do;
|
||||
%let outmode='o';
|
||||
%end;
|
||||
/* these IN and OUT filerefs can point to anything */
|
||||
%if &inref = ____in %then %do;
|
||||
filename &inref &inloc lrecl=1048576 ;
|
||||
%end;
|
||||
%if &outref=____out %then %do;
|
||||
filename &outref &outloc lrecl=1048576 ;
|
||||
filename &outref &outloc lrecl=1048576 &mod;
|
||||
%end;
|
||||
|
||||
/* copy the file byte-for-byte */
|
||||
data _null_;
|
||||
length filein 8 fileid 8;
|
||||
filein = fopen("&inref",'I',1,'B');
|
||||
fileid = fopen("&outref",'O',1,'B');
|
||||
fileid = fopen("&outref",&outmode,1,'B');
|
||||
rec = '20'x;
|
||||
do while(fread(filein)=0);
|
||||
rc = fget(filein,rec,1);
|
||||
|
||||
@@ -25,10 +25,13 @@
|
||||
options:
|
||||
@li SAS (default) - suitable for regular proc sql
|
||||
@li PGSQL - Used for Postgres databases
|
||||
@param [in] applydttm= (YES) If YES, any columns using datetime formats will
|
||||
be converted to native DB datetime literals
|
||||
|
||||
<h4> SAS Macros </h4>
|
||||
@li mf_existfileref.sas
|
||||
@li mf_getvarcount.sas
|
||||
@li mf_getvarformat.sas
|
||||
@li mf_getvarlist.sas
|
||||
@li mf_getvartype.sas
|
||||
|
||||
@@ -37,6 +40,7 @@
|
||||
**/
|
||||
|
||||
%macro mp_ds2inserts(ds, outref=0,schema=0,outds=0,flavour=SAS,maxobs=max
|
||||
,applydttm=YES
|
||||
)/*/STORE SOURCE*/;
|
||||
|
||||
%if not %sysfunc(exist(&ds)) %then %do;
|
||||
@@ -114,10 +118,11 @@ data _null_;
|
||||
length _____str $32767;
|
||||
format _numeric_ best.;
|
||||
format _character_ ;
|
||||
%local i comma var vtype;
|
||||
%local i comma var vtype vfmt;
|
||||
%do i=1 %to %sysfunc(countw(&varlist));
|
||||
%let var=%scan(&varlist,&i);
|
||||
%let vtype=%mf_getvartype(&ds,&var);
|
||||
%let vfmt=%upcase(%mf_getvarformat(&ds,&var,force=1));
|
||||
%if &i=1 %then %do;
|
||||
%if &flavour=SAS %then %do;
|
||||
put "insert into &schema.&outds set ";
|
||||
@@ -147,7 +152,13 @@ data _null_;
|
||||
%end;
|
||||
%else %if &flavour=PGSQL %then %do;
|
||||
if missing(&var) then put 'NULL';
|
||||
else put &var;
|
||||
%if &applydttm=YES and "%substr(&vfmt.xxxxxxxx,1,8)"="DATETIME"
|
||||
%then %do;
|
||||
else put "TIMESTAMP '" &var E8601DT25.6 "'";
|
||||
%end;
|
||||
%else %do;
|
||||
else put &var;
|
||||
%end;
|
||||
%end;
|
||||
%end;
|
||||
%else %do;
|
||||
|
||||
@@ -78,7 +78,7 @@ run;
|
||||
|
||||
data &outds;
|
||||
if &sqlrc or &syscc or &syserr then do;
|
||||
REASON_CD='VALIDATION_ERROR: '!!
|
||||
REASON_CD='VALIDATION_ERR'!!'OR: '!!
|
||||
coalescec(symget('SYSERRORTEXT'),symget('SYSWARNINGTEXT'));
|
||||
output;
|
||||
end;
|
||||
|
||||
104
base/mp_include.sas
Normal file
104
base/mp_include.sas
Normal file
@@ -0,0 +1,104 @@
|
||||
/**
|
||||
@file
|
||||
@brief Performs a wrapped \%include
|
||||
@details This macro wrapper is necessary if you need your included code to
|
||||
know that it is being \%included.
|
||||
|
||||
If you are using %include in a regular program, you could make use of the
|
||||
following macro variables:
|
||||
|
||||
@li SYSINCLUDEFILEDEVICE
|
||||
@li SYSINCLUDEFILEDIR
|
||||
@li SYSINCLUDEFILEFILEREF
|
||||
@li SYSINCLUDEFILENAME
|
||||
|
||||
However these variables are NOT available inside a macro, as documented here:
|
||||
https://documentation.sas.com/doc/en/pgmsascdc/9.4_3.5/mcrolref/n1j5tcc0n2xczyn1kg1o0606gsv9.htm
|
||||
|
||||
This macro can be used in place of the %include statement, and will insert
|
||||
the following (equivalent) global variables:
|
||||
|
||||
@li _SYSINCLUDEFILEDEVICE
|
||||
@li _SYSINCLUDEFILEDIR
|
||||
@li _SYSINCLUDEFILEFILEREF
|
||||
@li _SYSINCLUDEFILENAME
|
||||
|
||||
These can be used whenever testing _within a macro_. Outside of the macro,
|
||||
the regular automatic variables will still be available (thanks to a
|
||||
concatenated file list in the include statement).
|
||||
|
||||
Example usage:
|
||||
|
||||
filename example temp;
|
||||
data _null_;
|
||||
file example;
|
||||
put '%macro test();';
|
||||
put '%put &=_SYSINCLUDEFILEFILEREF;';
|
||||
put '%put &=SYSINCLUDEFILEFILEREF;';
|
||||
put '%mend; %test()';
|
||||
put '%put &=SYSINCLUDEFILEFILEREF;';
|
||||
run;
|
||||
%mp_include(example)
|
||||
|
||||
@param [in] fileref The fileref of the file to be included. Must be provided.
|
||||
@param [in] prefix= (_) The prefix to apply to the global variables.
|
||||
@param [in] opts= (SOURCE2) The options to apply to the %inc statement
|
||||
@param [in] errds= (work.mp_abort_errds) There is no clean way to end a
|
||||
process within a %include called within a macro. Furthermore, there is no
|
||||
way to test if a macro is called within a %include. To handle this
|
||||
particular scenario, the %mp_abort() macro will test for the existence of
|
||||
the `_SYSINCLUDEFILEDEVICE` variable and return the outputs (msg,mac) inside
|
||||
this dataset.
|
||||
It will then run an abort cancel FILE to stop the include running, and pass
|
||||
the dataset back.
|
||||
NOTE - it is NOT possible to read this dataset as part of _this_ macro -
|
||||
when running abort cancel FILE, ALL macros are closed, so instead it is
|
||||
necessary to invoke "%mp_abort(mode=INCLUDE)" OUTSIDE of any macro wrappers.
|
||||
|
||||
|
||||
@version 9.4
|
||||
@author Allan Bowe
|
||||
|
||||
<h4> SAS Macros </h4>
|
||||
@li mf_getuniquefileref.sas
|
||||
@li mp_abort.sas
|
||||
|
||||
**/
|
||||
|
||||
%macro mp_include(fileref
|
||||
,prefix=_
|
||||
,opts=SOURCE2
|
||||
,errds=work.mp_abort_errds
|
||||
)/*/STORE SOURCE*/;
|
||||
|
||||
/* prepare precode */
|
||||
%local tempref;
|
||||
%let tempref=%mf_getuniquefileref();
|
||||
data _null_;
|
||||
file &tempref;
|
||||
set sashelp.vextfl(where=(fileref="%upcase(&fileref)"));
|
||||
put '%let _SYSINCLUDEFILEDEVICE=' xengine ';';
|
||||
name=scan(xpath,-1,'/\');
|
||||
put '%let _SYSINCLUDEFILENAME=' name ';';
|
||||
path=subpad(xpath,1,length(xpath)-length(name)-1);
|
||||
put '%let _SYSINCLUDEFILEDIR=' path ';';
|
||||
put '%let _SYSINCLUDEFILEFILEREF=' "&fileref;";
|
||||
run;
|
||||
|
||||
/* prepare the errds */
|
||||
data &errds;
|
||||
length msg mac $1000;
|
||||
iftrue='1=0';
|
||||
run;
|
||||
|
||||
/* include the include */
|
||||
%inc &tempref &fileref/&opts;
|
||||
|
||||
%mp_abort(iftrue= (&syscc ne 0)
|
||||
,mac=%str(&_SYSINCLUDEFILEDIR/&_SYSINCLUDEFILENAME)
|
||||
,msg=%str(syscc=&syscc after executing &_SYSINCLUDEFILENAME)
|
||||
)
|
||||
|
||||
filename &tempref clear;
|
||||
|
||||
%mend mp_include;
|
||||
@@ -4,7 +4,7 @@
|
||||
@details PROC JSON is faster but will produce errs like the ones below if
|
||||
special chars are encountered.
|
||||
|
||||
> ERROR: Some code points did not transcode.
|
||||
> (ERR)OR: Some code points did not transcode.
|
||||
|
||||
> An object or array close is not valid at this point in the JSON text.
|
||||
|
||||
|
||||
@@ -28,6 +28,8 @@
|
||||
@param [out] outref= Output fileref in which to create the insert statements.
|
||||
If it exists, it will be appended to, otherwise it will be created.
|
||||
@param [out] schema= (0) The schema of the target database, or the libref.
|
||||
@param [in] applydttm= (YES) If YES, any columns using datetime formats will
|
||||
be converted to native DB datetime literals
|
||||
|
||||
@version 9.2
|
||||
@author Allan Bowe
|
||||
@@ -38,6 +40,7 @@
|
||||
,outref=0
|
||||
,schema=0
|
||||
,maxobs=max
|
||||
,applydttm=YES
|
||||
)/*/STORE SOURCE*/;
|
||||
|
||||
/* Find the tables */
|
||||
@@ -67,6 +70,7 @@ select distinct lowcase(memname)
|
||||
,outds=&ds
|
||||
,flavour=&flavour
|
||||
,maxobs=&maxobs
|
||||
,applydttm=&applydttm
|
||||
)
|
||||
%end;
|
||||
|
||||
|
||||
@@ -38,4 +38,12 @@
|
||||
),
|
||||
desc=Checking jobs appLoc matches,
|
||||
outds=work.test_results
|
||||
)
|
||||
|
||||
%mp_assert(
|
||||
iftrue=(
|
||||
"%mf_getapploc(/some/area/tests/macros/somemacro.sas)"="/some/area"
|
||||
),
|
||||
desc=Checking tests/macros appLoc matches (which has no subfolder),
|
||||
outds=work.test_results
|
||||
)
|
||||
99
tests/crossplatform/mp_binarycopy.test.sas
Normal file
99
tests/crossplatform/mp_binarycopy.test.sas
Normal file
@@ -0,0 +1,99 @@
|
||||
/**
|
||||
@file
|
||||
@brief Testing mp_binarycopy.sas macro
|
||||
|
||||
<h4> SAS Macros </h4>
|
||||
@li mp_binarycopy.sas
|
||||
@li mp_assert.sas
|
||||
|
||||
**/
|
||||
|
||||
|
||||
/* TEST 1 - regular file copy */
|
||||
%let string1=test1;
|
||||
filename tmp temp;
|
||||
filename myref temp;
|
||||
data _null_;
|
||||
file tmp;
|
||||
put "&string1";
|
||||
run;
|
||||
%mp_binarycopy(inref=tmp, outref=myref)
|
||||
data _null_;
|
||||
infile myref;
|
||||
input;
|
||||
put _infile_;
|
||||
call symputx('string1_check',_infile_);
|
||||
stop;
|
||||
run;
|
||||
%mp_assert(
|
||||
iftrue=("&string1"="&string1_check"),
|
||||
desc=Basic String Compare,
|
||||
outds=work.test_results
|
||||
)
|
||||
|
||||
|
||||
/* TEST 2 - File append */
|
||||
%let string2=test2;
|
||||
%let path2=%sysfunc(pathname(work))/somefile.txt;
|
||||
data _null_;
|
||||
file "&path2";
|
||||
put "&string2";
|
||||
run;
|
||||
%mp_binarycopy(inloc="&path2", outref=myref, mode=APPEND)
|
||||
data _null_;
|
||||
infile myref;
|
||||
input;
|
||||
put _infile_;
|
||||
if _n_=2 then call symputx('string2_check',_infile_);
|
||||
run;
|
||||
%mp_assert(
|
||||
iftrue=("&string2"="&string2_check"),
|
||||
desc=Append Check (file to ref),
|
||||
outds=work.test_results
|
||||
)
|
||||
|
||||
/* TEST 3 - File create (ref to existing file) */
|
||||
%let string3=test3;
|
||||
%let path3=%sysfunc(pathname(work))/somefile3.txt;
|
||||
filename tmp3 temp;
|
||||
data _null_;
|
||||
file tmp3;
|
||||
put "&string3";
|
||||
run;
|
||||
data _null_;
|
||||
file "&path3";
|
||||
put "this should not be returned";
|
||||
run;
|
||||
%mp_binarycopy(inref=tmp3, outloc="&path3")
|
||||
data _null_;
|
||||
infile "&path3";
|
||||
input;
|
||||
put _infile_;
|
||||
if _n_=1 then call symputx('string3_check',_infile_);
|
||||
run;
|
||||
%mp_assert(
|
||||
iftrue=("&string3"="&string3_check"),
|
||||
desc=Append Check (ref to existing file),
|
||||
outds=work.test_results
|
||||
)
|
||||
|
||||
/* TEST 4 - File append (ref to file) */
|
||||
%let string4=test4;
|
||||
%let string4_check=;
|
||||
filename tmp4 temp;
|
||||
data _null_;
|
||||
file tmp4;
|
||||
put "&string4";
|
||||
run;
|
||||
%mp_binarycopy(inref=tmp4, outloc="&path3",mode=APPEND)
|
||||
data _null_;
|
||||
infile "&path3";
|
||||
input;
|
||||
put _infile_;
|
||||
if _n_=2 then call symputx('string4_check',_infile_);
|
||||
run;
|
||||
%mp_assert(
|
||||
iftrue=("&string4"="&string4_check"),
|
||||
desc=Append Check (ref to file),
|
||||
outds=work.test_results
|
||||
)
|
||||
24
tests/viyaonly/mv_getfoldermembers.test.sas
Normal file
24
tests/viyaonly/mv_getfoldermembers.test.sas
Normal file
@@ -0,0 +1,24 @@
|
||||
/**
|
||||
@file
|
||||
@brief Testing mv_getfoldermembers macro
|
||||
@details Testing is performed by ensuring that the tests/macros folder
|
||||
contains more than 20 results (which also means the default was increased)
|
||||
|
||||
<h4> SAS Macros </h4>
|
||||
@li mf_getapploc.sas
|
||||
@li mp_assertdsobs.sas
|
||||
@li mv_getfoldermembers.sas
|
||||
|
||||
**/
|
||||
options mprint;
|
||||
|
||||
%mv_getfoldermembers(
|
||||
root=%mf_getapploc()/tests/macros,
|
||||
outds=work.results
|
||||
)
|
||||
|
||||
%mp_assertdsobs(work.results,
|
||||
desc=%str(Ensuring over 20 results found in %mf_getapploc()/tests/macros),
|
||||
test=ATLEAST 21,
|
||||
outds=work.test_results
|
||||
)
|
||||
@@ -1,22 +1,25 @@
|
||||
/**
|
||||
@file mv_getfoldermembers.sas
|
||||
@brief Gets a list of folders (and ids) for a given root
|
||||
@details Works for both root level and below, oauth or password. Default is
|
||||
oauth, and the token is expected in a global ACCESS_TOKEN variable.
|
||||
@file
|
||||
@brief Gets a list of folder members (and ids) for a given root
|
||||
@details Returns all members for a particular Viya folder. Works at both root
|
||||
level and below, and results are created in an output dataset.
|
||||
|
||||
%mv_getfoldermembers(root=/Public)
|
||||
%mv_getfoldermembers(root=/Public, outds=work.mymembers)
|
||||
|
||||
|
||||
@param root= The path for which to return the list of folders
|
||||
@param outds= The output dataset to create (default is work.mv_getfolders). Format:
|
||||
@param [in] root= (/) The path for which to return the list of folders
|
||||
@param [out] outds= (work.mv_getfolders) The output dataset to create. Format:
|
||||
|ordinal_root|ordinal_items|creationTimeStamp| modifiedTimeStamp|createdBy|modifiedBy|id| uri|added| type|name|description|
|
||||
|---|---|---|---|---|---|---|---|---|---|---|---|
|
||||
|1|1|2021-05-25T11:15:04.204Z|2021-05-25T11:15:04.204Z|allbow|allbow|4f1e3945-9655-462b-90f2-c31534b3ca47|/folders/folders/ed701ff3-77e8-468d-a4f5-8c43dec0fd9e|2021-05-25T11:15:04.212Z|child|my_folder_name|My folder Description|
|
||||
|
||||
@param access_token_var= The global macro variable to contain the access token
|
||||
@param grant_type= valid values are "password" or "authorization_code" (unquoted).
|
||||
The default is authorization_code.
|
||||
|
||||
@param [in] access_token_var= (ACCESS_TOKEN) The global macro variable to
|
||||
contain the access token
|
||||
@param [in] grant_type= (sas_services) Valid values are:
|
||||
@li password
|
||||
@li authorization_code
|
||||
@li detect
|
||||
@li sas_services
|
||||
|
||||
@version VIYA V.03.04
|
||||
@author Allan Bowe, source: https://github.com/sasjs/core
|
||||
@@ -28,6 +31,12 @@
|
||||
@li mf_getuniquelibref.sas
|
||||
@li mf_isblank.sas
|
||||
|
||||
<h4> Related Macros </h4>
|
||||
@li mv_createfolder.sas
|
||||
@li mv_deletefoldermember.sas
|
||||
@li mv_deleteviyafolder.sas
|
||||
@li mv_getfoldermembers.test.sas
|
||||
|
||||
**/
|
||||
|
||||
%macro mv_getfoldermembers(root=/
|
||||
@@ -67,7 +76,7 @@ options noquotelenmax;
|
||||
%if "&root"="/" %then %do;
|
||||
/* if root just list root folders */
|
||||
proc http method='GET' out=&fname1 &oauth_bearer
|
||||
url="&base_uri/folders/rootFolders";
|
||||
url="&base_uri/folders/rootFolders?limit=1000";
|
||||
%if &grant_type=authorization_code %then %do;
|
||||
headers "Authorization"="Bearer &&&access_token_var";
|
||||
%end;
|
||||
@@ -88,13 +97,17 @@ options noquotelenmax;
|
||||
/*data _null_;infile &fname1;input;putlog _infile_;run;*/
|
||||
libname &libref1 JSON fileref=&fname1;
|
||||
/* now get the followon link to list members */
|
||||
%local href;
|
||||
%let href=0;
|
||||
%local href cnt;
|
||||
%let cnt=0;
|
||||
data _null_;
|
||||
set &libref1..links;
|
||||
if rel='members' then call symputx('href',quote("&base_uri"!!trim(href)),'l');
|
||||
if rel='members' then do;
|
||||
url=cats("'","&base_uri",href,"?limit=10000'");
|
||||
call symputx('href',url,'l');
|
||||
call symputx('cnt',1,'l');
|
||||
end;
|
||||
run;
|
||||
%if &href=0 %then %do;
|
||||
%if &cnt=0 %then %do;
|
||||
%put NOTE:;%put NOTE- No members found in &root!!;%put NOTE-;
|
||||
%return;
|
||||
%end;
|
||||
|
||||
Reference in New Issue
Block a user