Files
dc/sas/sasjs/services/public/getrawdata.sas
T
dc 355c46a7b6
Build / Build-and-ng-test (pull_request) Successful in 5m30s
Lighthouse Checks / lighthouse (pull_request) Canceled after 14m27s
Build / Build-and-test-development (pull_request) Failing after 34m21s
fix(lint): clear the sasjs lint warnings on the files 7.19 touched
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.
2026-10-27 21:27:54 +00:00

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()