mirror of
https://github.com/sasjs/core.git
synced 2025-12-11 06:24:35 +00:00
Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3791cb8a2c | ||
|
|
ff82f7d75c | ||
|
|
fdd566e8ce | ||
|
|
328f8c260b | ||
|
|
029169ac80 | ||
| 66ff1de7a9 | |||
| 053290c7df | |||
| af71a5e53b |
249
all.sas
249
all.sas
@@ -442,6 +442,9 @@ options noquotelenmax;
|
||||
@version 9.2
|
||||
@author Allan Bowe
|
||||
|
||||
<h4> Related Macros </h4>
|
||||
@li mf_getxengine.sas
|
||||
|
||||
**/
|
||||
/** @cond */
|
||||
|
||||
@@ -1037,14 +1040,17 @@ options noquotelenmax;
|
||||
returns:
|
||||
> List of Variables=Name Sex Age Height Weight
|
||||
|
||||
For a seperated list of column values:
|
||||
|
||||
%put %mf_getvarlist(sashelp.class,dlm=%str(,),quote=double);
|
||||
|
||||
returns:
|
||||
> "Name","Sex","Age","Height","Weight"
|
||||
|
||||
@param libds Two part dataset (or view) reference.
|
||||
@param dlm= provide a delimiter (eg comma or space) to separate the vars
|
||||
@param quote= use either DOUBLE or SINGLE to quote the results
|
||||
@param [in] libds Two part dataset (or view) reference.
|
||||
@param [in] dlm= ( ) Provide a delimiter (eg comma or space) to separate the
|
||||
variables
|
||||
@param [in] quote= (none) use either DOUBLE or SINGLE to quote the results
|
||||
|
||||
@version 9.2
|
||||
@author Allan Bowe
|
||||
@@ -1189,6 +1195,49 @@ Usage:
|
||||
/* Return variable type */
|
||||
&vtype
|
||||
%mend;/**
|
||||
@file
|
||||
@brief Returns the engine type of a SAS fileref
|
||||
@details Queries sashelp.vextfl to get the xengine value.
|
||||
Usage:
|
||||
|
||||
filename feng temp;
|
||||
%put %mf_getxengine(feng);
|
||||
|
||||
returns:
|
||||
> TEMP
|
||||
|
||||
@param fref The fileref to check
|
||||
|
||||
@returns The XENGINE value in sashelp.vextfl or 0 if not found.
|
||||
|
||||
@version 9.2
|
||||
@author Allan Bowe
|
||||
|
||||
<h4> Related Macros </h4>
|
||||
@li mf_getengine.sas
|
||||
|
||||
**/
|
||||
|
||||
%macro mf_getxengine(fref
|
||||
)/*/STORE SOURCE*/;
|
||||
%local dsid engnum rc engine;
|
||||
|
||||
%let dsid=%sysfunc(
|
||||
open(sashelp.vextfl(where=(fileref="%upcase(&fref)")),i)
|
||||
);
|
||||
%if (&dsid ^= 0) %then %do;
|
||||
%let engnum=%sysfunc(varnum(&dsid,XENGINE));
|
||||
%let rc=%sysfunc(fetch(&dsid));
|
||||
%let engine=%sysfunc(getvarc(&dsid,&engnum));
|
||||
%* put &fref. ENGINE is &engine.;
|
||||
%let rc= %sysfunc(close(&dsid));
|
||||
%end;
|
||||
%else %let engine=0;
|
||||
|
||||
&engine
|
||||
|
||||
%mend;
|
||||
/**
|
||||
@file mf_isblank.sas
|
||||
@brief Checks whether a macro variable is empty (blank)
|
||||
@details Simply performs:
|
||||
@@ -3913,6 +3962,80 @@ create table &outds (rename=(
|
||||
%return;
|
||||
%end;
|
||||
|
||||
%mend;/**
|
||||
@file
|
||||
@brief Returns a unique hash for a dataset
|
||||
@details Ignores metadata attributes, used only to hash values. Compared
|
||||
datasets must be in the same order.
|
||||
|
||||
%mp_hashdataset(sashelp.class,outds=myhash)
|
||||
|
||||
data _null_;
|
||||
set work.myhash;
|
||||
put hashkey=;
|
||||
run;
|
||||
|
||||

|
||||
|
||||
<h4> SAS Macros </h4>
|
||||
@li mf_getattrn.sas
|
||||
@li mf_getuniquename.sas
|
||||
@li mf_getvarlist.sas
|
||||
@li mf_getvartype.sas
|
||||
|
||||
@param [in] libds dataset to hash
|
||||
@param [out] outds= (work.mf_hashdataset) The output dataset to create. This
|
||||
will contain one column (hashkey) with one observation (a hex32.
|
||||
representation of the input hash)
|
||||
|hashkey:$32.|
|
||||
|---|
|
||||
|28ABC74ABFC45F50794237BA5566E6CA|
|
||||
|
||||
@version 9.2
|
||||
@author Allan Bowe
|
||||
**/
|
||||
|
||||
%macro mp_hashdataset(
|
||||
libds,
|
||||
outds=
|
||||
)/*/STORE SOURCE*/;
|
||||
%if %mf_getattrn(&libds,NLOBS)=0 %then %do;
|
||||
%put %str(WARN)ING: Dataset &libds is empty;, or is not a dataset;
|
||||
%end;
|
||||
%else %if %mf_getattrn(&libds,NLOBS)<0 %then %do;
|
||||
%put %str(ERR)OR: Dataset &libds is not a dataset;
|
||||
%end;
|
||||
%else %do;
|
||||
%local keyvar /* roll up the md5 */
|
||||
prevkeyvar /* retain prev record md5 */
|
||||
lastvar /* last var in input ds */
|
||||
varlist var i;
|
||||
/* avoid naming conflict for hash key vars */
|
||||
%let keyvar=%mf_getuniquename();
|
||||
%let prevkeyvar=%mf_getuniquename();
|
||||
%let lastvar=%mf_getuniquename();
|
||||
%let varlist=%mf_getvarlist(&libds);
|
||||
data &outds(rename=(&keyvar=hashkey) keep=&keyvar);
|
||||
length &prevkeyvar &keyvar $32;
|
||||
retain &prevkeyvar;
|
||||
set &libds end=&lastvar;
|
||||
/* hash should include previous row */
|
||||
if _n_>1 then &keyvar=put(md5(&prevkeyvar
|
||||
/* loop every column, hashing every individual value */
|
||||
%do i=1 %to %sysfunc(countw(&varlist));
|
||||
%let var=%scan(&varlist,&i,%str( ));
|
||||
%if %mf_getvartype(&libds,&var)=C %then %do;
|
||||
!!put(md5(trim(&var)),$hex32.)
|
||||
%end;
|
||||
%else %do;
|
||||
!!put(md5(trim(put(&var*1,binary64.))),$hex32.)
|
||||
%end;
|
||||
%end;
|
||||
),$hex32.);
|
||||
&prevkeyvar=&keyvar;
|
||||
if &lastvar then output;
|
||||
run;
|
||||
%end;
|
||||
%mend;/**
|
||||
@file mp_jsonout.sas
|
||||
@brief Writes JSON in SASjs format to a fileref
|
||||
@@ -5374,60 +5497,6 @@ alter table &libds modify &var char(&len);
|
||||
|
||||
%mend;
|
||||
/**
|
||||
@file
|
||||
@brief Validates a filter clause
|
||||
@details Validates a filter to avoid SQL injection. Works by removing string
|
||||
literals, then ensuring that none of the following characters remain: &,%,;
|
||||
|
||||
|
||||
|
||||
%mp_setkeyvalue(someindex,22,type=N)
|
||||
%mp_setkeyvalue(somenewindex,somevalue)
|
||||
|
||||
<h4> SAS Macros </h4>
|
||||
@li mf_existds.sas
|
||||
|
||||
@param key Provide a key on which to perform the lookup
|
||||
@param value Provide a value
|
||||
@param type= either C or N will populate valc and valn respectively. C is
|
||||
default.
|
||||
@param libds= define the target table to hold the parameters
|
||||
|
||||
@version 9.2
|
||||
@author Allan Bowe
|
||||
@source https://github.com/sasjs/core
|
||||
|
||||
**/
|
||||
|
||||
%macro mp_setkeyvalue(key,value,type=C,libds=work.mp_setkeyvalue
|
||||
)/*/STORE SOURCE*/;
|
||||
|
||||
%if not (%mf_existds(&libds)) %then %do;
|
||||
data &libds (index=(key/unique));
|
||||
length key $64 valc $2048 valn 8 type $1;
|
||||
call missing(of _all_);
|
||||
stop;
|
||||
run;
|
||||
%end;
|
||||
|
||||
proc sql;
|
||||
delete from &libds
|
||||
where key=symget('key');
|
||||
insert into &libds
|
||||
set key=symget('key')
|
||||
%if &type=C %then %do;
|
||||
,valc=symget('value')
|
||||
,type='C'
|
||||
%end;
|
||||
%else %do;
|
||||
,valn=symgetn('value')
|
||||
,type='N'
|
||||
%end;
|
||||
;
|
||||
|
||||
quit;
|
||||
|
||||
%mend;/**
|
||||
@file
|
||||
@brief Creates a zip file
|
||||
@details For DIRECTORY usage, will ignore subfolders. For DATASET usage,
|
||||
@@ -7609,8 +7678,16 @@ data _null_;
|
||||
put '%else %if &action=OPEN %then %do; ';
|
||||
put ' /* fix encoding */ ';
|
||||
put ' OPTIONS NOBOMFILE; ';
|
||||
put ' ';
|
||||
put ' /** ';
|
||||
put ' * check engine type to avoid the below err message: ';
|
||||
put ' * > Function is only valid for filerefs using the CACHE access method. ';
|
||||
put ' */ ';
|
||||
put ' data _null_; ';
|
||||
put ' rc = stpsrv_header(''Content-type'',"text/html; encoding=utf-8"); ';
|
||||
put ' set sashelp.vextfl(where=(fileref="_WEBOUT")); ';
|
||||
put ' if xengine=''STREAM'' then do; ';
|
||||
put ' rc=stpsrv_header(''Content-type'',"text/html; encoding=utf-8"); ';
|
||||
put ' end; ';
|
||||
put ' run; ';
|
||||
put ' ';
|
||||
put ' /* setup json */ ';
|
||||
@@ -7624,16 +7701,9 @@ data _null_;
|
||||
put '%end; ';
|
||||
put ' ';
|
||||
put '%else %if &action=ARR or &action=OBJ %then %do; ';
|
||||
put ' %if &sysver=9.4 %then %do; ';
|
||||
put ' %mp_jsonout(&action,&ds,dslabel=&dslabel,fmt=&fmt ';
|
||||
put ' ,engine=PROCJSON,dbg=%str(&_debug) ';
|
||||
put ' ) ';
|
||||
put ' %end; ';
|
||||
put ' %else %do; ';
|
||||
put ' %mp_jsonout(&action,&ds,dslabel=&dslabel,fmt=&fmt ';
|
||||
put ' ,engine=DATASTEP,dbg=%str(&_debug) ';
|
||||
put ' ) ';
|
||||
put ' %end; ';
|
||||
put ' %mp_jsonout(&action,&ds,dslabel=&dslabel,fmt=&fmt ';
|
||||
put ' ,engine=DATASTEP,dbg=%str(&_debug) ';
|
||||
put ' ) ';
|
||||
put '%end; ';
|
||||
put '%else %if &action=CLOSE %then %do; ';
|
||||
put ' %if %str(&_debug) ge 131 %then %do; ';
|
||||
@@ -11078,8 +11148,16 @@ run;
|
||||
%else %if &action=OPEN %then %do;
|
||||
/* fix encoding */
|
||||
OPTIONS NOBOMFILE;
|
||||
|
||||
/**
|
||||
* check engine type to avoid the below err message:
|
||||
* > Function is only valid for filerefs using the CACHE access method.
|
||||
*/
|
||||
data _null_;
|
||||
rc = stpsrv_header('Content-type',"text/html; encoding=utf-8");
|
||||
set sashelp.vextfl(where=(fileref="_WEBOUT"));
|
||||
if xengine='STREAM' then do;
|
||||
rc=stpsrv_header('Content-type',"text/html; encoding=utf-8");
|
||||
end;
|
||||
run;
|
||||
|
||||
/* setup json */
|
||||
@@ -11093,16 +11171,9 @@ run;
|
||||
%end;
|
||||
|
||||
%else %if &action=ARR or &action=OBJ %then %do;
|
||||
%if &sysver=9.4 %then %do;
|
||||
%mp_jsonout(&action,&ds,dslabel=&dslabel,fmt=&fmt
|
||||
,engine=PROCJSON,dbg=%str(&_debug)
|
||||
)
|
||||
%end;
|
||||
%else %do;
|
||||
%mp_jsonout(&action,&ds,dslabel=&dslabel,fmt=&fmt
|
||||
,engine=DATASTEP,dbg=%str(&_debug)
|
||||
)
|
||||
%end;
|
||||
%mp_jsonout(&action,&ds,dslabel=&dslabel,fmt=&fmt
|
||||
,engine=DATASTEP,dbg=%str(&_debug)
|
||||
)
|
||||
%end;
|
||||
%else %if &action=CLOSE %then %do;
|
||||
%if %str(&_debug) ge 131 %then %do;
|
||||
@@ -11311,8 +11382,8 @@ run;
|
||||
|
||||
@param path= The full path of the folder to be created
|
||||
@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 grant_type= (authorization_code) Valid values are "password" or
|
||||
"authorization_code" (unquoted).
|
||||
|
||||
|
||||
@version VIYA V.03.04
|
||||
@@ -11341,7 +11412,6 @@ run;
|
||||
%let &access_token_var=;
|
||||
%end;
|
||||
|
||||
%put &sysmacroname: grant_type=&grant_type;
|
||||
%mp_abort(iftrue=(&grant_type ne authorization_code and &grant_type ne password
|
||||
and &grant_type ne sas_services
|
||||
)
|
||||
@@ -11387,12 +11457,15 @@ options noquotelenmax;
|
||||
%local libref1;
|
||||
%let libref1=%mf_getuniquelibref();
|
||||
libname &libref1 JSON fileref=&fname1;
|
||||
%mp_abort(iftrue=(&SYS_PROCHTTP_STATUS_CODE ne 200 and &SYS_PROCHTTP_STATUS_CODE ne 404)
|
||||
%mp_abort(
|
||||
iftrue=(
|
||||
&SYS_PROCHTTP_STATUS_CODE ne 200 and &SYS_PROCHTTP_STATUS_CODE ne 404
|
||||
)
|
||||
,mac=&sysmacroname
|
||||
,msg=%str(&SYS_PROCHTTP_STATUS_CODE &SYS_PROCHTTP_STATUS_PHRASE)
|
||||
)
|
||||
%if &SYS_PROCHTTP_STATUS_CODE=200 %then %do;
|
||||
%put &sysmacroname &newpath exists so grab the follow on link ;
|
||||
%*put &sysmacroname &newpath exists so grab the follow on link ;
|
||||
data _null_;
|
||||
set &libref1..links;
|
||||
if rel='createChild' then
|
||||
@@ -11524,7 +11597,6 @@ https://go.documentation.sas.com/?docsetId=calcontexts&docsetTarget=n1hjn8eobk5p
|
||||
%let oauth_bearer=oauth_bearer=sas_services;
|
||||
%let &access_token_var=;
|
||||
%end;
|
||||
%put &sysmacroname: grant_type=&grant_type;
|
||||
|
||||
/* initial validation checking */
|
||||
%mp_abort(iftrue=(&grant_type ne authorization_code and &grant_type ne password
|
||||
@@ -11843,7 +11915,6 @@ https://go.documentation.sas.com/?docsetId=calcontexts&docsetTarget=n1hjn8eobk5p
|
||||
%let oauth_bearer=oauth_bearer=sas_services;
|
||||
%let &access_token_var=;
|
||||
%end;
|
||||
%put &sysmacroname: grant_type=&grant_type;
|
||||
|
||||
/* initial validation checking */
|
||||
%mp_abort(iftrue=(&grant_type ne authorization_code and &grant_type ne password
|
||||
@@ -12627,7 +12698,7 @@ libname &libref1a clear;
|
||||
%let oauth_bearer=oauth_bearer=sas_services;
|
||||
%let &access_token_var=;
|
||||
%end;
|
||||
%put &sysmacroname: grant_type=&grant_type;
|
||||
|
||||
%mp_abort(iftrue=(&grant_type ne authorization_code and &grant_type ne password
|
||||
and &grant_type ne sas_services
|
||||
)
|
||||
@@ -12769,7 +12840,7 @@ libname &libref1a clear;
|
||||
%let oauth_bearer=oauth_bearer=sas_services;
|
||||
%let &access_token_var=;
|
||||
%end;
|
||||
%put &sysmacroname: grant_type=&grant_type;
|
||||
|
||||
%mp_abort(iftrue=(&grant_type ne authorization_code and &grant_type ne password
|
||||
and &grant_type ne sas_services
|
||||
)
|
||||
@@ -13195,7 +13266,7 @@ libname &libref1 clear;
|
||||
%let oauth_bearer=oauth_bearer=sas_services;
|
||||
%let &access_token_var=;
|
||||
%end;
|
||||
%put &sysmacroname: grant_type=&grant_type;
|
||||
|
||||
%mp_abort(iftrue=(&grant_type ne authorization_code and &grant_type ne password
|
||||
and &grant_type ne sas_services
|
||||
)
|
||||
|
||||
@@ -22,6 +22,9 @@
|
||||
@version 9.2
|
||||
@author Allan Bowe
|
||||
|
||||
<h4> Related Macros </h4>
|
||||
@li mf_getxengine.sas
|
||||
|
||||
**/
|
||||
/** @cond */
|
||||
|
||||
|
||||
@@ -10,14 +10,17 @@
|
||||
returns:
|
||||
> List of Variables=Name Sex Age Height Weight
|
||||
|
||||
For a seperated list of column values:
|
||||
|
||||
%put %mf_getvarlist(sashelp.class,dlm=%str(,),quote=double);
|
||||
|
||||
returns:
|
||||
> "Name","Sex","Age","Height","Weight"
|
||||
|
||||
@param libds Two part dataset (or view) reference.
|
||||
@param dlm= provide a delimiter (eg comma or space) to separate the vars
|
||||
@param quote= use either DOUBLE or SINGLE to quote the results
|
||||
@param [in] libds Two part dataset (or view) reference.
|
||||
@param [in] dlm= ( ) Provide a delimiter (eg comma or space) to separate the
|
||||
variables
|
||||
@param [in] quote= (none) use either DOUBLE or SINGLE to quote the results
|
||||
|
||||
@version 9.2
|
||||
@author Allan Bowe
|
||||
|
||||
43
base/mf_getxengine.sas
Normal file
43
base/mf_getxengine.sas
Normal file
@@ -0,0 +1,43 @@
|
||||
/**
|
||||
@file
|
||||
@brief Returns the engine type of a SAS fileref
|
||||
@details Queries sashelp.vextfl to get the xengine value.
|
||||
Usage:
|
||||
|
||||
filename feng temp;
|
||||
%put %mf_getxengine(feng);
|
||||
|
||||
returns:
|
||||
> TEMP
|
||||
|
||||
@param fref The fileref to check
|
||||
|
||||
@returns The XENGINE value in sashelp.vextfl or 0 if not found.
|
||||
|
||||
@version 9.2
|
||||
@author Allan Bowe
|
||||
|
||||
<h4> Related Macros </h4>
|
||||
@li mf_getengine.sas
|
||||
|
||||
**/
|
||||
|
||||
%macro mf_getxengine(fref
|
||||
)/*/STORE SOURCE*/;
|
||||
%local dsid engnum rc engine;
|
||||
|
||||
%let dsid=%sysfunc(
|
||||
open(sashelp.vextfl(where=(fileref="%upcase(&fref)")),i)
|
||||
);
|
||||
%if (&dsid ^= 0) %then %do;
|
||||
%let engnum=%sysfunc(varnum(&dsid,XENGINE));
|
||||
%let rc=%sysfunc(fetch(&dsid));
|
||||
%let engine=%sysfunc(getvarc(&dsid,&engnum));
|
||||
%* put &fref. ENGINE is &engine.;
|
||||
%let rc= %sysfunc(close(&dsid));
|
||||
%end;
|
||||
%else %let engine=0;
|
||||
|
||||
&engine
|
||||
|
||||
%mend;
|
||||
67
base/mp_assertdsobs.sas
Normal file
67
base/mp_assertdsobs.sas
Normal file
@@ -0,0 +1,67 @@
|
||||
/**
|
||||
@file
|
||||
@brief Asserts the number of observations in a dataset
|
||||
@details Useful in the context of writing sasjs tests. The results of the
|
||||
test are _appended_ to the &outds. table.
|
||||
|
||||
Example usage:
|
||||
|
||||
%mp_assertdsobs(sashelp.class) %* tests if any observations are present;
|
||||
|
||||
<h4> SAS Macros </h4>
|
||||
@li mf_nobs.sas
|
||||
|
||||
|
||||
@param [in] inds input dataset to test for presence of observations
|
||||
@param [in] desc= (Testing observations) The user provided test description
|
||||
@param [in] test= (HASOBS) The test to apply. Valid values are:
|
||||
@li HASOBS Test is a PASS if the input dataset has any observations
|
||||
@li EMPTY Test is a PASS if input dataset is empty
|
||||
@param [out] outds= (work.test_results) The output dataset to contain the
|
||||
results. If it does not exist, it will be created, with the following format:
|
||||
|TEST_DESCRIPTION:$256|TEST_RESULT:$4|TEST_COMMENTS:$256|
|
||||
|---|---|---|
|
||||
|User Provided description|PASS|Dataset &inds has XX obs|
|
||||
|
||||
|
||||
@version 9.2
|
||||
@author Allan Bowe
|
||||
|
||||
**/
|
||||
|
||||
%macro mp_assertdsobs(inds,
|
||||
test=HASOBS,
|
||||
desc=Testing observations,
|
||||
outds=work.test_results
|
||||
)/*/STORE SOURCE*/;
|
||||
|
||||
%local nobs;
|
||||
%let nobs=%mf_nobs(&inds);
|
||||
%let test=%upcase(&test);
|
||||
|
||||
data;
|
||||
length test_description $256 test_result $4 test_comments $256;
|
||||
test_description=symget('desc');
|
||||
test_result='FAIL';
|
||||
test_comments="&sysmacroname: Dataset &inds has &nobs observations";
|
||||
%if &test=HASOBS %then %do;
|
||||
if &nobs>0 then test_result='PASS';
|
||||
%end;
|
||||
%else %if &test=EMPTY %then %do;
|
||||
if &nobs=0 then test_result='PASS';
|
||||
%end;
|
||||
%else %do;
|
||||
test_comments="&sysmacroname: Unsatisfied test condition - &test";
|
||||
%end;
|
||||
run;
|
||||
|
||||
%local ds;
|
||||
%let ds=&syslast;
|
||||
|
||||
proc append base=&outds data=&ds;
|
||||
run;
|
||||
|
||||
proc sql;
|
||||
drop table &ds;
|
||||
|
||||
%mend;
|
||||
143
base/mp_filtercheck.sas
Normal file
143
base/mp_filtercheck.sas
Normal file
@@ -0,0 +1,143 @@
|
||||
/**
|
||||
@file
|
||||
@brief Checks an input filter table for validity
|
||||
@details Performs checks on the input table to ensure it arrives in the
|
||||
correct format. This is necessary to prevent code injection. Will update
|
||||
SYSCC to 1008 if bad records are found.
|
||||
|
||||
Used for dynamic filtering in [Data Controller for SAS®](https://datacontroller.io).
|
||||
|
||||
Usage:
|
||||
|
||||
%mp_filtercheck(work.filter,targetds=sashelp.class,outds=work.badrecords)
|
||||
|
||||
The input table should have the following format:
|
||||
|
||||
|GROUP_LOGIC:$3|SUBGROUP_LOGIC:$3|SUBGROUP_ID:8.|VARIABLE_NM:$32|OPERATOR_NM:$10|RAW_VALUE:$32767|
|
||||
|---|---|---|---|---|---|
|
||||
|AND|AND|1|AGE|=|12|
|
||||
|AND|AND|1|SEX|<=|'M'|
|
||||
|AND|OR|2|Name|NOT IN|('Jane','Alfred')|
|
||||
|AND|OR|2|Weight|>=|7|
|
||||
|
||||
Rules applied:
|
||||
|
||||
@li GROUP_LOGIC - only AND/OR
|
||||
@li SUBGROUP_LOGIC - only AND/OR
|
||||
@li SUBGROUP_ID - only integers
|
||||
@li VARIABLE_NM - must be in the target table
|
||||
@li OPERATOR_NM - only =/>/</<=/>=/BETWEEN/IN/NOT IN/NOT EQUAL/CONTAINS
|
||||
@li RAW_VALUE - no unquoted values except integers, commas and spaces.
|
||||
|
||||
@returns The &outds table containing any bad rows, plus a REASON_CD column.
|
||||
|
||||
@param [in] inds The table to be checked, with the format above
|
||||
@param [in] targetds= The target dataset against which to verify VARIABLE_NM
|
||||
@param [out] outds= The output table, which is a copy of the &inds. table
|
||||
plus a REASON_CD column, containing only bad records. If bad records found,
|
||||
the SYSCC value will be set to 1008 (general data problem). Downstream
|
||||
processes should check this table (and return code) before continuing.
|
||||
|
||||
<h4> SAS Macros </h4>
|
||||
@li mp_abort.sas
|
||||
@li mf_getvarlist.sas
|
||||
@li mf_nobs.sas
|
||||
|
||||
<h4> Related Macros </h4>
|
||||
@li mp_filtergenerate.sas
|
||||
|
||||
@version 9.3
|
||||
@author Allan Bowe
|
||||
|
||||
@todo Support date / hex / name literals and exponents in RAW_VALUE field
|
||||
**/
|
||||
|
||||
%macro mp_filtercheck(inds,targetds=,outds=work.badrecords);
|
||||
|
||||
%mp_abort(iftrue= (&syscc ne 0)
|
||||
,mac=&sysmacroname
|
||||
,msg=%str(syscc=&syscc - on macro entry)
|
||||
)
|
||||
|
||||
/**
|
||||
* Sanitise the values based on valid value lists, then strip out
|
||||
* quotes, commas, periods and spaces.
|
||||
* Only numeric values should remain
|
||||
*/
|
||||
|
||||
data &outds;
|
||||
set &inds;
|
||||
length reason_cd $32;
|
||||
|
||||
/* closed list checks */
|
||||
if GROUP_LOGIC not in ('AND','OR') then do;
|
||||
REASON_CD='GROUP_LOGIC should be either AND or OR';
|
||||
putlog REASON_CD= GROUP_LOGIC=;
|
||||
output;
|
||||
end;
|
||||
if SUBGROUP_LOGIC not in ('AND','OR') then do;
|
||||
REASON_CD='SUBGROUP_LOGIC should be either AND or OR';
|
||||
putlog REASON_CD= SUBGROUP_LOGIC=;
|
||||
output;
|
||||
end;
|
||||
if mod(SUBGROUP_ID,1) ne 0 then do;
|
||||
REASON_CD='SUBGROUP_ID should be integer';
|
||||
putlog REASON_CD= SUBGROUP_ID=;
|
||||
output;
|
||||
end;
|
||||
if upcase(VARIABLE_NM) not in
|
||||
(%upcase(%mf_getvarlist(&targetds,dlm=%str(,),quote=SINGLE)))
|
||||
then do;
|
||||
REASON_CD="VARIABLE_NM not in &targetds";
|
||||
putlog REASON_CD= VARIABLE_NM=;
|
||||
output;
|
||||
end;
|
||||
if OPERATOR_NM not in
|
||||
('=','>','<','<=','>=','BETWEEN','IN','NOT IN','NOT EQUAL','CONTAINS')
|
||||
then do;
|
||||
REASON_CD='Invalid OPERATOR_NM';
|
||||
putlog REASON_CD= OPERATOR_NM=;
|
||||
output;
|
||||
end;
|
||||
|
||||
/* special logic */
|
||||
if OPERATOR_NM='BETWEEN' then raw_value1=tranwrd(raw_value,' AND ','');
|
||||
else if OPERATOR_NM in ('IN','NOT IN') then do;
|
||||
if substr(raw_value,1,1) ne '('
|
||||
or substr(cats(reverse(raw_value)),1,1) ne ')'
|
||||
then do;
|
||||
REASON_CD='Missing brackets in RAW_VALUE';
|
||||
putlog REASON_CD= OPERATOR_NM= raw_value= raw_value1= ;
|
||||
output;
|
||||
end;
|
||||
else raw_value1=substr(raw_value,2,max(length(raw_value)-2,0));
|
||||
end;
|
||||
else raw_value1=raw_value;
|
||||
|
||||
/* remove nested literals eg '' */
|
||||
raw_value1=tranwrd(raw_value1,"''",'');
|
||||
|
||||
/* now match string literals (always single quotes) */
|
||||
raw_value2=raw_value1;
|
||||
regex = prxparse("s/(\').*?(\')//");
|
||||
call prxchange(regex,-1,raw_value2);
|
||||
|
||||
/* remove commas */
|
||||
raw_value3=compress(raw_value2,',');
|
||||
|
||||
|
||||
|
||||
|
||||
/* output records that contain values other than digits and spaces */
|
||||
if notdigit(compress(raw_value3,' '))>0 then do;
|
||||
putlog raw_value3= $hex32.;
|
||||
REASON_CD='Invalid RAW_VALUE';
|
||||
putlog REASON_CD= raw_value= raw_value1= raw_value2= raw_value3=;
|
||||
output;
|
||||
end;
|
||||
|
||||
run;
|
||||
|
||||
%if %mf_nobs(&outds)>0 %then %let syscc=1008;
|
||||
|
||||
%mend;
|
||||
90
base/mp_filtergenerate.sas
Normal file
90
base/mp_filtergenerate.sas
Normal file
@@ -0,0 +1,90 @@
|
||||
/**
|
||||
@file
|
||||
@brief Generates a filter clause from an input table, to a fileref
|
||||
@details Uses the input table to generate an output filter clause.
|
||||
This feature is used to create dynamic dropdowns in [Data Controller for SAS®](
|
||||
https://datacontroller.io). The input table should be in the format below:
|
||||
|
||||
|GROUP_LOGIC:$3|SUBGROUP_LOGIC:$3|SUBGROUP_ID:8.|VARIABLE_NM:$32|OPERATOR_NM:$10|RAW_VALUE:$32767|
|
||||
|---|---|---|---|---|---|
|
||||
|AND|AND|1|AGE|=|12|
|
||||
|AND|AND|1|SEX|<=|'M'|
|
||||
|AND|OR|2|Name|NOT IN|('Jane','Alfred')|
|
||||
|AND|OR|2|Weight|>=|7|
|
||||
|
||||
Note - if the above table is received from an external client, the values
|
||||
should first be validated using the mp_filtercheck.sas macro to avoid risk
|
||||
of SQL injection.
|
||||
|
||||
To generate the filter, run the following code:
|
||||
|
||||
data work.filtertable;
|
||||
infile datalines4 dsd;
|
||||
input GROUP_LOGIC:$3. SUBGROUP_LOGIC:$3. SUBGROUP_ID:8. VARIABLE_NM:$32.
|
||||
OPERATOR_NM:$10. RAW_VALUE:$32767.;
|
||||
datalines4;
|
||||
AND,AND,1,AGE,=,12
|
||||
AND,AND,1,SEX,<=,"'M'"
|
||||
AND,OR,2,Name,NOT IN,"('Jane','Alfred')"
|
||||
AND,OR,2,Weight,>=,7
|
||||
;;;;
|
||||
run;
|
||||
|
||||
%mp_filtergenerate(work.filtertable,outref=myfilter)
|
||||
|
||||
data _null_;
|
||||
infile myfilter;
|
||||
input;
|
||||
put _infile_;
|
||||
run;
|
||||
|
||||
Will write the following query to the log:
|
||||
|
||||
> (
|
||||
> AGE = 12
|
||||
> AND
|
||||
> SEX <= 'M'
|
||||
> ) AND (
|
||||
> Name NOT IN ('Jane','Alfred')
|
||||
> OR
|
||||
> Weight >= 7
|
||||
> )
|
||||
|
||||
@param [in] inds The input table with query values
|
||||
@param [out] outref= The output fileref to contain the filter clause. Will
|
||||
be created (or replaced).
|
||||
|
||||
<h4> Related Macros </h4>
|
||||
@li mp_filtercheck.sas
|
||||
|
||||
<h4> SAS Macros </h4>
|
||||
@li mp_abort.sas
|
||||
|
||||
@version 9.3
|
||||
@author Allan Bowe
|
||||
|
||||
**/
|
||||
|
||||
%macro mp_filtergenerate(inds,outref=filter);
|
||||
|
||||
%mp_abort(iftrue= (&syscc ne 0)
|
||||
,mac=&sysmacroname
|
||||
,msg=%str(syscc=&syscc - on macro entry)
|
||||
)
|
||||
|
||||
filename &outref temp;
|
||||
|
||||
data _null_;
|
||||
file &outref lrecl=32800;
|
||||
set &inds end=last;
|
||||
by SUBGROUP_ID;
|
||||
if _n_=1 then put '(';
|
||||
else if first.SUBGROUP_ID then put +1 GROUP_LOGIC '(';
|
||||
else put +2 SUBGROUP_LOGIC;
|
||||
|
||||
put +4 VARIABLE_NM OPERATOR_NM RAW_VALUE;
|
||||
|
||||
if last.SUBGROUP_ID then put ')'@;
|
||||
run;
|
||||
|
||||
%mend;
|
||||
75
base/mp_hashdataset.sas
Normal file
75
base/mp_hashdataset.sas
Normal file
@@ -0,0 +1,75 @@
|
||||
/**
|
||||
@file
|
||||
@brief Returns a unique hash for a dataset
|
||||
@details Ignores metadata attributes, used only to hash values. Compared
|
||||
datasets must be in the same order.
|
||||
|
||||
%mp_hashdataset(sashelp.class,outds=myhash)
|
||||
|
||||
data _null_;
|
||||
set work.myhash;
|
||||
put hashkey=;
|
||||
run;
|
||||
|
||||

|
||||
|
||||
<h4> SAS Macros </h4>
|
||||
@li mf_getattrn.sas
|
||||
@li mf_getuniquename.sas
|
||||
@li mf_getvarlist.sas
|
||||
@li mf_getvartype.sas
|
||||
|
||||
@param [in] libds dataset to hash
|
||||
@param [out] outds= (work.mf_hashdataset) The output dataset to create. This
|
||||
will contain one column (hashkey) with one observation (a hex32.
|
||||
representation of the input hash)
|
||||
|hashkey:$32.|
|
||||
|---|
|
||||
|28ABC74ABFC45F50794237BA5566E6CA|
|
||||
|
||||
@version 9.2
|
||||
@author Allan Bowe
|
||||
**/
|
||||
|
||||
%macro mp_hashdataset(
|
||||
libds,
|
||||
outds=
|
||||
)/*/STORE SOURCE*/;
|
||||
%if %mf_getattrn(&libds,NLOBS)=0 %then %do;
|
||||
%put %str(WARN)ING: Dataset &libds is empty;, or is not a dataset;
|
||||
%end;
|
||||
%else %if %mf_getattrn(&libds,NLOBS)<0 %then %do;
|
||||
%put %str(ERR)OR: Dataset &libds is not a dataset;
|
||||
%end;
|
||||
%else %do;
|
||||
%local keyvar /* roll up the md5 */
|
||||
prevkeyvar /* retain prev record md5 */
|
||||
lastvar /* last var in input ds */
|
||||
varlist var i;
|
||||
/* avoid naming conflict for hash key vars */
|
||||
%let keyvar=%mf_getuniquename();
|
||||
%let prevkeyvar=%mf_getuniquename();
|
||||
%let lastvar=%mf_getuniquename();
|
||||
%let varlist=%mf_getvarlist(&libds);
|
||||
data &outds(rename=(&keyvar=hashkey) keep=&keyvar);
|
||||
length &prevkeyvar &keyvar $32;
|
||||
retain &prevkeyvar;
|
||||
set &libds end=&lastvar;
|
||||
/* hash should include previous row */
|
||||
if _n_>1 then &keyvar=put(md5(&prevkeyvar
|
||||
/* loop every column, hashing every individual value */
|
||||
%do i=1 %to %sysfunc(countw(&varlist));
|
||||
%let var=%scan(&varlist,&i,%str( ));
|
||||
%if %mf_getvartype(&libds,&var)=C %then %do;
|
||||
!!put(md5(trim(&var)),$hex32.)
|
||||
%end;
|
||||
%else %do;
|
||||
!!put(md5(trim(put(&var*1,binary64.))),$hex32.)
|
||||
%end;
|
||||
%end;
|
||||
),$hex32.);
|
||||
&prevkeyvar=&keyvar;
|
||||
if &lastvar then output;
|
||||
run;
|
||||
%end;
|
||||
%mend;
|
||||
@@ -237,8 +237,16 @@ data _null_;
|
||||
put '%else %if &action=OPEN %then %do; ';
|
||||
put ' /* fix encoding */ ';
|
||||
put ' OPTIONS NOBOMFILE; ';
|
||||
put ' ';
|
||||
put ' /** ';
|
||||
put ' * check engine type to avoid the below err message: ';
|
||||
put ' * > Function is only valid for filerefs using the CACHE access method. ';
|
||||
put ' */ ';
|
||||
put ' data _null_; ';
|
||||
put ' rc = stpsrv_header(''Content-type'',"text/html; encoding=utf-8"); ';
|
||||
put ' set sashelp.vextfl(where=(fileref="_WEBOUT")); ';
|
||||
put ' if xengine=''STREAM'' then do; ';
|
||||
put ' rc=stpsrv_header(''Content-type'',"text/html; encoding=utf-8"); ';
|
||||
put ' end; ';
|
||||
put ' run; ';
|
||||
put ' ';
|
||||
put ' /* setup json */ ';
|
||||
@@ -252,16 +260,9 @@ data _null_;
|
||||
put '%end; ';
|
||||
put ' ';
|
||||
put '%else %if &action=ARR or &action=OBJ %then %do; ';
|
||||
put ' %if &sysver=9.4 %then %do; ';
|
||||
put ' %mp_jsonout(&action,&ds,dslabel=&dslabel,fmt=&fmt ';
|
||||
put ' ,engine=PROCJSON,dbg=%str(&_debug) ';
|
||||
put ' ) ';
|
||||
put ' %end; ';
|
||||
put ' %else %do; ';
|
||||
put ' %mp_jsonout(&action,&ds,dslabel=&dslabel,fmt=&fmt ';
|
||||
put ' ,engine=DATASTEP,dbg=%str(&_debug) ';
|
||||
put ' ) ';
|
||||
put ' %end; ';
|
||||
put ' %mp_jsonout(&action,&ds,dslabel=&dslabel,fmt=&fmt ';
|
||||
put ' ,engine=DATASTEP,dbg=%str(&_debug) ';
|
||||
put ' ) ';
|
||||
put '%end; ';
|
||||
put '%else %if &action=CLOSE %then %do; ';
|
||||
put ' %if %str(&_debug) ge 131 %then %do; ';
|
||||
|
||||
@@ -68,8 +68,16 @@
|
||||
%else %if &action=OPEN %then %do;
|
||||
/* fix encoding */
|
||||
OPTIONS NOBOMFILE;
|
||||
|
||||
/**
|
||||
* check engine type to avoid the below err message:
|
||||
* > Function is only valid for filerefs using the CACHE access method.
|
||||
*/
|
||||
data _null_;
|
||||
rc = stpsrv_header('Content-type',"text/html; encoding=utf-8");
|
||||
set sashelp.vextfl(where=(fileref="_WEBOUT"));
|
||||
if xengine='STREAM' then do;
|
||||
rc=stpsrv_header('Content-type',"text/html; encoding=utf-8");
|
||||
end;
|
||||
run;
|
||||
|
||||
/* setup json */
|
||||
@@ -83,16 +91,9 @@
|
||||
%end;
|
||||
|
||||
%else %if &action=ARR or &action=OBJ %then %do;
|
||||
%if &sysver=9.4 %then %do;
|
||||
%mp_jsonout(&action,&ds,dslabel=&dslabel,fmt=&fmt
|
||||
,engine=PROCJSON,dbg=%str(&_debug)
|
||||
)
|
||||
%end;
|
||||
%else %do;
|
||||
%mp_jsonout(&action,&ds,dslabel=&dslabel,fmt=&fmt
|
||||
,engine=DATASTEP,dbg=%str(&_debug)
|
||||
)
|
||||
%end;
|
||||
%mp_jsonout(&action,&ds,dslabel=&dslabel,fmt=&fmt
|
||||
,engine=DATASTEP,dbg=%str(&_debug)
|
||||
)
|
||||
%end;
|
||||
%else %if &action=CLOSE %then %do;
|
||||
%if %str(&_debug) ge 131 %then %do;
|
||||
|
||||
@@ -1,6 +1,12 @@
|
||||
{
|
||||
"$schema": "https://cli.sasjs.io/sasjsconfig-schema.json",
|
||||
"macroFolders": ["base", "meta", "metax", "viya", "lua"],
|
||||
"macroFolders": [
|
||||
"base",
|
||||
"meta",
|
||||
"metax",
|
||||
"viya",
|
||||
"lua"
|
||||
],
|
||||
"docConfig": {
|
||||
"displayMacroCore": false,
|
||||
"enableLineage": false,
|
||||
@@ -21,14 +27,18 @@
|
||||
"serverType": "SASVIYA",
|
||||
"appLoc": "/Public/temp/macrocore",
|
||||
"serviceConfig": {
|
||||
"serviceFolders": ["tests/viya"],
|
||||
"serviceFolders": [
|
||||
"tests/base",
|
||||
"tests/viya"
|
||||
],
|
||||
"macroVars": {
|
||||
"mcTestAppLoc": "/Public/temp/macrocore"
|
||||
}
|
||||
},
|
||||
"deployConfig": {
|
||||
"deployServicePack": true
|
||||
}
|
||||
},
|
||||
"contextName": "SAS Job Execution compute context"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
128
tests/base/mp_filtercheck.test.sas
Normal file
128
tests/base/mp_filtercheck.test.sas
Normal file
@@ -0,0 +1,128 @@
|
||||
/**
|
||||
@file
|
||||
@brief Testing mp_filtercheck macro
|
||||
|
||||
<h4> SAS Macros </h4>
|
||||
@li mp_filtercheck.sas
|
||||
@li mp_assertdsobs.sas
|
||||
|
||||
**/
|
||||
|
||||
|
||||
/* valid filter */
|
||||
data work.inds;
|
||||
infile datalines4 dsd;
|
||||
input GROUP_LOGIC:$3. SUBGROUP_LOGIC:$3. SUBGROUP_ID:8. VARIABLE_NM:$32.
|
||||
OPERATOR_NM:$10. RAW_VALUE:$32767.;
|
||||
datalines4;
|
||||
AND,AND,1,AGE,=,12
|
||||
AND,AND,1,SEX,<=,"'M'"
|
||||
AND,OR,2,Name,NOT IN,"('Jane','Alfred')"
|
||||
AND,OR,2,Weight,>=,7
|
||||
;;;;
|
||||
run;
|
||||
|
||||
%mp_filtercheck(work.inds,
|
||||
targetds=sashelp.class,
|
||||
outds=work.badrecords
|
||||
)
|
||||
%let syscc=0;
|
||||
%mp_assertdsobs(work.badrecords,
|
||||
desc=Valid filter query,
|
||||
test=EMPTY,
|
||||
outds=work.test_results
|
||||
)
|
||||
|
||||
/* invalid column */
|
||||
data work.inds;
|
||||
infile datalines4 dsd;
|
||||
input GROUP_LOGIC:$3. SUBGROUP_LOGIC:$3. SUBGROUP_ID:8. VARIABLE_NM:$32.
|
||||
OPERATOR_NM:$10. RAW_VALUE:$32767.;
|
||||
datalines4;
|
||||
AND,AND,1,invalid,=,12
|
||||
AND,AND,1,SEX,<=,"'M'"
|
||||
AND,OR,2,Name,NOT IN,"('Jane','Alfred')"
|
||||
AND,OR,2,Weight,>=,7
|
||||
;;;;
|
||||
run;
|
||||
|
||||
%mp_filtercheck(work.inds,
|
||||
targetds=sashelp.class,
|
||||
outds=work.badrecords
|
||||
)
|
||||
%let syscc=0;
|
||||
%mp_assertdsobs(work.badrecords,
|
||||
desc=Invalid column name,
|
||||
test=HASOBS,
|
||||
outds=work.test_results
|
||||
)
|
||||
|
||||
/* invalid raw value */
|
||||
data work.inds;
|
||||
infile datalines4 dsd;
|
||||
input GROUP_LOGIC:$3. SUBGROUP_LOGIC:$3. SUBGROUP_ID:8. VARIABLE_NM:$32.
|
||||
OPERATOR_NM:$10. RAW_VALUE:$32767.;
|
||||
datalines4;
|
||||
AND,OR,2,Name,NOT IN,"(''''Jane','Alfred')"
|
||||
;;;;
|
||||
run;
|
||||
|
||||
%mp_filtercheck(work.inds,
|
||||
targetds=sashelp.class,
|
||||
outds=work.badrecords
|
||||
)
|
||||
%let syscc=0;
|
||||
%mp_assertdsobs(work.badrecords,
|
||||
desc=Invalid raw value,
|
||||
test=HASOBS,
|
||||
outds=work.test_results
|
||||
)
|
||||
|
||||
/* Code injection - column name */
|
||||
data work.inds;
|
||||
infile datalines4 dsd;
|
||||
input GROUP_LOGIC:$3. SUBGROUP_LOGIC:$3. SUBGROUP_ID:8. VARIABLE_NM:$32.
|
||||
OPERATOR_NM:$10. RAW_VALUE:$32767.;
|
||||
datalines4;
|
||||
AND,AND,1,%abort,=,12
|
||||
AND,OR,2,Weight,>=,7
|
||||
;;;;
|
||||
run;
|
||||
|
||||
%mp_filtercheck(work.inds,
|
||||
targetds=sashelp.class,
|
||||
outds=work.badrecords
|
||||
)
|
||||
%let syscc=0;
|
||||
%mp_assertdsobs(work.badrecords,
|
||||
desc=Code injection - column name,
|
||||
test=HASOBS,
|
||||
outds=work.test_results
|
||||
)
|
||||
|
||||
/* Code injection - raw values*/
|
||||
data work.inds;
|
||||
infile datalines4 dsd;
|
||||
input GROUP_LOGIC:$3. SUBGROUP_LOGIC:$3. SUBGROUP_ID:8. VARIABLE_NM:$32.
|
||||
OPERATOR_NM:$10. RAW_VALUE:$32767.;
|
||||
datalines4;
|
||||
AND,AND,1,age,=,;;%abort
|
||||
;;;;
|
||||
run;
|
||||
|
||||
%mp_filtercheck(work.inds,
|
||||
targetds=sashelp.class,
|
||||
outds=work.badrecords
|
||||
)
|
||||
%let syscc=0;
|
||||
%mp_assertdsobs(work.badrecords,
|
||||
desc=Code injection - raw value abort,
|
||||
test=HASOBS,
|
||||
outds=work.test_results
|
||||
)
|
||||
|
||||
|
||||
|
||||
%webout(OPEN)
|
||||
%webout(OBJ, TEST_RESULTS)
|
||||
%webout(CLOSE)
|
||||
@@ -9,8 +9,8 @@
|
||||
|
||||
@param path= The full path of the folder to be created
|
||||
@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 grant_type= (authorization_code) Valid values are "password" or
|
||||
"authorization_code" (unquoted).
|
||||
|
||||
|
||||
@version VIYA V.03.04
|
||||
@@ -39,7 +39,6 @@
|
||||
%let &access_token_var=;
|
||||
%end;
|
||||
|
||||
%put &sysmacroname: grant_type=&grant_type;
|
||||
%mp_abort(iftrue=(&grant_type ne authorization_code and &grant_type ne password
|
||||
and &grant_type ne sas_services
|
||||
)
|
||||
@@ -85,12 +84,15 @@ options noquotelenmax;
|
||||
%local libref1;
|
||||
%let libref1=%mf_getuniquelibref();
|
||||
libname &libref1 JSON fileref=&fname1;
|
||||
%mp_abort(iftrue=(&SYS_PROCHTTP_STATUS_CODE ne 200 and &SYS_PROCHTTP_STATUS_CODE ne 404)
|
||||
%mp_abort(
|
||||
iftrue=(
|
||||
&SYS_PROCHTTP_STATUS_CODE ne 200 and &SYS_PROCHTTP_STATUS_CODE ne 404
|
||||
)
|
||||
,mac=&sysmacroname
|
||||
,msg=%str(&SYS_PROCHTTP_STATUS_CODE &SYS_PROCHTTP_STATUS_PHRASE)
|
||||
)
|
||||
%if &SYS_PROCHTTP_STATUS_CODE=200 %then %do;
|
||||
%put &sysmacroname &newpath exists so grab the follow on link ;
|
||||
%*put &sysmacroname &newpath exists so grab the follow on link ;
|
||||
data _null_;
|
||||
set &libref1..links;
|
||||
if rel='createChild' then
|
||||
|
||||
@@ -72,7 +72,6 @@ https://go.documentation.sas.com/?docsetId=calcontexts&docsetTarget=n1hjn8eobk5p
|
||||
%let oauth_bearer=oauth_bearer=sas_services;
|
||||
%let &access_token_var=;
|
||||
%end;
|
||||
%put &sysmacroname: grant_type=&grant_type;
|
||||
|
||||
/* initial validation checking */
|
||||
%mp_abort(iftrue=(&grant_type ne authorization_code and &grant_type ne password
|
||||
|
||||
@@ -83,7 +83,6 @@ https://go.documentation.sas.com/?docsetId=calcontexts&docsetTarget=n1hjn8eobk5p
|
||||
%let oauth_bearer=oauth_bearer=sas_services;
|
||||
%let &access_token_var=;
|
||||
%end;
|
||||
%put &sysmacroname: grant_type=&grant_type;
|
||||
|
||||
/* initial validation checking */
|
||||
%mp_abort(iftrue=(&grant_type ne authorization_code and &grant_type ne password
|
||||
|
||||
@@ -44,7 +44,7 @@
|
||||
%let oauth_bearer=oauth_bearer=sas_services;
|
||||
%let &access_token_var=;
|
||||
%end;
|
||||
%put &sysmacroname: grant_type=&grant_type;
|
||||
|
||||
%mp_abort(iftrue=(&grant_type ne authorization_code and &grant_type ne password
|
||||
and &grant_type ne sas_services
|
||||
)
|
||||
|
||||
@@ -39,7 +39,7 @@
|
||||
%let oauth_bearer=oauth_bearer=sas_services;
|
||||
%let &access_token_var=;
|
||||
%end;
|
||||
%put &sysmacroname: grant_type=&grant_type;
|
||||
|
||||
%mp_abort(iftrue=(&grant_type ne authorization_code and &grant_type ne password
|
||||
and &grant_type ne sas_services
|
||||
)
|
||||
|
||||
@@ -54,7 +54,7 @@
|
||||
%let oauth_bearer=oauth_bearer=sas_services;
|
||||
%let &access_token_var=;
|
||||
%end;
|
||||
%put &sysmacroname: grant_type=&grant_type;
|
||||
|
||||
%mp_abort(iftrue=(&grant_type ne authorization_code and &grant_type ne password
|
||||
and &grant_type ne sas_services
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user