sasjs lint (@sasjs/cli 6.2.5) reported 24 warnings across the 11 .sas files
this release touches. None of them are in those files now:
- mpeinit and mpeterm are added to ignoredMacros in sas/.sasjslint. Every
service is compiled with a serviceInit program (sasjs/utils/
serviceinitsas9|viya|server.sas), and all three declare both macros in
their own header, which is how a service body's %mpeinit()/%mpeterm()
resolves without a declaration of its own. An explicit ignoredMacros list
replaces the default, so webout stays listed.
- Header entries nothing in the file calls are dropped: mp_binarycopy in
getrawdata (it arrives through mp_streamfile, which calls it - the
compiled program is unchanged), mp_jsonout in viewdata,
mp_assertcolvals in validatefilter.test, and mx_execute in testsetup
(every file that calls it declares it).
- Prose carrying "error"/"warning" is reworded. Where the text is log
output, %put WARNING: is written %put %str(WARN)ING: so the line the
log receives is unchanged.
- The server makedata service keeps its doxygen @warning tag - the tag is
the content - so that file carries @sasjslint {"noBannedStrings": false}.
- Two comment lines in getdata are re-indented to the file's multiple of 2.
AGENTS.md records the ignoredMacros rule and the %str(WARN)ING convention so
the next warning is not silenced by reflex. Compile passes; the 93 warnings
that remain are all in files this release does not touch.
251 lines
6.5 KiB
SAS
251 lines
6.5 KiB
SAS
/**
|
|
@file
|
|
@brief Downloads data in a variety of formats
|
|
@details To enable direct download, this service runs in a dedicated stream
|
|
as a GET request using URL parameters as inputs.
|
|
|
|
The inputs are:
|
|
|
|
@li table - the libds of the table to be downloaded
|
|
@li type - either SAS, CSV, EXCEL, MARKDOWN, WEBCSV or WEBTAB
|
|
@li filter - the filter RK if used
|
|
|
|
<h4> SAS Macros </h4>
|
|
@li mf_getuser.sas
|
|
@li mf_existfeature.sas
|
|
@li dc_assignlib.sas
|
|
@li mp_abort.sas
|
|
@li mp_cntlout.sas
|
|
@li mp_ds2cards.sas
|
|
@li mp_ds2csv.sas
|
|
@li mp_ds2md.sas
|
|
@li mp_streamfile.sas
|
|
@li mp_validatecol.sas
|
|
@li mf_nobs.sas
|
|
@li mpe_derived.sas
|
|
@li mpe_validatecol.sas
|
|
@li mpe_filtermaster.sas
|
|
|
|
|
|
@version 9.2
|
|
@author 4GL Apps Ltd
|
|
@copyright 4GL Apps Ltd. This code may only be used within Data Controller
|
|
and may not be re-distributed or re-sold without the express permission of
|
|
4GL Apps Ltd.
|
|
|
|
**/
|
|
%global table type filter ds format is_fmt txfrom txto;
|
|
%mpeinit()
|
|
|
|
%let user=%mf_getuser();
|
|
%let is_fmt=0;
|
|
|
|
/**
|
|
* Validate inputs before they reach executable code. table is used as a
|
|
* dataset reference, in the output file path, and in the download filename,
|
|
* so it must be a well-formed LIBREF.DATASET (the trailing -FC catalog
|
|
* suffix is permitted); filter must be an integer. Values are read with
|
|
* symget (never re-resolved) and validated in a data step so no macro
|
|
* content in the input can execute.
|
|
*/
|
|
%let is_libds=0;
|
|
%let is_int=0;
|
|
%let is_type=0;
|
|
data _null_;
|
|
length _table $64 _filter $16 _type $16;
|
|
_type=upcase(coalescec(symget('type'),''));
|
|
_table=coalescec(symget('table'),'');
|
|
_filter=coalescec(symget('filter'),'0');
|
|
if missing(_table) then do;
|
|
putlog 'ERR' 'OR: Missing table';
|
|
stop;
|
|
end;
|
|
%mpe_validatecol(_table,LIBDS,is_libds)
|
|
/* an absent filter is a valid, unfiltered download */
|
|
if missing(_filter) then _filter='0';
|
|
%mp_validatecol(_filter,ISINT,is_int)
|
|
/* type is validated against a fixed list of download formats */
|
|
length _types_ok 8;
|
|
_types_ok=0;
|
|
if _type in ('SAS','CSV','EXCEL','MARKDOWN','WEBCSV','WEBTAB')
|
|
then _types_ok=1;
|
|
else putlog 'ERR' 'OR: Invalid type:' _type;
|
|
if is_libds=0 then putlog 'ERR' 'OR: Invalid table:' _table;
|
|
if is_int=0 then putlog 'ERR' 'OR: Invalid filter:' _filter;
|
|
call symputx('is_libds',is_libds,'l');
|
|
call symputx('is_int',is_int,'l');
|
|
call symputx('is_type',_types_ok,'l');
|
|
call symputx('filter',_filter,'l');
|
|
if is_libds=1 then call symputx('table',upcase(_table),'l');
|
|
if _types_ok=1 then call symputx('type',_type,'l');
|
|
run;
|
|
|
|
%mp_abort(iftrue= (&is_libds ne 1)
|
|
,mac=&_program..sas
|
|
,msg=%str(Invalid table)
|
|
)
|
|
%mp_abort(iftrue= (&is_int ne 1)
|
|
,mac=&_program..sas
|
|
,msg=%str(Invalid filter)
|
|
)
|
|
%mp_abort(iftrue= (&is_type ne 1)
|
|
,mac=&_program..sas
|
|
,msg=%str(Invalid type)
|
|
)
|
|
|
|
%let libds=%upcase(&table); /* actual source */
|
|
%let table=%upcase(&table); /* used as label for fmt catalogs */
|
|
%let lib=%scan(&table,1,.);
|
|
%let ds=%scan(&table,2,.);
|
|
%dc_assignlib(READ,&lib)
|
|
|
|
data _null_;
|
|
set &mpelib..MPE_TABLES;
|
|
where upcase(libref)="&lib" and upcase(dsn)="&ds";
|
|
|
|
/* if a TXTEMPORAL table then filter as such */
|
|
call symputx('txfrom',var_txfrom);
|
|
call symputx('txto',var_txto);
|
|
run;
|
|
|
|
/* if a format, extract relevant info */
|
|
data _null_;
|
|
ds=symget('ds');
|
|
is_fmt=0;
|
|
if subpad(cats(reverse(ds)),1,3)=:'CF-' then do;
|
|
ds=scan(ds,1,'-');
|
|
table=cats("&lib..",ds);
|
|
putlog "Format Catalog Captured";
|
|
is_fmt=1;
|
|
call symputx('libds','work.fmtextract');
|
|
call symputx('table',table);
|
|
end;
|
|
call symputx('is_fmt',is_fmt);
|
|
putlog (_all_)(=);
|
|
run;
|
|
|
|
%mp_cntlout(
|
|
iftrue=(&is_fmt=1)
|
|
,libcat=&table
|
|
,fmtlist=0
|
|
,cntlout=work.fmtextract
|
|
)
|
|
|
|
%put preparing query;
|
|
%mpe_filtermaster(DLOAD,&libds,
|
|
dclib=&mpelib,
|
|
filter_rk=&filter,
|
|
outref=filtref,
|
|
outds=work.query
|
|
)
|
|
%put printing generated filterquery:;
|
|
data _null_;
|
|
infile filtref;
|
|
input;
|
|
putlog _infile_;
|
|
run;
|
|
|
|
/* derived variables exist only in a view, so a filter that references one
|
|
must be applied against it. They are dropped from the download - they are
|
|
an EDIT screen feature. */
|
|
%mpe_derived(libds=&libds,
|
|
dclib=&mpelib,
|
|
outref=dercode,
|
|
outview=work.vw_derived,
|
|
outvars=work.derived_vars
|
|
)
|
|
%let derived_nobs=%mf_nobs(work.derived_vars);
|
|
%let derived_droplist=;
|
|
%let filterds=%sysfunc(ifc(&derived_nobs>0,work.vw_derived,&libds));
|
|
%if &derived_nobs>0 %then %do;
|
|
proc sql noprint;
|
|
select name into: derived_droplist separated by ' '
|
|
from work.derived_vars;
|
|
quit;
|
|
%end;
|
|
|
|
options obs=200000;/* stop limit */
|
|
data staged(drop=&txfrom &txto);
|
|
set &filterds;
|
|
where %inc filtref;;
|
|
%if %length(&derived_droplist)>0 %then %do;
|
|
drop &derived_droplist;
|
|
%end;
|
|
run;
|
|
options obs=max;
|
|
|
|
options validvarname=upcase;
|
|
|
|
%macro mpestp_getrawdata();
|
|
%local outfile;
|
|
%if &type=SAS %then %do;
|
|
%let outfile=%sysfunc(pathname(work))/&table..sas;
|
|
%mp_ds2cards(base_ds=staged
|
|
, tgt_ds=&table
|
|
, cards_file= "&outfile"
|
|
, maxobs=100000)
|
|
%let ext=sas;
|
|
%let mimetype=text;
|
|
%end;
|
|
%else %if &type=CSV or (&type=EXCEL and %mf_existfeature(EXPORTXLS) ne 1)
|
|
/* cannot proc export excel if PC Files is not licensed */
|
|
%then %do;
|
|
%let outfile=%sysfunc(pathname(work))/&table..csv;
|
|
/* cannot use PROC EXPORT as we need to wrap all char values in quotes */
|
|
%mp_ds2csv(work.staged,outfile="&outfile",headerformat=NAME)
|
|
%let ext=csv;
|
|
%let mimetype=csv;
|
|
%end;
|
|
%else %if &type=EXCEL %then %do;
|
|
%let ext=xlsx;
|
|
%let outfile=%sysfunc(pathname(work))/&table..&ext;
|
|
PROC EXPORT DATA= staged
|
|
OUTFILE= "&outfile"
|
|
DBMS=xlsx ;
|
|
RUN;
|
|
%let mimetype=XLSX;
|
|
%end;
|
|
%else %if &type=MARKDOWN %then %do;
|
|
%let ext=md;
|
|
%let outfile=%sysfunc(pathname(work))/&table..&ext;
|
|
filename mdref "&outfile" lrecl=32767;
|
|
|
|
%mp_ds2md(staged,outref=mdref,showlog=NO)
|
|
|
|
%let mimetype=MARKDOWN;
|
|
%end;
|
|
%else %if &type=WEBCSV %then %do;
|
|
PROC EXPORT DATA= staged
|
|
OUTFILE= _webout
|
|
DBMS=csv REPLACE;
|
|
RUN;
|
|
/* don't set headers */
|
|
%return;
|
|
%end;
|
|
%else %if &type=WEBTAB %then %do;
|
|
PROC EXPORT DATA= staged
|
|
OUTFILE= _webout
|
|
DBMS=tab REPLACE;
|
|
RUN;
|
|
/* don't set headers */
|
|
%return;
|
|
%end;
|
|
%else %do;
|
|
%mp_abort(msg=type &type not supported,mac=mpestp_getrawdata.sas);
|
|
%end;
|
|
|
|
%mp_abort(iftrue= (&syscc ne 0)
|
|
,mac=&_program..sas
|
|
,msg=%str(syscc=&syscc)
|
|
)
|
|
|
|
%mp_streamfile(contenttype=&mimetype
|
|
,inloc=%str(&outfile)
|
|
,outname=&table..&ext
|
|
)
|
|
|
|
%mend mpestp_getrawdata;
|
|
%mpestp_getrawdata()
|
|
|
|
%mpeterm()
|