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

Compare commits

...

19 Commits

Author SHA1 Message Date
Allan Bowe
b4834f9b40 fix: updates following test runs in Studio 2021-05-05 20:12:06 +03:00
Allan Bowe
1b5ad93cad chore: updating all.sas 2021-05-05 11:48:38 +03:00
Allan Bowe
f2942f2032 chore: adding sasjsresults to .gitignore 2021-05-05 01:39:53 +03:00
Allan Bowe
4198448b81 chore: removing temp results folder 2021-05-05 01:39:29 +03:00
Allan Bowe
47a33452e0 Merge pull request #16 from sasjs/testrelease
feat: new macro for validating inputs (mp_validatecol.sas), also a re…
2021-05-05 01:36:38 +03:00
Allan Bowe
fb21a0adfd feat: new macro for validating inputs (mp_validatecol.sas), also a refresh of the tests now that sasjs test is released. All tests are passing 2021-05-05 01:35:00 +03:00
Allan Bowe
e01b06b640 feat: new assertion macro (mp_assertcols.sas) to test for column existence (or not) 2021-05-04 21:53:57 +03:00
Allan Bowe
24380ddf26 Merge branch 'main' of github.com:sasjs/core 2021-05-03 22:44:07 +03:00
Allan Bowe
1ef42d45af fix: wrapping filter query in brackets to allow logic to be encapsulated when using with other logic sources 2021-05-03 22:43:56 +03:00
Allan Bowe
6ee13a2779 Merge pull request #15 from sasjs/removewarns
fix: removing WARNINGs from code logic
2021-05-03 20:30:43 +03:00
Allan Bowe
ffd2e135dc fix: removing WARNINGs from code logic 2021-05-03 20:28:48 +03:00
Allan Bowe
7f2ad5fc66 feat: mp_filtervalidate.sas - to run a proc sql validate against the target table to ensure validity 2021-05-03 13:48:24 +03:00
ff1eb54cc3 chore: updating logo 2021-05-03 10:59:25 +02:00
Allan Bowe
d6235c6357 chore: reducing raw_value size to 4000 for wider DB support 2021-05-03 11:17:20 +03:00
Allan Bowe
98118adb9a feat: new assertion macro for testing the values in a target column. Designed for use with 'sasjs test'. 2021-05-03 01:00:36 +03:00
Allan Bowe
369c4412f3 fix: adding tests for mp_filtergenerate, also updating the corresponding macros following test results 2021-05-02 22:11:44 +03:00
Allan Bowe
7d7608f06c chore: updating all.sas 2021-05-02 19:12:08 +03:00
Allan Bowe
3791cb8a2c feat: two new macros for checking a filter query, and then generating a filter query. One test to cover the generation part. One more macro to provide assertions on the number of rows in a table, compatible with the upcoming 'sasjs test' feature. 2021-05-02 19:10:37 +03:00
Allan Bowe
ff82f7d75c chore: header update in mf_getvarlist 2021-05-01 20:33:45 +03:00
37 changed files with 2354 additions and 115 deletions

1
.gitignore vendored
View File

@@ -1,6 +1,7 @@
node_modules
.DS_Store
sasjsbuild/
sasjsresults/
# avoid filenames with spaces being committed to source control
**\ **

1051
all.sas

File diff suppressed because it is too large Load Diff

View File

@@ -2,9 +2,9 @@
@file
@brief Checks if a set of variables ALL exist in a data set.
@details Returns 0 if ANY of the variables do not exist, or 1 if they ALL do.
Usage:
Usage:
%put %mf_existVarList(sashelp.class, age sex name dummyvar)
%put %mf_existVarList(sashelp.class, age sex name dummyvar);
<h4> SAS Macros </h4>
@li mf_abort.sas
@@ -29,7 +29,7 @@
%let dsid=%sysfunc(open(&libds,is));
%if &dsid=0 %then %do;
%put WARNING: unable to open &libds in mf_existvarlist (&dsid);
%put %str(WARN)ING: unable to open &libds in mf_existvarlist (&dsid);
%end;
%if %sysfunc(attrn(&dsid,NVARS))=0 %then %do;

View File

@@ -23,7 +23,7 @@
%local dsid rc;
%let dsid=%sysfunc(open(&libds,is));
%if &dsid = 0 %then %do;
%put WARNING: Cannot open %trim(&libds), system message below;
%put %str(WARN)ING: Cannot open %trim(&libds), system message below;
%put %sysfunc(sysmsg());
-1
%end;

View File

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

View File

@@ -30,7 +30,7 @@
%local count_base count_extr i i2 extr_word base_word match outvar;
%if %length(&str1)=0 or %length(&str2)=0 %then %do;
%put WARNING: empty string provided!;
%put %str(WARN)ING: empty string provided!;
%put base string (str1)= &str1;
%put compare string (str2) = &str2;
%return;

145
base/mp_assertcols.sas Normal file
View File

@@ -0,0 +1,145 @@
/**
@file
@brief Asserts the existence (or not) of columns
@details Useful in the context of writing sasjs tests. The results of the
test are _appended_ to the &outds. table.
Example usage:
%mp_assertcols(sashelp.class,
cols=name age sex,
test=ALL,
desc=check all columns exist
)
%mp_assertcols(sashelp.class,
cols=a b c,
test=NONE
)
%mp_assertcols(sashelp.class,
cols=age depth,
test=ANY
)
<h4> SAS Macros </h4>
@li mf_existds.sas
@li mf_existvarlist.sas
@li mf_getvarlist.sas
@li mf_wordsinstr1butnotstr2.sas
@li mp_abort.sas
@param [in] inds The input library.dataset to test for values
@param [in] cols= The list of columns to check for
@param [in] desc= (Testing observations) The user provided test description
@param [in] test= (ALL) The test to apply. Valid values are:
@li ALL - Test is a PASS if ALL columns exist in &inds
@li ANY - Test is a PASS if ANY of the columns exist in &inds
@li NONE - Test is a PASS if NONE of the columns exist in &inds
@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|Column &inds contained ALL columns|
<h4> Related Macros </h4>
@li mp_assertdsobs.sas
@li mp_assertcolvals.sas
@li mp_assertdsobs.sas
@version 9.2
@author Allan Bowe
**/
%macro mp_assertcols(inds,
cols=0,
test=ALL,
desc=0,
outds=work.test_results
)/*/STORE SOURCE*/;
%mp_abort(iftrue= (&syscc ne 0)
,mac=&sysmacroname
,msg=%str(syscc=&syscc - on macro entry)
)
%local lib ds ;
%let lib=%scan(&inds,1,%str(.));
%let ds=%scan(&inds,2,%str(.));
%let cols=%upcase(&cols);
%mp_abort(iftrue= (%mf_existds(&lib..&ds)=0)
,mac=&sysmacroname
,msg=%str(&lib..&ds not found!)
)
%mp_abort(iftrue= (&cols=0)
,mac=&sysmacroname
,msg=%str(No cols provided)
)
%let test=%upcase(&test);
%if &test ne ANY and &test ne ALL and &test ne NONE %then %do;
%mp_abort(
mac=&sysmacroname,
msg=%str(Invalid test - &test)
)
%end;
/**
* now do the actual test!
*/
%local result;
%if %mf_existVarList(&inds,&cols)=1 %then %let result=ALL;
%else %do;
%local targetcols compare;
%let targetcols=%upcase(%mf_getvarlist(&inds));
%let compare=%mf_wordsinstr1butnotstr2(
Str1=&cols,
Str2=&targetcols
);
%if %cmpres(&compare)=%cmpres(&cols) %then %let result=NONE;
%else %let result=SOME;
%end;
data;
length test_description $256 test_result $4 test_comments $256;
test_description=symget('desc');
if test_description='0'
then test_description="Testing &inds for existence of &test of: &cols";
test_result='FAIL';
test_comments="&sysmacroname: &inds has &result columns ";
%if &test=ALL %then %do;
%if &result=ALL %then %do;
test_result='PASS';
%end;
%end;
%else %if &test=ANY %then %do;
%if &result=SOME %then %do;
test_result='PASS';
%end;
%end;
%else %if &test=NONE %then %do;
%if &result=NONE %then %do;
test_result='PASS';
%end;
%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;

147
base/mp_assertcolvals.sas Normal file
View File

@@ -0,0 +1,147 @@
/**
@file
@brief Asserts the values in a column
@details Useful in the context of writing sasjs tests. The results of the
test are _appended_ to the &outds. table.
Example usage:
data work.checkds;
do checkval='Jane','James','Jill';
output;
end;
run;
%mp_assertcolvals(sashelp.class.name,
checkvals=work.checkds.checkval,
desc=At least one value has a match,
test=ANYVAL
)
data work.check;
do val='M','F';
output;
end;
run;
%mp_assertcolvals(sashelp.class.sex,
checkvals=work.check.val,
desc=All values have a match,
test=ALLVALS
)
<h4> SAS Macros </h4>
@li mf_existds.sas
@li mf_nobs.sas
@li mp_abort.sas
@param [in] indscol The input library.dataset.column to test for values
@param [in] checkvals= A library.dataset.column value containing a UNIQUE
list of values to be compared against the source (indscol).
@param [in] desc= (Testing observations) The user provided test description
@param [in] test= (ALLVALS) The test to apply. Valid values are:
@li ALLVALS - Test is a PASS if ALL values have a match in checkvals
@li ANYVAL - Test is a PASS if at least 1 value has a match in checkvals
@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|Column &indscol contained ALL target vals|
<h4> Related Macros </h4>
@li mp_assertdsobs.sas
@version 9.2
@author Allan Bowe
**/
%macro mp_assertcolvals(indscol,
checkvals=0,
test=ALLVALS,
desc=mp_assertcolvals - no desc provided,
outds=work.test_results
)/*/STORE SOURCE*/;
%mp_abort(iftrue= (&syscc ne 0)
,mac=&sysmacroname
,msg=%str(syscc=&syscc - on macro entry)
)
%local lib ds col clib cds ccol nobs;
%let lib=%scan(&indscol,1,%str(.));
%let ds=%scan(&indscol,2,%str(.));
%let col=%scan(&indscol,3,%str(.));
%mp_abort(iftrue= (%mf_existds(&lib..&ds)=0)
,mac=&sysmacroname
,msg=%str(&lib..&ds not found!)
)
%mp_abort(iftrue= (&checkvals=0)
,mac=&sysmacroname
,msg=%str(Set CHECKVALS to a library.dataset.column containing check vals)
)
%let clib=%scan(&checkvals,1,%str(.));
%let cds=%scan(&checkvals,2,%str(.));
%let ccol=%scan(&checkvals,3,%str(.));
%mp_abort(iftrue= (%mf_existds(&clib..&cds)=0)
,mac=&sysmacroname
,msg=%str(&clib..&cds not found!)
)
%let nobs=%mf_nobs(&clib..&cds);
%mp_abort(iftrue= (&nobs=0)
,mac=&sysmacroname
,msg=%str(&clib..&cds is empty!)
)
%let test=%upcase(&test);
%if &test ne ALLVALS and &test ne ANYVAL %then %do;
%mp_abort(
mac=&sysmacroname,
msg=%str(Invalid test - &test)
)
%end;
%local result orig;
%let result=-1;
%let orig=-1;
proc sql noprint;
select count(*) into: result
from &lib..&ds
where &col not in (
select &ccol from &clib..&cds
);
select count(*) into: orig from &lib..&ds;
quit;
%mp_abort(iftrue= (&syscc ne 0)
,mac=&sysmacroname
,msg=%str(syscc=&syscc after macro query)
)
data;
length test_description $256 test_result $4 test_comments $256;
test_description=symget('desc');
test_result='FAIL';
test_comments="&sysmacroname: &lib..&ds..&col has &result values "
!!"not in &clib..&cds..&ccol ";
%if &test=ANYVAL %then %do;
if &result < &orig then test_result='PASS';
%end;
%else %if &test=ALLVALS %then %do;
if &result=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;

89
base/mp_assertdsobs.sas Normal file
View File

@@ -0,0 +1,89 @@
/**
@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
@li mp_abort.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
@li EQUALS [integer] - Test passes if obs count matches the provided integer
@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|
<h4> Related Macros </h4>
@li mp_assertcolvals.sas
@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);
%if %substr(&test.xxxxx,1,6)=EQUALS %then %do;
%let val=%scan(&test,2,%str( ));
%mp_abort(iftrue= (%DATATYP(&val)=CHAR)
,mac=&sysmacroname
,msg=%str(Invalid test - &test, expected EQUALS [integer])
)
%let test=EQUALS;
%end;
%else %if &test ne HASOBS and &test ne EMPTY %then %do;
%mp_abort(
mac=&sysmacroname,
msg=%str(Invalid test - &test)
)
%end;
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 %if &test=EQUALS %then %do;
if &nobs=&val 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;

View File

@@ -43,7 +43,7 @@
%local i setds nvars;
%if not %sysfunc(exist(&base_ds)) %then %do;
%put WARNING: &base_ds does not exist;
%put %str(WARN)ING: &base_ds does not exist;
%return;
%end;
@@ -61,7 +61,7 @@ select count(*) into: nvars from dictionary.columns
where libname="%scan(%upcase(&base_ds),1)"
and memname="%scan(%upcase(&base_ds),2)";
%if &nvars=0 %then %do;
%put WARNING: Dataset &base_ds has no variables! It will not be converted.;
%put %str(WARN)ING: Dataset &base_ds has no variables, will not be converted.;
%return;
%end;

View File

@@ -19,7 +19,7 @@
)/*/STORE SOURCE*/;
%if not %sysfunc(exist(&ds)) %then %do;
%put WARNING: &ds does not exist;
%put %str(WARN)ING: &ds does not exist;
%return;
%end;

171
base/mp_filtercheck.sas Normal file
View File

@@ -0,0 +1,171 @@
/**
@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, and call mp_abort.sas for a
graceful service exit (configurable).
Used for dynamic filtering in [Data Controller for SAS&reg;](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:$4000|
|---|---|---|---|---|---|
|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/NE/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] abort= (YES) If YES will call mp_abort.sas on any exceptions
@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_getuniquefileref.sas
@li mf_getvarlist.sas
@li mf_nobs.sas
@li mp_filtergenerate.sas
@li mp_filtervalidate.sas
<h4> Related Macros </h4>
@li mp_filtergenerate.sas
@li mp_filtervalidate.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,abort=YES);
%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','NE','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 and periods*/
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;
data _null_;
set &outds;
call symputx('REASON_CD',reason_cd,'l');
stop;
run;
%mp_abort(iftrue=(&abort=YES)
mac=&sysmacroname,
msg=%str(Filter issues in &inds, first was &reason_cd, details in &outds)
)
%if %mf_nobs(&outds)>0 %then %do;
%let syscc=1008;
%return;
%end;
/**
* syntax checking passed but it does not mean the filter is valid
* for that we can run a proc sql validate query
*/
%local fref1;
%let fref1=%mf_getuniquefileref();
%mp_filtergenerate(&inds,outref=&fref1)
/* this macro will also set syscc to 1008 if any issues found */
%mp_filtervalidate(&fref1,&targetds,outds=&outds,abort=&abort)
%mend;

102
base/mp_filtergenerate.sas Normal file
View File

@@ -0,0 +1,102 @@
/**
@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&reg](
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:$4000|
|---|---|---|---|---|---|
|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:$4000.;
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
@li mp_filtervalidate.sas
<h4> SAS Macros </h4>
@li mp_abort.sas
@li mf_nobs.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;
%if %mf_nobs(&inds)=0 %then %do;
/* ensure we have a default filter */
data _null_;
file &outref;
put '1=1';
run;
%end;
%else %do;
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 ')'@;
if last then put ')';
run;
%end;
%mend;

104
base/mp_filtervalidate.sas Normal file
View File

@@ -0,0 +1,104 @@
/**
@file
@brief Checks a generated filter query for validity
@details Runs a generated filter in proc sql with the validate option.
Used in mp_filtercheck.sas in an fcmp container.
Built to support dynamic filtering in
[Data Controller for SAS&reg;](https://datacontroller.io).
Usage:
data work.filtertable;
infile datalines4 dsd;
input GROUP_LOGIC:$3. SUBGROUP_LOGIC:$3. SUBGROUP_ID:8. VARIABLE_NM:$32.
OPERATOR_NM:$10. RAW_VALUE:$4000.;
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)
%mp_filtervalidate(myfilter,sashelp.class)
@returns The SYSCC value will be 1008 if there are validation issues.
@param [in] inref The input fileref to validate (generated by
mp_filtergenerate.sas)
@param [in] targetds The target dataset against which to verify the query
@param [out] abort= (YES) If YES will call mp_abort.sas on any exceptions
@param [out] outds= (work.mp_filtervalidate) Output dataset containing the
error / warning message, if one exists. If this table contains any rows,
there are problems!
<h4> SAS Macros </h4>
@li mf_getuniquefileref.sas
@li mf_nobs.sas
@li mp_abort.sas
<h4> Related Macros </h4>
@li mp_filtercheck.sas
@li mp_filtergenerate.sas
@version 9.3
@author Allan Bowe
**/
%macro mp_filtervalidate(inref,targetds,abort=YES,outds=work.mp_filtervalidate);
%mp_abort(iftrue= (&syscc ne 0 or &syserr ne 0)
,mac=&sysmacroname
,msg=%str(syscc=&syscc / syserr=&syserr - on macro entry)
)
%local fref1;
%let fref1=%mf_getuniquefileref();
data _null_;
file &fref1;
infile &inref end=eof;
if _n_=1 then do;
put "proc sql;";
put "validate select * from &targetds";
put "where " ;
end;
input;
put _infile_;
putlog _infile_;
if eof then put ";quit;";
run;
%inc &fref1;
data &outds;
if &sqlrc or &syscc or &syserr then do;
REASON_CD=coalescec(symget('SYSERRORTEXT'),symget('SYSWARNINGTEXT'));
output;
end;
else stop;
run;
filename &fref1 clear;
%if %mf_nobs(&outds)>0 %then %do;
%if &abort=YES %then %do;
data _null_;
set &outds;
call symputx('REASON_CD',reason_cd,'l');
stop;
run;
%mp_abort(
mac=&sysmacroname,
msg=%str(Filter issues in &inref: %quote(&reason_cd))
)
%end;
%let syscc=1008;
%end;
%mend;

View File

@@ -36,8 +36,20 @@
%let contentype=%upcase(&contenttype);
%local platform; %let platform=%mf_getplatform();
/**
* check engine type to avoid the below err message:
* > Function is only valid for filerefs using the CACHE access method.
*/
%local streamweb;
%let streamweb=0;
data _null_;
set sashelp.vextfl(where=(upcase(fileref)="_WEBOUT"));
if xengine='STREAM' then call symputx('streamweb',1,'l');
run;
%if &contentype=ZIP %then %do;
%if &platform=SASMETA %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");
@@ -51,7 +63,7 @@
%end;
%else %if &contentype=EXCEL %then %do;
/* suitable for XLS format */
%if &platform=SASMETA %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");
@@ -64,7 +76,7 @@
%end;
%end;
%else %if &contentype=XLSX %then %do;
%if &platform=SASMETA %then %do;
%if &platform=SASMETA and &streamweb=1 %then %do;
data _null_;
rc=stpsrv_header('Content-type',
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
@@ -79,7 +91,7 @@
%end;
%end;
%else %if &contentype=TEXT %then %do;
%if &platform=SASMETA %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");
@@ -92,7 +104,7 @@
%end;
%end;
%else %if &contentype=CSV %then %do;
%if &platform=SASMETA %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");
@@ -122,4 +134,4 @@
%mp_binarycopy(inloc="&inloc",outref=_webout)
%end;
%mend;
%mend;

65
base/mp_validatecol.sas Normal file
View File

@@ -0,0 +1,65 @@
/**
@file
@brief Used to validate variables in a dataset
@details Useful when sanitising inputs, to ensure that they arrive with a
certain pattern.
Usage:
data test;
infile datalines4 dsd;
input;
libds=_infile_;
%mp_validatecol(libds,LIBDS,is_libds)
datalines4;
some.libname
!lib.blah
%abort
definite.ok
not.ok!
nineletrs._
;;;;
run;
@param [in] incol The column to be validated
@param [in] rule The rule to apply. Current rules:
@li LIBDS - matches LIBREF.DATASET format
@param [out] outcol The variable to create, with the results of the match
<h4> SAS Macros </h4>
@li mf_getuniquename.sas
@version 9.3
**/
%macro mp_validatecol(incol,rule,outcol);
/* tempcol is given a unique name with every invocation */
%local tempcol;
%let tempcol=%mf_getuniquename();
%if &rule=ISNUM %then %do;
/*
credit SØREN LASSEN
https://sasmacro.blogspot.com/2009/06/welcome-isnum-macro.html
*/
&tempcol=input(&incol,?? best32.);
if missing(&tempcol) then &outcol=0;
else &outcol=1;
drop &tempcol;
%end;
%else %if &rule=LIBDS %then %do;
/* match libref.dataset */
if _n_=1 then do;
retain &tempcol;
&tempcol=prxparse('/^[_a-z]\w{0,7}\.[_a-z]\w{0,31}$/i');
if missing(&tempcol) then do;
putlog "%str(ERR)OR: Invalid expression for LIBDS";
stop;
end;
drop &tempcol;
end;
if prxmatch(&tempcol, trim(&incol)) then &outcol=1;
else &outcol=0;
%end;
%mend;

View File

@@ -2,11 +2,11 @@
@file
@brief Creates a zip file
@details For DIRECTORY usage, will ignore subfolders. For DATASET usage,
provide a column that contains the full file path to each file to be zipped.
provide a column that contains the full file path to each file to be zipped.
%mp_zip(in=myzips,type=directory,outname=myDir)
%mp_zip(in=/my/file/path.txt,type=FILE,outname=myFile)
%mp_zip(in=SOMEDS,incol=FPATH,type=DATASET,outname=myFile)
%mp_zip(in=myzips,type=directory,outname=myDir)
%mp_zip(in=/my/file/path.txt,type=FILE,outname=myFile)
%mp_zip(in=SOMEDS,incol=FPATH,type=DATASET,outname=myFile)
If you are sending zipped output to the _webout destination as part of an STP
be sure that _debug is not set (else the SPWA will send non zipped content

View File

@@ -70,7 +70,7 @@ run;
%end;
%if &syscc ge 4 %then %do;
%put WARNING: SYSCC=&syscc, exiting &sysmacroname;
%put %str(WARN)ING: SYSCC=&syscc, exiting &sysmacroname;
%return;
%end;

View File

@@ -332,7 +332,7 @@ run;
run;
%if %length(&open_passthrough)>0 %then %do;
%put WARNING: Passthrough option for postgres not yet supported;
%put %str(WARN)ING: Passthrough option for postgres not yet supported;
%return;
%end;
%else %do;
@@ -455,8 +455,8 @@ run;
%return;
%end;
%else %do;
%put WARNING: Engine &engine is currently unsupported;
%put WARNING- Please contact your support team.;
%put %str(WARN)ING: Engine &engine is currently unsupported;
%put %str(WARN)ING- Please contact your support team.;
%return;
%end;

View File

@@ -88,7 +88,7 @@ data _null_;
putlog (_all_)(=);
run;
%if &checktype = SASLibrary %then %do;
%put WARNING: Library (&liburi) already exists with libname (&libname) ;
%put %str(WARN)ING: Library (&liburi) already exists with libname (&libname);
%return;
%end;
@@ -103,7 +103,7 @@ data _null_;
putlog (_all_)(=);
run;
%if &checktype = SASLibrary %then %do;
%put WARNING: Library (&liburi) already exists with libref (&libref) ;
%put %str(WARN)ING: Library (&liburi) already exists with libref (&libref) ;
%return;
%end;
@@ -123,7 +123,7 @@ data _null_;
call symputx('treeuri',uri,'l');
run;
%if &foldertype ne Tree %then %do;
%put WARNING: Tree &tree does not exist!;
%put %str(WARN)ING: Tree &tree does not exist!;
%return;
%end;
@@ -231,7 +231,7 @@ filename &frefout temp;
* check SAS version
*/
%if %sysevalf(&sysver lt 9.3) %then %do;
%put WARNING: Version 9.3 or later required;
%put %str(WARN)ING: Version 9.3 or later required;
%return;
%end;

View File

@@ -118,7 +118,7 @@ data _null_;
call symputx('treeuri',uri,'l');
run;
%if &foldertype ne Tree %then %do;
%put WARNING: Tree &tree does not exist!;
%put %str(WARN)ING: Tree &tree does not exist!;
%return;
%end;
@@ -133,7 +133,7 @@ data _null_;
call symputx('stpuri',uri,'l');
run;
%if &cmtype = ClassifierMap %then %do;
%put WARNING: Stored Process &stpname already exists in &tree!;
%put %str(WARN)ING: Stored Process &stpname already exists in &tree!;
%return;
%end;
@@ -141,14 +141,14 @@ run;
* Check that the physical file exists
*/
%if %sysfunc(fileexist(&directory/&filename)) ne 1 %then %do;
%put WARNING: FILE *&directory/&filename* NOT FOUND!;
%put %str(WARN)ING: FILE *&directory/&filename* NOT FOUND!;
%return;
%end;
%if &stptype=1 %then %do;
/* type 1 STP - where code is stored on filesystem */
%if %sysevalf(&sysver lt 9.2) %then %do;
%put WARNING: Version 9.2 or later required;
%put %str(WARN)ING: Version 9.2 or later required;
%return;
%end;
@@ -162,7 +162,7 @@ run;
%if &checkdirtype ne Directory %then %do;
%mm_getdirectories(path=&directory,outds=&outds ,mDebug=&mDebug)
%if %mf_nobs(&outds)=0 or %sysfunc(exist(&outds))=0 %then %do;
%put WARNING: The directory object does not exist for &directory;
%put %str(WARN)ING: The directory object does not exist for &directory;
%return;
%end;
%end;
@@ -180,12 +180,12 @@ run;
length id $20 type $256;
__rc=metadata_resolve("&treeuri",type,id);
if type ne 'Tree' then do;
putlog "WARNING: Invalid tree URI: &treeuri";
putlog "%str(WARN)ING: Invalid tree URI: &treeuri";
stopme=1;
end;
__rc=metadata_resolve(directoryuri,type,id);
if type ne 'Directory' then do;
putlog 'WARNING: Invalid directory URI: ' directoryuri;
putlog "%str(WARN)ING: Invalid directory URI: " directoryuri;
stopme=1;
end;
@@ -194,7 +194,7 @@ run;
if type ne 'LogicalServer' then do;
__rc=metadata_getnobj("omsobj:LogicalServer?@Name='&server'",1,serveruri);
if serveruri='' then do;
putlog "WARNING: Invalid server: &server";
putlog "%str(WARN)ING: Invalid server: &server";
stopme=1;
end;
end;
@@ -217,7 +217,7 @@ run;
rc6 = METADATA_SETATTR(prompturi, 'GroupInfo',groupinfo);
if sum(of rc1-rc6) ne 0 then do;
putlog 'WARNING: Issue creating prompt.';
putlog "%str(WARN)ING: Issue creating prompt.";
if prompturi ne . then do;
putlog ' Removing orphan: ' prompturi;
rc = METADATA_DELOBJ(prompturi);
@@ -232,7 +232,7 @@ run;
rc9=METADATA_SETATTR(fileuri, 'IsARelativeName','1');
rc10=METADATA_SETASSN(fileuri, 'Directories','MODIFY',directoryuri);
if sum(of rc7-rc10) ne 0 then do;
putlog 'WARNING: Issue creating file.';
putlog "%str(WARN)ING: Issue creating file.";
if fileuri ne . then do;
putlog ' Removing orphans:' prompturi fileuri;
rc = METADATA_DELOBJ(prompturi);
@@ -251,7 +251,7 @@ run;
!!"<OutputParameters/></StoredProcess>";
rc14= METADATA_SETATTR(texturi, 'StoredText',storedtext);
if sum(of rc11-rc14) ne 0 then do;
putlog 'WARNING: Issue creating TextStore.';
putlog "%str(WARN)ING: Issue creating TextStore.";
if texturi ne . then do;
putlog ' Removing orphans: ' prompturi fileuri texturi;
rc = METADATA_DELOBJ(prompturi);
@@ -299,7 +299,7 @@ run;
%else %if &stptype=2 %then %do;
/* type 2 stp - code is stored in metadata */
%if %sysevalf(&sysver lt 9.3) %then %do;
%put WARNING: SAS version 9.3 or later required to create type2 STPs;
%put %str(WARN)ING: SAS version 9.3 or later required to create type2 STPs;
%return;
%end;
/* check we have the correct ServerContext */
@@ -311,7 +311,7 @@ run;
call symputx('serveruri',serveruri);
run;
%if &serveruri=NOTFOUND %then %do;
%put WARNING: ServerContext *&server* not found!;
%put %str(WARN)ING: ServerContext *&server* not found!;
%return;
%end;
@@ -382,7 +382,7 @@ run;
%end;
%else %do;
%put WARNING: STPTYPE=*&stptype* not recognised!;
%put %str(WARN)ING: STPTYPE=*&stptype* not recognised!;
%end;
%mend;

View File

@@ -32,7 +32,7 @@ data _null_;
call symputx('stpuri',uri,'l');
run;
%if &type ne Document %then %do;
%put WARNING: No Document found at &target;
%put %str(WARN)ING: No Document found at &target;
%return;
%end;

View File

@@ -125,7 +125,7 @@ data _null_;
when ('&#x0d;') rec='0D'x;
when ('&#36;' ) rec='$' ;
when ('&#x09;') rec='09'x;
otherwise putlog "WARNING: missing value for " entity=;
otherwise putlog "%str(WARN)ING: missing value for " entity=;
end;
rc =fput(fileid, substr(rec,1,1));
rc =fwrite(fileid);

View File

@@ -99,7 +99,7 @@ run;
when ('&#x0d;') rec='0D'x;
when ('&#36;' ) rec='$' ;
when ('&#x09;') rec='09'x;
otherwise putlog "WARNING: missing value for " entity=;
otherwise putlog "%str(WARN)ING: missing value for " entity=;
end;
rc =fput(fileid, substr(rec,1,1));
rc =fwrite(fileid);

View File

@@ -70,7 +70,7 @@ data _null_;
run;
%if &appuri=stopifempty %then %do;
%put WARNING: &app.(Application) not found!;
%put %str(WARN)ING: &app.(Application) not found!;
%return;
%end;

View File

@@ -58,12 +58,12 @@ data _null_;
run;
%if &tsuri=stopifempty %then %do;
%put WARNING: &path/&name.(Document) not found!;
%put %str(WARN)ING: &path/&name.(Document) not found!;
%return;
%end;
%if %length(&text)<2 %then %do;
%put WARNING: No text supplied!!;
%put %str(WARN)ING: No text supplied!!;
%return;
%end;

View File

@@ -34,7 +34,7 @@ data _null_;
call symputx('stpuri',uri,'l');
run;
%if &cmtype ne ClassifierMap %then %do;
%put WARNING: No Stored Process found at &target;
%put %str(WARN)ING: No Stored Process found at &target;
%return;
%end;

View File

@@ -68,12 +68,12 @@ data _null_;
run;
%if &tsuri=stopifempty %then %do;
%put WARNING: &stp.(StoredProcess) not found!;
%put %str(WARN)ING: &stp.(StoredProcess) not found!;
%return;
%end;
%if %length(&stpcode)<2 %then %do;
%put WARNING: No SAS code supplied!!;
%put %str(WARN)ING: No SAS code supplied!!;
%return;
%end;

Binary file not shown.

Before

Width:  |  Height:  |  Size: 57 KiB

After

Width:  |  Height:  |  Size: 146 KiB

View File

@@ -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,
@@ -11,7 +17,22 @@
}
},
"serviceConfig": {
"initProgram": "tests/testinit.sas"
"initProgram": "tests/testinit.sas",
"termProgram": "tests/testterm.sas",
"serviceFolders": [
"tests/base",
"tests/viya"
],
"macroVars": {
"mcTestAppLoc": "/Public/temp/macrocore"
}
},
"testConfig": {
"initProgram": "tests/testinit.sas",
"termProgram": "tests/testterm.sas",
"macroVars": {
"mcTestAppLoc": "/Public/temp/macrocore"
}
},
"defaultTarget": "viya",
"targets": [
@@ -20,15 +41,10 @@
"serverUrl": "https://sas.analytium.co.uk",
"serverType": "SASVIYA",
"appLoc": "/Public/temp/macrocore",
"serviceConfig": {
"serviceFolders": ["tests/viya"],
"macroVars": {
"mcTestAppLoc": "/Public/temp/macrocore"
}
},
"deployConfig": {
"deployServicePack": true
}
},
"contextName": "SAS Job Execution compute context"
}
]
}
}

View File

@@ -0,0 +1,32 @@
/**
@file
@brief Testing mp_assertcolvals macro
<h4> SAS Macros </h4>
@li mp_assertcolvals.sas
**/
data work.checkds;
do checkval='Jane','James','Jill';
output;
end;
run;
%mp_assertcolvals(sashelp.class.name,
checkvals=work.checkds.checkval,
desc=At least one value has a match,
test=ANYVAL
)
data work.check;
do val='M','F';
output;
end;
run;
%mp_assertcolvals(sashelp.class.sex,
checkvals=work.check.val,
desc=All values have a match,
test=ALLVALS
)

View File

@@ -0,0 +1,127 @@
/**
@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:$4000.;
datalines4;
AND,AND,1,AGE,=,12
AND,AND,1,SEX,<=,"'M'"
AND,OR,2,Name,NOT IN,"('Jane','Alfred')"
AND,OR,2,Weight,>=,77.7
AND,OR,2,Weight,NE,77.7
;;;;
run;
%mp_filtercheck(work.inds,
targetds=sashelp.class,
outds=work.badrecords,
abort=NO
)
%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:$4000.;
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,
abort=NO
)
%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:$4000.;
datalines4;
AND,OR,2,Name,NOT IN,"(''''Jane','Alfred')"
;;;;
run;
%mp_filtercheck(work.inds,
targetds=sashelp.class,
outds=work.badrecords,
abort=NO
)
%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:$4000.;
datalines4;
AND,AND,1,%abort,=,12
AND,OR,2,Weight,>=,7
;;;;
run;
%mp_filtercheck(work.inds,
targetds=sashelp.class,
outds=work.badrecords,
abort=NO
)
%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:$4000.;
datalines4;
AND,AND,1,age,=,;;%abort
;;;;
run;
%mp_filtercheck(work.inds,
targetds=sashelp.class,
outds=work.badrecords,
abort=NO
)
%let syscc=0;
%mp_assertdsobs(work.badrecords,
desc=Code injection - raw value abort,
test=HASOBS,
outds=work.test_results
)

View File

@@ -0,0 +1,122 @@
/**
@file
@brief Testing mp_filtergenerate macro
<h4> SAS Macros </h4>
@li mp_filtergenerate.sas
@li mp_filtercheck.sas
@li mp_assertdsobs.sas
**/
options source2;
/* 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:$4000.;
datalines4;
AND,AND,1,AGE,>,5
AND,AND,1,SEX,NE,"'M'"
AND,OR,2,Name,NOT IN,"('Jane','Janet')"
AND,OR,2,Weight,>=,84.6
;;;;
run;
%mp_filtercheck(work.inds,targetds=sashelp.class)
%mp_filtergenerate(work.inds,outref=myfilter)
data work.test;
set sashelp.class;
where %inc myfilter;;
run;
%mp_assertdsobs(work.test,
desc=Valid filter,
test=EQUALS 8,
outds=work.test_results
)
/* empty filter (return all records) */
data work.inds;
infile datalines4 dsd;
input GROUP_LOGIC:$3. SUBGROUP_LOGIC:$3. SUBGROUP_ID:8. VARIABLE_NM:$32.
OPERATOR_NM:$10. RAW_VALUE:$4000.;
datalines4;
;;;;
run;
%mp_filtercheck(work.inds,targetds=sashelp.class)
%mp_filtergenerate(work.inds,outref=myfilter)
data work.test;
set sashelp.class;
where %inc myfilter;;
run;
%mp_assertdsobs(work.test,
desc=Empty filter (return all records) ,
test=EQUALS 19,
outds=work.test_results
)
/* single line 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:$4000.;
datalines4;
AND,OR,2,Name,IN,"('Jane','Janet')"
;;;;
run;
%mp_filtercheck(work.inds,targetds=sashelp.class)
%mp_filtergenerate(work.inds,outref=myfilter)
data work.test;
set sashelp.class;
where %inc myfilter;;
run;
%mp_assertdsobs(work.test,
desc=Single line filter ,
test=EQUALS 2,
outds=work.test_results
)
/* single line 2 group 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:$4000.;
datalines4;
OR,OR,2,Name,IN,"('Jane','Janet')"
OR,OR,3,Name,IN,"('James')"
;;;;
run;
%mp_filtercheck(work.inds,targetds=sashelp.class)
%mp_filtergenerate(work.inds,outref=myfilter)
data work.test;
set sashelp.class;
where %inc myfilter;;
run;
%mp_assertdsobs(work.test,
desc=Single line 2 group filter ,
test=EQUALS 3,
outds=work.test_results
)
/* filter with nothing returned */
data work.inds;
infile datalines4 dsd;
input GROUP_LOGIC:$3. SUBGROUP_LOGIC:$3. SUBGROUP_ID:8. VARIABLE_NM:$32.
OPERATOR_NM:$10. RAW_VALUE:$4000.;
datalines4;
AND,OR,2,Name,IN,"('Jane','Janet')"
AND,OR,3,Name,IN,"('James')"
;;;;
run;
%mp_filtercheck(work.inds,targetds=sashelp.class)
%mp_filtergenerate(work.inds,outref=myfilter)
data work.test;
set sashelp.class;
where %inc myfilter;;
run;
%mp_assertdsobs(work.test,
desc=Filter with nothing returned,
test=EQUALS 0,
outds=work.test_results
)

View File

@@ -0,0 +1,68 @@
/**
@file
@brief Testing mp_filtervalidate macro
<h4> SAS Macros </h4>
@li mp_filtergenerate.sas
@li mp_filtervalidate.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:$4000.;
datalines4;
AND,AND,1,AGE,>,5
AND,AND,1,SEX,NE,"'M'"
AND,OR,2,Name,NOT IN,"('Jane','Janet')"
AND,OR,2,Weight,>=,84.6
;;;;
run;
%mp_filtergenerate(work.inds,outref=myfilter)
%mp_filtervalidate(myfilter,sashelp.class,outds=work.results,abort=NO)
%mp_assertdsobs(work.results,
desc=Valid filter,
test=EMPTY,
outds=work.test_results
)
/* empty filter (return all records) */
data work.inds;
infile datalines4 dsd;
input GROUP_LOGIC:$3. SUBGROUP_LOGIC:$3. SUBGROUP_ID:8. VARIABLE_NM:$32.
OPERATOR_NM:$10. RAW_VALUE:$4000.;
datalines4;
;;;;
run;
%mp_filtergenerate(work.inds,outref=myfilter)
%mp_filtervalidate(myfilter,sashelp.class,outds=work.results,abort=NO)
%mp_assertdsobs(work.results,
desc=Valid filter,
test=EMPTY,
outds=work.test_results
)
/* invalid 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:$4000.;
datalines4;
AND,AND,1,SEX,NE,2
;;;;
run;
%mp_filtergenerate(work.inds,outref=myfilter)
%mp_filtervalidate(myfilter,sashelp.class,outds=work.results,abort=NO)
%let syscc=0;
%mp_assertdsobs(work.results,
desc=Valid filter,
test=EQUALS 1,
outds=work.test_results
)

View File

@@ -0,0 +1,62 @@
/**
@file
@brief Testing mp_validatecol.sas macro
<h4> SAS Macros </h4>
@li mp_assertdsobs.sas
@li mp_validatecol.sas
**/
/**
* Test 1 - LIBDS
*/
data test1;
infile datalines4 dsd;
input;
libds=_infile_;
%mp_validatecol(libds,LIBDS,is_libds)
if libds=1;
datalines4;
some.libname
!lib.blah
%abort
definite.ok
not.ok!
nineletrs._
;;;;
run;
%mp_assertdsobs(work.test1,
desc=Testing LIBDS,
test=EQUALS 2,
outds=work.test_results
)
/**
* Test 2 - ISNUM
*/
data test2;
infile datalines4 dsd;
input;
infile=_infile_;
%mp_validatecol(infile,ISNUM,is_numeric)
if is_numeric=1;
datalines4;
1
0001
1e6
-44
above are good
the rest are bad
%abort
1&somethingverybad.
&
+-1
;;;;
run;
%mp_assertdsobs(work.test2,
desc=Test2 - ISNUM,
test=EQUALS 4,
outds=work.test_results
)

9
tests/testterm.sas Normal file
View File

@@ -0,0 +1,9 @@
/**
@file
@brief term file for tests
**/
%webout(OPEN)
%webout(OBJ, TEST_RESULTS)
%webout(CLOSE)

View File

@@ -4,6 +4,7 @@
<h4> SAS Macros </h4>
@li mv_createwebservice.sas
@li mv_getjobcode.sas
**/
@@ -21,4 +22,20 @@ run;
path=&mcTestAppLoc/tests/macros,
code=testref,
name=mv_createwebservice
)
)
filename compare temp;
%mv_getjobcode(
path=&mcTestAppLoc/tests/macros
,name=mv_createwebservice
,outref=compare;
)
data test_results;
length test_description $256 test_result $4 test_comments $256;
infile compare;
input;
if _infile_='01'x then test_result='PASS';
else test_result='FAIL';
test_description="Creating web service with invisible character";
run;