From 900120df1ba7af5c70b15c2bb56eba835e7a8552 Mon Sep 17 00:00:00 2001 From: Trevor Moody Date: Wed, 30 Oct 2024 19:22:02 +0000 Subject: [PATCH] feat: Triggering of stored processes. Closes #378 --- server/ms_triggerstp.sas | 233 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 233 insertions(+) create mode 100644 server/ms_triggerstp.sas diff --git a/server/ms_triggerstp.sas b/server/ms_triggerstp.sas new file mode 100644 index 00000000..3deb50d9 --- /dev/null +++ b/server/ms_triggerstp.sas @@ -0,0 +1,233 @@ +/** + @file + @brief Triggers a SASjs Server STP using the /SASjsApi/stp/trigger endpoint + @details Triggers the STP and returns the sessionId + + Example: + + %ms_triggerstp(/some/stored/program + ,debug=131 + ,outds=work.myresults + ) + + @param [in] pgm The full path to the Stored Program in SASjs Drive (_program + parameter) + @param [in] debug= (131) The value to supply to the _debug URL parameter + @param [in] mdebug= (0) Set to 1 to enable DEBUG messages + @param [in] inputparams=(_null_) A dataset containing name/value pairs in the + following format: + |name:$32|value:$10000| + |---|---| + |stpmacname|some value| + |mustbevalidname|can be anything, oops, %abort!!| + @param [in] inputfiles= (_null_) A dataset containing fileref/name/filename in + the following format: + |fileref:$8|name:$32|filename:$256| + |---|---|--| + |someref|some_name|some_filename.xls| + |fref2|another_file|zyx_v2.csv| + @param [in] expiresaftermins= (15) The number of minutes to retain the session + folder after the session ends. + + @param [out] outds= (work.ms_triggerstp) Set to the name of a dataset to + contain the sessionId. If this dataset already exists, and contains the + sessionId, it will be appended to. + Format: + |sessionId:$36| + |---| + |20241028074744-54132-1730101664824| + +

SAS Macros

+ @li mf_getuniquefileref.sas + @li mf_getuniquename.sas + @li mp_abort.sas + @li mp_dropmembers.sas + @li mf_nobs.sas + +**/ + +%macro ms_triggerstp(pgm + ,debug=131 + ,inputparams=_null_ + ,inputfiles=_null_ + ,expiresAfterMins=15 + ,outds=work.ms_triggerstp + ,mdebug=0 + ); + %local dbg mainref authref boundary triggered_sid; + %let mainref=%mf_getuniquefileref(); + %let authref=%mf_getuniquefileref(); + %let boundary=%mf_getuniquename(); + %if &inputparams=0 %then %let inputparams=_null_; + + %if &mdebug=1 %then %do; + %put &sysmacroname entry vars:; + %put _local_; + %end; + %else %let dbg=*; + + + %mp_abort(iftrue=("&pgm"="") + ,mac=&sysmacroname + ,msg=%str(Program not provided) + ) + %mp_abort(iftrue=("&outds"="") + ,mac=&sysmacroname + ,msg=%str(Output dataset not provided) + ) + + /* avoid sending bom marker to API */ + %local optval; + %let optval=%sysfunc(getoption(bomfile)); + options nobomfile; + + /* Add header to the content of the http request */ + data _null_; + file &mainref termstr=crlf lrecl=32767; + put "--&boundary"; + run; + + /* Add params to the content */ + data _null_; + file &mainref termstr=crlf lrecl=32767 mod; + length line $1000 name $32 value $32767; + if _n_=1 then call missing(of _all_); + set &inputparams; + line=cats('Content-Disposition: form-data; name="',name,'"'); + put line; + put ; + put value; + run; + + /* parse input file list */ + %local webcount; + %let webcount=0; + data _null_; + set &inputfiles end=last; + length fileref $8 name $32 filename $256; + call symputx(cats('webref',_n_),fileref,'l'); + call symputx(cats('webname',_n_),name,'l'); + call symputx(cats('webfilename',_n_),filename,'l'); + if last then do; + call symputx('webcount',_n_); + call missing(of _all_); + end; + run; + + /* write out the input files to the content */ + %local i; + %do i=1 %to &webcount; + data _null_; + file &mainref termstr=crlf lrecl=32767 mod; + infile &&webref&i lrecl=32767; + if _n_ = 1 then do; + length line $32767; + line=cats( + 'Content-Disposition: form-data; name="' + ,"&&webname&i" + ,'"; filename="' + ,"&&webfilename&i" + ,'"' + ); + put "--&boundary"; + put line; + put "Content-Type: text/plain"; + put ; + end; + input; + put _infile_; /* add the actual file to be sent */ + run; + %end; + + /* Add footer to the content */ + data _null_; + file &mainref termstr=crlf mod; + put / "--&boundary--"; + run; + + data _null_; + file &authref lrecl=1000; + infile "&_sasjs_tokenfile" lrecl=1000; + input; + if _n_=1 then put "Content-Type: multipart/form-data; boundary=&boundary"; + put _infile_; + run; + + %if &mdebug=1 %then %do; + data _null_; + if _n_ eq 1 then putlog "NOTE: ***** authref=&authref content *****"; + infile &authref; + input; + put _infile_; + data _null_; + if _n_ eq 1 then putlog "NOTE: ***** mainref=&mainref content *****"; + infile &mainref; + input; + put _infile_; + run; + %end; + + %local resp_path outref; + %let resp_path=%sysfunc(pathname(work))/%mf_getuniquename(); + %let outref=%mf_getuniquefileref(); + filename &outref "&resp_path" lrecl=32767; + + /* prepare request*/ + proc http method='POST' headerin=&authref in=&mainref out=&outref + url="&_sasjs_apiserverurl/SASjsApi/stp/trigger?%trim( + )_program=&pgm%str(&)_debug=131%str(&)expiresAfterMins=&expiresaftermins"; + %if &mdebug=1 %then %do; + debug level=2; + %end; + run; + + %if (&SYS_PROCHTTP_STATUS_CODE ne 200 and &SYS_PROCHTTP_STATUS_CODE ne 201) + or &mdebug=1 + %then %do; + data _null_; + if _n_ eq 1 then putlog "NOTE: ***** outref=&outref content *****"; + infile &outref; + input; + putlog _infile_; + run; + %end; + %mp_abort( + iftrue=(&SYS_PROCHTTP_STATUS_CODE ne 200 + and &SYS_PROCHTTP_STATUS_CODE ne 201) + ,mac=&sysmacroname + ,msg=%str(&SYS_PROCHTTP_STATUS_CODE &SYS_PROCHTTP_STATUS_PHRASE) + ) + + /* reset options */ + options &optval; + + libname response JSON fileref=&outref; + %let triggered_sid=%mf_getuniquename(prefix=triggered_sid_); + + data work.&triggered_sid (keep=sessionid); + set response.root; + + %if &mdebug=1 %then %do; + putlog (_all_)(=); + %end; + run; + + %if %mf_nobs(work.&triggered_sid)>0 %then %do; + proc append base=&outds data=work.&triggered_sid; + run; + %end; + + %if &mdebug=1 %then %do; + %put &sysmacroname exit vars:; + %put _local_; + %end; + %else %do; + /* clear refs */ + filename &authref; + filename &mainref; + filename &outref; + /* and remove temp dataset */ + %mp_dropmembers(&triggered_sid,libref=work); + %end; + +%mend ms_triggerstp; \ No newline at end of file