/** @file @brief Send data to/from the SAS Viya Job Execution Service @details This macro should be added to the start of each Job Execution Service, **immediately** followed by a call to: %mv_webout(FETCH) This will read all the input data and create same-named SAS datasets in the WORK library. You can then insert your code, and send data back using the following syntax: data some datasets; * make some data ; retain some columns; run; %mv_webout(OPEN) %mv_webout(ARR,some) * Array format, fast, suitable for large tables ; %mv_webout(OBJ,datasets) * Object format, easier to work with ; %mv_webout(CLOSE) @param action Either OPEN, ARR, OBJ or CLOSE @param ds The dataset to send back to the frontend @param _webout= fileref for returning the json @param fref=(_mvwtemp) Temp fileref to which to write the output @param dslabel= value to use instead of the real name for sending to JSON @param fmt=(Y) change to N to strip formats from output @param stream=(Y) Change to N if not streaming to _webout

SAS Macros

@li mp_jsonout.sas @li mf_getuser.sas @version Viya 3.3 @author Allan Bowe, source: https://github.com/sasjs/core **/ %macro mv_webout(action,ds,fref=_mvwtemp,dslabel=,fmt=Y,stream=Y); %global _webin_file_count _webin_fileuri _debug _omittextlog _webin_name sasjs_tables SYS_JES_JOB_URI; %if %index("&_debug",log) %then %let _debug=131; %local i tempds; %let action=%upcase(&action); %if &action=FETCH %then %do; %if %upcase(&_omittextlog)=FALSE or %str(&_debug) ge 131 %then %do; options mprint notes mprintnest; %end; %if not %symexist(_webin_fileuri1) %then %do; %let _webin_file_count=%eval(&_webin_file_count+0); %let _webin_fileuri1=&_webin_fileuri; %let _webin_name1=&_webin_name; %end; /* if the sasjs_tables param is passed, we expect param based upload */ %if %length(&sasjs_tables.XX)>2 %then %do; filename _sasjs "%sysfunc(pathname(work))/sasjs.lua"; data _null_; file _sasjs; put 's=sas.symget("sasjs_tables")'; put 'if(s:sub(1,7) == "%nrstr(")'; put 'then'; put ' tablist=s:sub(8,s:len()-1)'; put 'else'; put ' tablist=s'; put 'end'; put 'for i = 1,sas.countw(tablist) '; put 'do '; put ' tab=sas.scan(tablist,i)'; put ' sasdata=""'; put ' if (sas.symexist("sasjs"..i.."data0")==0)'; put ' then'; /* TODO - condense this logic */ put ' s=sas.symget("sasjs"..i.."data")'; put ' if(s:sub(1,7) == "%nrstr(")'; put ' then'; put ' sasdata=s:sub(8,s:len()-1)'; put ' else'; put ' sasdata=s'; put ' end'; put ' else'; put ' for d = 1, sas.symget("sasjs"..i.."data0")'; put ' do'; put ' s=sas.symget("sasjs"..i.."data"..d)'; put ' if(s:sub(1,7) == "%nrstr(")'; put ' then'; put ' sasdata=sasdata..s:sub(8,s:len()-1)'; put ' else'; put ' sasdata=sasdata..s'; put ' end'; put ' end'; put ' end'; put ' file = io.open(sas.pathname("work").."/"..tab..".csv", "a")'; put ' io.output(file)'; put ' io.write(sasdata)'; put ' io.close(file)'; put 'end'; run; %inc _sasjs; /* now read in the data */ %do i=1 %to %sysfunc(countw(&sasjs_tables)); %local table; %let table=%scan(&sasjs_tables,&i); data _null_; infile "%sysfunc(pathname(work))/&table..csv" termstr=crlf ; input; if _n_=1 then call symputx('input_statement',_infile_); list; data &table; infile "%sysfunc(pathname(work))/&table..csv" firstobs=2 dsd termstr=crlf; input &input_statement; run; %end; %end; %else %do i=1 %to &_webin_file_count; /* read in any files that are sent */ /* this part needs refactoring for wide files */ filename indata filesrvc "&&_webin_fileuri&i" lrecl=999999; data _null_; infile indata termstr=crlf lrecl=32767; input; if _n_=1 then call symputx('input_statement',_infile_); %if %str(&_debug) ge 131 %then %do; if _n_<20 then putlog _infile_; else stop; %end; %else %do; stop; %end; run; data &&_webin_name&i; infile indata firstobs=2 dsd termstr=crlf ; input &input_statement; run; %let sasjs_tables=&sasjs_tables &&_webin_name&i; %end; %end; %else %if &action=OPEN %then %do; /* setup webout */ OPTIONS NOBOMFILE; %if "X&SYS_JES_JOB_URI.X"="XX" %then %do; filename _webout temp lrecl=999999 mod; %end; %else %do; filename _webout filesrvc parenturi="&SYS_JES_JOB_URI" name="_webout.json" lrecl=999999 mod; %end; /* setup temp ref */ %if %upcase(&fref) ne _WEBOUT %then %do; filename &fref temp lrecl=999999 permission='A::u::rwx,A::g::rw-,A::o::---' mod; %end; /* setup json */ data _null_;file &fref; put '{"START_DTTM" : "' "%sysfunc(datetime(),datetime20.3)" '"'; run; %end; %else %if &action=ARR or &action=OBJ %then %do; %mp_jsonout(&action,&ds,dslabel=&dslabel,fmt=&fmt ,jref=&fref,engine=DATASTEP,dbg=%str(&_debug) ) %end; %else %if &action=CLOSE %then %do; %if %str(&_debug) ge 131 %then %do; /* send back first 10 records of each work table for debugging */ options obs=10; data;run;%let tempds=%scan(&syslast,2,.); ods output Members=&tempds; proc datasets library=WORK memtype=data; %local wtcnt;%let wtcnt=0; data _null_; set &tempds; if not (name =:"DATA"); i+1; call symputx('wt'!!left(i),name); call symputx('wtcnt',i); data _null_; file &fref mod; put ",""WORK"":{"; %do i=1 %to &wtcnt; %let wt=&&wt&i; proc contents noprint data=&wt out=_data_ (keep=name type length format:); run;%let tempds=%scan(&syslast,2,.); data _null_; file &fref mod; dsid=open("WORK.&wt",'is'); nlobs=attrn(dsid,'NLOBS'); nvars=attrn(dsid,'NVARS'); rc=close(dsid); if &i>1 then put ','@; put " ""&wt"" : {"; put '"nlobs":' nlobs; put ',"nvars":' nvars; %mp_jsonout(OBJ,&tempds,jref=&fref,dslabel=colattrs,engine=DATASTEP) %mp_jsonout(OBJ,&wt,jref=&fref,dslabel=first10rows,engine=DATASTEP) data _null_; file &fref mod;put "}"; %end; data _null_; file &fref mod;put "}";run; %end; /* close off json */ data _null_;file &fref mod; _PROGRAM=quote(trim(resolve(symget('_PROGRAM')))); put ",""SYSUSERID"" : ""&sysuserid"" "; put ",""MF_GETUSER"" : ""%mf_getuser()"" "; SYS_JES_JOB_URI=quote(trim(resolve(symget('SYS_JES_JOB_URI')))); put ',"SYS_JES_JOB_URI" : ' SYS_JES_JOB_URI ; put ",""SYSJOBID"" : ""&sysjobid"" "; put ",""_DEBUG"" : ""&_debug"" "; put ',"_PROGRAM" : ' _PROGRAM ; put ",""SYSCC"" : ""&syscc"" "; put ",""SYSERRORTEXT"" : ""&syserrortext"" "; put ",""SYSHOSTNAME"" : ""&syshostname"" "; put ",""SYSSITE"" : ""&syssite"" "; sysvlong=quote(trim(symget('sysvlong'))); put ',"SYSVLONG" : ' sysvlong; put ",""SYSWARNINGTEXT"" : ""&syswarningtext"" "; put ',"END_DTTM" : "' "%sysfunc(datetime(),datetime20.3)" '" '; put "}"; %if %upcase(&fref) ne _WEBOUT and &stream=Y %then %do; data _null_; rc=fcopy("&fref","_webout");run; %end; %end; %mend mv_webout;