diff --git a/all.sas b/all.sas index cbfafcf..9f17e8d 100644 --- a/all.sas +++ b/all.sas @@ -3883,8 +3883,15 @@ Usage: ,replace=&replace ) %end; +%else %if &platform=SASJS %then %do; + %if "&path"="HOME" %then %let path=/Users/&_sasjs_username/My Folder; + %ms_createfile(&path/&name..sas + ,inref=&code + ,prerefs=&precode + ) +%end; %else %do; - %if "&path"="HOME" %then %let path=/User Folders/&sysuserid/My Folder; + %if "&path"="HOME" %then %let path=/User Folders/&_METAPERSON/My Folder; %mm_createwebservice(path=&path ,name=&name ,code=&code @@ -15052,6 +15059,25 @@ data _null_; put ' run; '; put '%end; '; put '%mend mp_jsonout; '; + put ' '; + put '%macro mf_getuser(type=META '; + put ')/*/STORE SOURCE*/; '; + put ' %local user metavar; '; + put ' %if &type=OS %then %let metavar=_secureusername; '; + put ' %else %let metavar=_metaperson; '; + put ' '; + put ' %if %symexist(SYS_COMPUTE_SESSION_OWNER) %then %let user=&SYS_COMPUTE_SESSION_OWNER; '; + put ' %else %if %symexist(&metavar) %then %do; '; + put ' %if %length(&&&metavar)=0 %then %let user=&sysuserid; '; + put ' /* sometimes SAS will add @domain extension - remove for consistency */ '; + put ' /* but be sure to quote in case of usernames with commas */ '; + put ' %else %let user=%unquote(%scan(%quote(&&&metavar),1,@)); '; + put ' %end; '; + put ' %else %let user=&sysuserid; '; + put ' '; + put ' %quote(&user) '; + put ' '; + put '%mend mf_getuser; '; put '%macro mm_webout(action,ds,dslabel=,fref=_webout,fmt=Y,missing=NULL '; put ' ,showmeta=NO '; put '); '; @@ -15193,25 +15219,6 @@ data _null_; put '%end; '; put ' '; put '%mend mm_webout; '; - put ' '; - put '%macro mf_getuser(type=META '; - put ')/*/STORE SOURCE*/; '; - put ' %local user metavar; '; - put ' %if &type=OS %then %let metavar=_secureusername; '; - put ' %else %let metavar=_metaperson; '; - put ' '; - put ' %if %symexist(SYS_COMPUTE_SESSION_OWNER) %then %let user=&SYS_COMPUTE_SESSION_OWNER; '; - put ' %else %if %symexist(&metavar) %then %do; '; - put ' %if %length(&&&metavar)=0 %then %let user=&sysuserid; '; - put ' /* sometimes SAS will add @domain extension - remove for consistency */ '; - put ' /* but be sure to quote in case of usernames with commas */ '; - put ' %else %let user=%unquote(%scan(%quote(&&&metavar),1,@)); '; - put ' %end; '; - put ' %else %let user=&sysuserid; '; - put ' '; - put ' %quote(&user) '; - put ' '; - put '%mend mf_getuser; '; /* WEBOUT END */ put '%macro webout(action,ds,dslabel=,fmt=,missing=NULL,showmeta=NO);'; put ' %mm_webout(&action,ds=&ds,dslabel=&dslabel,fmt=&fmt,missing=&missing'; @@ -19167,6 +19174,528 @@ options &optval; %end; %mend ms_createuser; +/** + @file ms_createwebservice.sas + @brief Create a Web-Ready Stored Program + @details This macro creates a Stored Program along with the necessary precode + to enable the %webout() macro + + Usage: + + %* compile macros ; + filename mc url "https://raw.githubusercontent.com/sasjs/core/main/all.sas"; + %inc mc; + + %* parmcards lets us write to a text file from open code ; + filename ft15f001 temp; + parmcards4; + %webout(FETCH) + %* do some sas, any inputs are now already WORK tables; + data example1 example2; + set sashelp.class; + run; + %* send data back; + %webout(OPEN) + %webout(ARR,example1) * Array format, fast, suitable for large tables ; + %webout(OBJ,example2) * Object format, easier to work with ; + %webout(CLOSE) + ;;;; + %ms_createwebservice(path=/Public/app/common,name=appInit,code=ft15f001) + + + + For more examples of using these web services with the SASjs Adapter, see: + https://github.com/sasjs/adapter#readme + + @param [in] path= The full SASjs Drive path in which to create the service + @param [in] name= Stored Program name + @param [in] desc= The description of the service (not implemented yet) + @param [in] precode= Space separated list of filerefs, pointing to the code + that needs to be attached to the beginning of the service (optional) + @param [in] code= (ft15f001) Space seperated fileref(s) of the actual code to + be added + @param [in] mDebug= (0) set to 1 to show debug messages in the log + +

SAS Macros

+ @li ms_createfile.sas + @li mf_getuser.sas + @li mf_getuniquename.sas + + @version 9.2 + @author Allan Bowe + +**/ + +%macro ms_createwebservice(path= + ,name=initService + ,precode= + ,code=ft15f001 + ,desc=Not currently used + ,mDebug=0 +)/*/STORE SOURCE*/; + +%if &syscc ge 4 %then %do; + %put &=syscc - &sysmacroname will not execute in this state; + %return; +%end; + +%local mD; +%if &mDebug=1 %then %let mD=; +%else %let mD=%str(*); +%&mD.put Executing ms_createwebservice.sas; +%&mD.put _local_; + +* remove any trailing slash ; +%if "%substr(&path,%length(&path),1)" = "/" %then + %let path=%substr(&path,1,%length(&path)-1); + +/** + * Add webout macro + * These put statements are auto generated - to change the macro, change the + * source (ms_webout) and run `build.py` + */ +filename sasjs temp; +data _null_; + file sasjs lrecl=3000 ; + put "/* Created on %sysfunc(datetime(),datetime19.) by %mf_getuser() */"; +/* WEBOUT BEGIN */ + put ' '; + put '%macro mp_jsonout(action,ds,jref=_webout,dslabel=,fmt=Y '; + put ' ,engine=DATASTEP '; + put ' ,missing=NULL '; + put ' ,showmeta=NO '; + put ')/*/STORE SOURCE*/; '; + put '%local tempds colinfo fmtds i numcols; '; + put '%let numcols=0; '; + put ' '; + put '%if &action=OPEN %then %do; '; + put ' options nobomfile; '; + put ' data _null_;file &jref encoding=''utf-8'' ; '; + put ' put ''{"PROCESSED_DTTM" : "'' "%sysfunc(datetime(),E8601DT26.6)" ''"''; '; + put ' run; '; + put '%end; '; + put '%else %if (&action=ARR or &action=OBJ) %then %do; '; + put ' options validvarname=upcase; '; + put ' data _null_; file &jref encoding=''utf-8'' mod; '; + put ' put ", ""%lowcase(%sysfunc(coalescec(&dslabel,&ds)))"":"; '; + put ' '; + put ' /* grab col defs */ '; + put ' proc contents noprint data=&ds '; + put ' out=_data_(keep=name type length format formatl formatd varnum label); '; + put ' run; '; + put ' %let colinfo=%scan(&syslast,2,.); '; + put ' proc sort data=&colinfo; '; + put ' by varnum; '; + put ' run; '; + put ' /* move meta to mac vars */ '; + put ' data _null_; '; + put ' if _n_=1 then call symputx(''numcols'',nobs,''l''); '; + put ' set &colinfo end=last nobs=nobs; '; + put ' name=upcase(name); '; + put ' /* fix formats */ '; + put ' if type=2 or type=6 then do; '; + put ' typelong=''char''; '; + put ' length fmt $49.; '; + put ' if format='''' then fmt=cats(''$'',length,''.''); '; + put ' else if formatl=0 then fmt=cats(format,''.''); '; + put ' else fmt=cats(format,formatl,''.''); '; + put ' newlen=max(formatl,length); '; + put ' end; '; + put ' else do; '; + put ' typelong=''num''; '; + put ' if format='''' then fmt=''best.''; '; + put ' else if formatl=0 then fmt=cats(format,''.''); '; + put ' else if formatd=0 then fmt=cats(format,formatl,''.''); '; + put ' else fmt=cats(format,formatl,''.'',formatd); '; + put ' /* needs to be wide, for datetimes etc */ '; + put ' newlen=max(length,formatl,24); '; + put ' end; '; + put ' /* 32 char unique name */ '; + put ' newname=''sasjs''!!substr(cats(put(md5(name),$hex32.)),1,27); '; + put ' '; + put ' call symputx(cats(''name'',_n_),name,''l''); '; + put ' call symputx(cats(''newname'',_n_),newname,''l''); '; + put ' call symputx(cats(''len'',_n_),newlen,''l''); '; + put ' call symputx(cats(''length'',_n_),length,''l''); '; + put ' call symputx(cats(''fmt'',_n_),fmt,''l''); '; + put ' call symputx(cats(''type'',_n_),type,''l''); '; + put ' call symputx(cats(''typelong'',_n_),typelong,''l''); '; + put ' call symputx(cats(''label'',_n_),coalescec(label,name),''l''); '; + put ' run; '; + put ' '; + put ' %let tempds=%substr(_%sysfunc(compress(%sysfunc(uuidgen()),-)),1,32); '; + put ' '; + put ' %if &engine=PROCJSON %then %do; '; + put ' %if &missing=STRING %then %do; '; + put ' %put &sysmacroname: Special Missings not supported in proc json.; '; + put ' %put &sysmacroname: Switching to DATASTEP engine; '; + put ' %goto datastep; '; + put ' %end; '; + put ' data &tempds;set &ds; '; + put ' %if &fmt=N %then format _numeric_ best32.;; '; + put ' /* PRETTY is necessary to avoid line truncation in large files */ '; + put ' proc json out=&jref pretty '; + put ' %if &action=ARR %then nokeys ; '; + put ' ;export &tempds / nosastags fmtnumeric; '; + put ' run; '; + put ' %end; '; + put ' %else %if &engine=DATASTEP %then %do; '; + put ' %datastep: '; + put ' %if %sysfunc(exist(&ds)) ne 1 & %sysfunc(exist(&ds,VIEW)) ne 1 '; + put ' %then %do; '; + put ' %put &sysmacroname: &ds NOT FOUND!!!; '; + put ' %return; '; + put ' %end; '; + put ' '; + put ' %if &fmt=Y %then %do; '; + put ' data _data_; '; + put ' /* rename on entry */ '; + put ' set &ds(rename=( '; + put ' %do i=1 %to &numcols; '; + put ' &&name&i=&&newname&i '; + put ' %end; '; + put ' )); '; + put ' %do i=1 %to &numcols; '; + put ' length &&name&i $&&len&i; '; + put ' %if &&typelong&i=num %then %do; '; + put ' &&name&i=left(put(&&newname&i,&&fmt&i)); '; + put ' %end; '; + put ' %else %do; '; + put ' &&name&i=put(&&newname&i,&&fmt&i); '; + put ' %end; '; + put ' drop &&newname&i; '; + put ' %end; '; + put ' if _error_ then call symputx(''syscc'',1012); '; + put ' run; '; + put ' %let fmtds=&syslast; '; + put ' %end; '; + put ' '; + put ' proc format; /* credit yabwon for special null removal */ '; + put ' value bart (default=40) '; + put ' %if &missing=NULL %then %do; '; + put ' ._ - .z = null '; + put ' %end; '; + put ' %else %do; '; + put ' ._ = [quote()] '; + put ' . = null '; + put ' .a - .z = [quote()] '; + put ' %end; '; + put ' other = [best.]; '; + put ' '; + put ' data &tempds; '; + put ' attrib _all_ label=''''; '; + put ' %do i=1 %to &numcols; '; + put ' %if &&typelong&i=char or &fmt=Y %then %do; '; + put ' length &&name&i $32767; '; + put ' format &&name&i $32767.; '; + put ' %end; '; + put ' %end; '; + put ' %if &fmt=Y %then %do; '; + put ' set &fmtds; '; + put ' %end; '; + put ' %else %do; '; + put ' set &ds; '; + put ' %end; '; + put ' format _numeric_ bart.; '; + put ' %do i=1 %to &numcols; '; + put ' %if &&typelong&i=char or &fmt=Y %then %do; '; + put ' if findc(&&name&i,''"\''!!''0A0D09000E0F01021011''x) then do; '; + put ' &&name&i=''"''!!trim( '; + put ' prxchange(''s/"/\\"/'',-1, /* double quote */ '; + put ' prxchange(''s/\x0A/\n/'',-1, /* new line */ '; + put ' prxchange(''s/\x0D/\r/'',-1, /* carriage return */ '; + put ' prxchange(''s/\x09/\\t/'',-1, /* tab */ '; + put ' prxchange(''s/\x00/\\u0000/'',-1, /* NUL */ '; + put ' prxchange(''s/\x0E/\\u000E/'',-1, /* SS */ '; + put ' prxchange(''s/\x0F/\\u000F/'',-1, /* SF */ '; + put ' prxchange(''s/\x01/\\u0001/'',-1, /* SOH */ '; + put ' prxchange(''s/\x02/\\u0002/'',-1, /* STX */ '; + put ' prxchange(''s/\x10/\\u0010/'',-1, /* DLE */ '; + put ' prxchange(''s/\x11/\\u0011/'',-1, /* DC1 */ '; + put ' prxchange(''s/\\/\\\\/'',-1,&&name&i) '; + put ' ))))))))))))!!''"''; '; + put ' end; '; + put ' else &&name&i=quote(cats(&&name&i)); '; + put ' %end; '; + put ' %end; '; + put ' run; '; + put ' '; + put ' /* write to temp loc to avoid _webout truncation '; + put ' - https://support.sas.com/kb/49/325.html */ '; + put ' filename _sjs temp lrecl=131068 encoding=''utf-8''; '; + put ' data _null_; file _sjs lrecl=131068 encoding=''utf-8'' mod ; '; + put ' if _n_=1 then put "["; '; + put ' set &tempds; '; + put ' if _n_>1 then put "," @; put '; + put ' %if &action=ARR %then "[" ; %else "{" ; '; + put ' %do i=1 %to &numcols; '; + put ' %if &i>1 %then "," ; '; + put ' %if &action=OBJ %then """&&name&i"":" ; '; + put ' &&name&i '; + put ' %end; '; + put ' %if &action=ARR %then "]" ; %else "}" ; ; '; + put ' /* now write the long strings to _webout 1 byte at a time */ '; + put ' data _null_; '; + put ' length filein 8 fileid 8; '; + put ' filein=fopen("_sjs",''I'',1,''B''); '; + put ' fileid=fopen("&jref",''A'',1,''B''); '; + put ' rec=''20''x; '; + put ' do while(fread(filein)=0); '; + put ' rc=fget(filein,rec,1); '; + put ' rc=fput(fileid, rec); '; + put ' rc=fwrite(fileid); '; + put ' end; '; + put ' /* close out the table */ '; + put ' rc=fput(fileid, "]"); '; + put ' rc=fwrite(fileid); '; + put ' rc=fclose(filein); '; + put ' rc=fclose(fileid); '; + put ' run; '; + put ' filename _sjs clear; '; + put ' %end; '; + put ' '; + put ' proc sql; '; + put ' drop table &colinfo, &tempds; '; + put ' '; + put ' %if &showmeta=YES %then %do; '; + put ' data _null_; file &jref encoding=''utf-8'' mod; '; + put ' put ", ""$%lowcase(%sysfunc(coalescec(&dslabel,&ds)))"":{""vars"":{"; '; + put ' do i=1 to &numcols; '; + put ' name=quote(trim(symget(cats(''name'',i)))); '; + put ' format=quote(trim(symget(cats(''fmt'',i)))); '; + put ' label=quote(trim(symget(cats(''label'',i)))); '; + put ' length=quote(trim(symget(cats(''length'',i)))); '; + put ' type=quote(trim(symget(cats(''typelong'',i)))); '; + put ' if i>1 then put "," @@; '; + put ' put name '':{"format":'' format '',"label":'' label '; + put ' '',"length":'' length '',"type":'' type ''}''; '; + put ' end; '; + put ' put ''}}''; '; + put ' run; '; + put ' %end; '; + put '%end; '; + put ' '; + put '%else %if &action=CLOSE %then %do; '; + put ' data _null_; file &jref encoding=''utf-8'' mod ; '; + put ' put "}"; '; + put ' run; '; + put '%end; '; + put '%mend mp_jsonout; '; + put ' '; + put '%macro mf_getuser(type=META '; + put ')/*/STORE SOURCE*/; '; + put ' %local user metavar; '; + put ' %if &type=OS %then %let metavar=_secureusername; '; + put ' %else %let metavar=_metaperson; '; + put ' '; + put ' %if %symexist(SYS_COMPUTE_SESSION_OWNER) %then %let user=&SYS_COMPUTE_SESSION_OWNER; '; + put ' %else %if %symexist(&metavar) %then %do; '; + put ' %if %length(&&&metavar)=0 %then %let user=&sysuserid; '; + put ' /* sometimes SAS will add @domain extension - remove for consistency */ '; + put ' /* but be sure to quote in case of usernames with commas */ '; + put ' %else %let user=%unquote(%scan(%quote(&&&metavar),1,@)); '; + put ' %end; '; + put ' %else %let user=&sysuserid; '; + put ' '; + put ' %quote(&user) '; + put ' '; + put '%mend mf_getuser; '; + put ' '; + put '%macro ms_webout(action,ds,dslabel=,fref=_webout,fmt=Y,missing=NULL '; + put ' ,showmeta=NO '; + put '); '; + put '%global _webin_file_count _webin_fileref1 _webin_name1 _program _debug '; + put ' sasjs_tables; '; + put ' '; + put '%local i tempds; '; + put '%let action=%upcase(&action); '; + put ' '; + put '%if &action=FETCH %then %do; '; + put ' %if %str(&_debug) ge 131 %then %do; '; + put ' options mprint notes mprintnest; '; + put ' %end; '; + put ' %let _webin_file_count=%eval(&_webin_file_count+0); '; + put ' /* now read in the data */ '; + put ' %do i=1 %to &_webin_file_count; '; + put ' %if &_webin_file_count=1 %then %do; '; + put ' %let _webin_fileref1=&_webin_fileref; '; + put ' %let _webin_name1=&_webin_name; '; + put ' %end; '; + put ' data _null_; '; + put ' infile &&_webin_fileref&i termstr=crlf; '; + put ' input; '; + put ' call symputx(''input_statement'',_infile_); '; + put ' putlog "&&_webin_name&i input statement: " _infile_; '; + put ' stop; '; + put ' data &&_webin_name&i; '; + put ' infile &&_webin_fileref&i firstobs=2 dsd termstr=crlf encoding=''utf-8''; '; + put ' input &input_statement; '; + put ' %if %str(&_debug) ge 131 %then %do; '; + put ' if _n_<20 then putlog _infile_; '; + put ' %end; '; + put ' run; '; + put ' %let sasjs_tables=&sasjs_tables &&_webin_name&i; '; + put ' %end; '; + put '%end; '; + put ' '; + put '%else %if &action=OPEN %then %do; '; + put ' /* fix encoding */ '; + put ' OPTIONS NOBOMFILE; '; + put ' '; + put ' /* set the header */ '; + put ' %mfs_httpheader(Content-type,application/json) '; + put ' '; + put ' /* setup json */ '; + put ' data _null_;file &fref encoding=''utf-8'' termstr=lf; '; + put ' put ''{"SYSDATE" : "'' "&SYSDATE" ''"''; '; + put ' put '',"SYSTIME" : "'' "&SYSTIME" ''"''; '; + put ' run; '; + put ' '; + put '%end; '; + put ' '; + put '%else %if &action=ARR or &action=OBJ %then %do; '; + put ' %mp_jsonout(&action,&ds,dslabel=&dslabel,fmt=&fmt,jref=&fref '; + put ' ,engine=DATASTEP,missing=&missing,showmeta=&showmeta '; + put ' ) '; + put '%end; '; + put '%else %if &action=CLOSE %then %do; '; + put ' %if %str(&_debug) ge 131 %then %do; '; + put ' /* if debug mode, send back first 10 records of each work table also */ '; + put ' options obs=10; '; + put ' data;run;%let tempds=%scan(&syslast,2,.); '; + put ' ods output Members=&tempds; '; + put ' proc datasets library=WORK memtype=data; '; + put ' %local wtcnt;%let wtcnt=0; '; + put ' data _null_; '; + put ' set &tempds; '; + put ' if not (upcase(name) =:"DATA"); /* ignore temp datasets */ '; + put ' if not (upcase(name)=:"_DATA_"); '; + put ' i+1; '; + put ' call symputx(cats(''wt'',i),name,''l''); '; + put ' call symputx(''wtcnt'',i,''l''); '; + put ' data _null_; file &fref mod encoding=''utf-8'' termstr=lf; '; + put ' put ",""WORK"":{"; '; + put ' %do i=1 %to &wtcnt; '; + put ' %let wt=&&wt&i; '; + put ' data _null_; file &fref mod encoding=''utf-8'' termstr=lf; '; + put ' dsid=open("WORK.&wt",''is''); '; + put ' nlobs=attrn(dsid,''NLOBS''); '; + put ' nvars=attrn(dsid,''NVARS''); '; + put ' rc=close(dsid); '; + put ' if &i>1 then put '',''@; '; + put ' put " ""&wt"" : {"; '; + put ' put ''"nlobs":'' nlobs; '; + put ' put '',"nvars":'' nvars; '; + put ' %mp_jsonout(OBJ,&wt,jref=&fref,dslabel=first10rows,showmeta=YES) '; + put ' data _null_; file &fref mod encoding=''utf-8'' termstr=lf; '; + put ' put "}"; '; + put ' %end; '; + put ' data _null_; file &fref mod encoding=''utf-8'' termstr=lf termstr=lf; '; + put ' put "}"; '; + put ' run; '; + put ' %end; '; + put ' /* close off json */ '; + put ' data _null_;file &fref mod encoding=''utf-8'' termstr=lf; '; + put ' _PROGRAM=quote(trim(resolve(symget(''_PROGRAM'')))); '; + put ' put ",""SYSUSERID"" : ""&sysuserid"" "; '; + put ' put ",""MF_GETUSER"" : ""%mf_getuser()"" "; '; + put ' put ",""_DEBUG"" : ""&_debug"" "; '; + put ' put '',"_PROGRAM" : '' _PROGRAM ; '; + put ' put ",""SYSCC"" : ""&syscc"" "; '; + put ' syserrortext=quote(cats(symget(''SYSERRORTEXT''))); '; + put ' put '',"SYSERRORTEXT" : '' syserrortext; '; + put ' SYSHOSTINFOLONG=quote(trim(symget(''SYSHOSTINFOLONG''))); '; + put ' put '',"SYSHOSTINFOLONG" : '' SYSHOSTINFOLONG; '; + put ' put ",""SYSHOSTNAME"" : ""&syshostname"" "; '; + put ' put ",""SYSPROCESSID"" : ""&SYSPROCESSID"" "; '; + put ' put ",""SYSPROCESSMODE"" : ""&SYSPROCESSMODE"" "; '; + put ' length SYSPROCESSNAME $512; '; + put ' SYSPROCESSNAME=quote(urlencode(cats(SYSPROCESSNAME))); '; + put ' put ",""SYSPROCESSNAME"" : " SYSPROCESSNAME; '; + put ' put ",""SYSJOBID"" : ""&sysjobid"" "; '; + put ' put ",""SYSSCPL"" : ""&sysscpl"" "; '; + put ' put ",""SYSSITE"" : ""&syssite"" "; '; + put ' put ",""SYSTCPIPHOSTNAME"" : ""&SYSTCPIPHOSTNAME"" "; '; + put ' sysvlong=quote(trim(symget(''sysvlong''))); '; + put ' put '',"SYSVLONG" : '' sysvlong; '; + put ' syswarningtext=quote(cats(symget(''SYSWARNINGTEXT''))); '; + put ' put '',"SYSWARNINGTEXT" : '' syswarningtext; '; + put ' put '',"END_DTTM" : "'' "%sysfunc(datetime(),E8601DT26.6)" ''" ''; '; + put ' length autoexec $512; '; + put ' autoexec=quote(urlencode(trim(getoption(''autoexec'')))); '; + put ' put '',"AUTOEXEC" : '' autoexec; '; + put ' length memsize $32; '; + put ' memsize="%sysfunc(INPUTN(%sysfunc(getoption(memsize)), best.),sizekmg.)"; '; + put ' memsize=quote(cats(memsize)); '; + put ' put '',"MEMSIZE" : '' memsize; '; + put ' put "}" @; '; + put ' run; '; + put '%end; '; + put ' '; + put '%mend ms_webout; '; + put ' '; + put '%macro mfs_httpheader(header_name '; + put ' ,header_value '; + put ')/*/STORE SOURCE*/; '; + put '%local fref fid i; '; + put ' '; + put '%if %sysfunc(filename(fref,&sasjs_stpsrv_header_loc)) ne 0 %then %do; '; + put ' %put &=fref &=sasjs_stpsrv_header_loc; '; + put ' %put %str(ERR)OR: %sysfunc(sysmsg()); '; + put ' %return; '; + put '%end; '; + put ' '; + put '%let fid=%sysfunc(fopen(&fref,A)); '; + put ' '; + put '%if &fid=0 %then %do; '; + put ' %put %str(ERR)OR: %sysfunc(sysmsg()); '; + put ' %return; '; + put '%end; '; + put ' '; + put '%let rc=%sysfunc(fput(&fid,%str(&header_name): %str(&header_value))); '; + put '%let rc=%sysfunc(fwrite(&fid)); '; + put ' '; + put '%let rc=%sysfunc(fclose(&fid)); '; + put '%let rc=%sysfunc(filename(&fref)); '; + put ' '; + put '%mend mfs_httpheader; '; +/* WEBOUT END */ + put '%macro webout(action,ds,dslabel=,fmt=,missing=NULL,showmeta=NO);'; + put ' %ms_webout(&action,ds=&ds,dslabel=&dslabel,fmt=&fmt,missing=&missing'; + put ' ,showmeta=&showmeta'; + put ' )'; + put '%mend;'; +run; + +/* add precode and code */ +%local tmpref x fref freflist mod; +%let tmpref=%mf_getuniquefileref(); +%let freflist=&precode &code ; +%do x=1 %to %sysfunc(countw(&freflist)); + %if &x>1 %then %let mod=mod; + + %let fref=%scan(&freflist,&x); + %put &sysmacroname: adding &fref; + data _null_; + file &tmpref lrecl=3000 &mod; + infile &fref; + input; + put _infile_; + run; +%end; + +/* create the web service */ +%ms_createfile(&path/&name..sas, inref=&tmpref,mdebug=&mdebug) + +%put ;%put ;%put ;%put ;%put ;%put ; +%put &sysmacroname: STP &name successfully created in &path; +%put ;%put ;%put ; +%put Check it out here:; +%put ;%put ;%put ; +%put &_sasjs_apiserverurl.&_sasjs_apipath?_PROGRAM=&path/&name; +%put ;%put ;%put ;%put ;%put ;%put ; + +%mend ms_createwebservice; /** @file @brief Deletes a file from SASjs Drive @@ -20822,6 +21351,25 @@ data _null_; put ' run; '; put '%end; '; put '%mend mp_jsonout; '; + put ' '; + put '%macro mf_getuser(type=META '; + put ')/*/STORE SOURCE*/; '; + put ' %local user metavar; '; + put ' %if &type=OS %then %let metavar=_secureusername; '; + put ' %else %let metavar=_metaperson; '; + put ' '; + put ' %if %symexist(SYS_COMPUTE_SESSION_OWNER) %then %let user=&SYS_COMPUTE_SESSION_OWNER; '; + put ' %else %if %symexist(&metavar) %then %do; '; + put ' %if %length(&&&metavar)=0 %then %let user=&sysuserid; '; + put ' /* sometimes SAS will add @domain extension - remove for consistency */ '; + put ' /* but be sure to quote in case of usernames with commas */ '; + put ' %else %let user=%unquote(%scan(%quote(&&&metavar),1,@)); '; + put ' %end; '; + put ' %else %let user=&sysuserid; '; + put ' '; + put ' %quote(&user) '; + put ' '; + put '%mend mf_getuser; '; put '%macro mv_webout(action,ds,fref=_mvwtemp,dslabel=,fmt=Y,stream=Y,missing=NULL '; put ' ,showmeta=NO '; put '); '; @@ -20996,25 +21544,6 @@ data _null_; put '%end; '; put ' '; put '%mend mv_webout; '; - put ' '; - put '%macro mf_getuser(type=META '; - put ')/*/STORE SOURCE*/; '; - put ' %local user metavar; '; - put ' %if &type=OS %then %let metavar=_secureusername; '; - put ' %else %let metavar=_metaperson; '; - put ' '; - put ' %if %symexist(SYS_COMPUTE_SESSION_OWNER) %then %let user=&SYS_COMPUTE_SESSION_OWNER; '; - put ' %else %if %symexist(&metavar) %then %do; '; - put ' %if %length(&&&metavar)=0 %then %let user=&sysuserid; '; - put ' /* sometimes SAS will add @domain extension - remove for consistency */ '; - put ' /* but be sure to quote in case of usernames with commas */ '; - put ' %else %let user=%unquote(%scan(%quote(&&&metavar),1,@)); '; - put ' %end; '; - put ' %else %let user=&sysuserid; '; - put ' '; - put ' %quote(&user) '; - put ' '; - put '%mend mf_getuser; '; /* WEBOUT END */ put '/* if calling viya service with _job param, _program will conflict */'; put '/* so it is provided by SASjs instead as __program */'; diff --git a/base/mp_createwebservice.sas b/base/mp_createwebservice.sas index a719f17..4959d0d 100644 --- a/base/mp_createwebservice.sas +++ b/base/mp_createwebservice.sas @@ -71,8 +71,15 @@ Usage: ,replace=&replace ) %end; +%else %if &platform=SASJS %then %do; + %if "&path"="HOME" %then %let path=/Users/&_sasjs_username/My Folder; + %ms_createfile(&path/&name..sas + ,inref=&code + ,prerefs=&precode + ) +%end; %else %do; - %if "&path"="HOME" %then %let path=/User Folders/&sysuserid/My Folder; + %if "&path"="HOME" %then %let path=/User Folders/&_METAPERSON/My Folder; %mm_createwebservice(path=&path ,name=&name ,code=&code diff --git a/build.py b/build.py index 7df8e39..973c6f8 100755 --- a/build.py +++ b/build.py @@ -4,8 +4,8 @@ from pathlib import Path # Prepare Lua Macros files = [f for f in Path('lua').iterdir() if f.match("*.lua")] for file in files: - basename=os.path.basename(file) - name='ml_' + os.path.splitext(basename)[0] + basename = os.path.basename(file) + name = 'ml_' + os.path.splitext(basename)[0] ml = open('lua/' + name + '.sas', "w") ml.write("/**\n") ml.write(" @file " + name + '.sas\n') @@ -20,50 +20,61 @@ for file in files: ml.write(" file \"%sysfunc(pathname(work))/" + name + ".lua\";\n") with open(file) as infile: for line in infile: - ml.write(" put '" + line.rstrip().replace("'","''") + " ';\n") + ml.write(" put '" + line.rstrip().replace("'", "''") + " ';\n") ml.write("run;\n\n") - ml.write("%inc \"%sysfunc(pathname(work))/" + name + ".lua\" /source2;\n\n") + ml.write("%inc \"%sysfunc(pathname(work))/" + + name + ".lua\" /source2;\n\n") ml.write("%mend " + name + ";\n") ml.close() # prepare web files -files=['viya/mv_createwebservice.sas','meta/mm_createwebservice.sas'] +files = ['viya/mv_createwebservice.sas', + 'meta/mm_createwebservice.sas', 'server/ms_createwebservice.sas'] for file in files: - webout0=open('base/mp_jsonout.sas','r') - if file=='viya/mv_createwebservice.sas': - webout1=open('viya/mv_webout.sas',"r") + webout0 = open('base/mp_jsonout.sas', 'r') + webout1 = open('base/mf_getuser.sas', 'r') + + if file == 'viya/mv_createwebservice.sas': + webout2 = open('viya/mv_webout.sas', "r") + weboutfiles = [webout0, webout1, webout2] + elif file == 'server/ms_createwebservice.sas': + webout2 = open('server/ms_webout.sas', "r") + webout3 = open('server/mfs_httpheader.sas', 'r') + weboutfiles = [webout0, webout1, webout2, webout3] else: - webout1=open('meta/mm_webout.sas','r') - webout2=open('base/mf_getuser.sas','r') - outfile=open(file + 'TEMP','w') - infile=open(file,'r') - delrow=0 + webout2 = open('meta/mm_webout.sas', 'r') + weboutfiles = [webout0, webout1, webout2] + outfile = open(file + 'TEMP', 'w') + infile = open(file, 'r') + delrow = 0 for line in infile: - if line=='/* WEBOUT BEGIN */\n': - delrow=1 + if line == '/* WEBOUT BEGIN */\n': + delrow = 1 outfile.write('/* WEBOUT BEGIN */\n') - weboutfiles=[webout0,webout1,webout2] for weboutfile in weboutfiles: - stripcomment=1 + stripcomment = 1 for w in weboutfile: - if w=='**/\n': stripcomment=0 - elif stripcomment==0: - outfile.write(" put '" + w.rstrip().replace("'","''") + " ';\n") - elif delrow==1 and line=='/* WEBOUT END */\n': - delrow=0 - outfile.write('/* WEBOUT END */\n') - elif delrow==0: + if w == '**/\n': + stripcomment = 0 + elif stripcomment == 0: + outfile.write( + " put '" + w.rstrip().replace("'", "''") + " ';\n") + elif delrow == 1 and line == '/* WEBOUT END */\n': + delrow = 0 + outfile.write('/* WEBOUT END */\n') + elif delrow == 0: outfile.write(line.rstrip() + "\n") webout0.close() webout1.close() + webout2.close() outfile.close() infile.close() os.remove(file) - os.rename(file + 'TEMP',file) + os.rename(file + 'TEMP', file) # Concatenate all macros into a single file -header=""" +header = """ /** @file @brief Auto-generated file @@ -84,14 +95,15 @@ options noquotelenmax; """ f = open('all.sas', "w") # r / r+ / rb / rb+ / w / wb f.write(header) -folders=['base','ddl','meta','metax','server','viya','lua','fcmp'] +folders = ['base', 'ddl', 'meta', 'metax', 'server', 'viya', 'lua', 'fcmp'] for folder in folders: - filenames = [fn for fn in Path('./' + folder).iterdir() if fn.match("*.sas")] + filenames = [fn for fn in Path( + './' + folder).iterdir() if fn.match("*.sas")] filenames.sort() with open('mc_' + folder + '.sas', 'w') as outfile: for fname in filenames: with open(fname) as infile: outfile.write(infile.read()) - with open('mc_' + folder + '.sas','r') as c: + with open('mc_' + folder + '.sas', 'r') as c: f.write(c.read()) f.close() diff --git a/meta/mm_createwebservice.sas b/meta/mm_createwebservice.sas index adf0997..049d606 100644 --- a/meta/mm_createwebservice.sas +++ b/meta/mm_createwebservice.sas @@ -314,6 +314,25 @@ data _null_; put ' run; '; put '%end; '; put '%mend mp_jsonout; '; + put ' '; + put '%macro mf_getuser(type=META '; + put ')/*/STORE SOURCE*/; '; + put ' %local user metavar; '; + put ' %if &type=OS %then %let metavar=_secureusername; '; + put ' %else %let metavar=_metaperson; '; + put ' '; + put ' %if %symexist(SYS_COMPUTE_SESSION_OWNER) %then %let user=&SYS_COMPUTE_SESSION_OWNER; '; + put ' %else %if %symexist(&metavar) %then %do; '; + put ' %if %length(&&&metavar)=0 %then %let user=&sysuserid; '; + put ' /* sometimes SAS will add @domain extension - remove for consistency */ '; + put ' /* but be sure to quote in case of usernames with commas */ '; + put ' %else %let user=%unquote(%scan(%quote(&&&metavar),1,@)); '; + put ' %end; '; + put ' %else %let user=&sysuserid; '; + put ' '; + put ' %quote(&user) '; + put ' '; + put '%mend mf_getuser; '; put '%macro mm_webout(action,ds,dslabel=,fref=_webout,fmt=Y,missing=NULL '; put ' ,showmeta=NO '; put '); '; @@ -455,25 +474,6 @@ data _null_; put '%end; '; put ' '; put '%mend mm_webout; '; - put ' '; - put '%macro mf_getuser(type=META '; - put ')/*/STORE SOURCE*/; '; - put ' %local user metavar; '; - put ' %if &type=OS %then %let metavar=_secureusername; '; - put ' %else %let metavar=_metaperson; '; - put ' '; - put ' %if %symexist(SYS_COMPUTE_SESSION_OWNER) %then %let user=&SYS_COMPUTE_SESSION_OWNER; '; - put ' %else %if %symexist(&metavar) %then %do; '; - put ' %if %length(&&&metavar)=0 %then %let user=&sysuserid; '; - put ' /* sometimes SAS will add @domain extension - remove for consistency */ '; - put ' /* but be sure to quote in case of usernames with commas */ '; - put ' %else %let user=%unquote(%scan(%quote(&&&metavar),1,@)); '; - put ' %end; '; - put ' %else %let user=&sysuserid; '; - put ' '; - put ' %quote(&user) '; - put ' '; - put '%mend mf_getuser; '; /* WEBOUT END */ put '%macro webout(action,ds,dslabel=,fmt=,missing=NULL,showmeta=NO);'; put ' %mm_webout(&action,ds=&ds,dslabel=&dslabel,fmt=&fmt,missing=&missing'; diff --git a/sasjs/sasjsconfig.json b/sasjs/sasjsconfig.json index aeb6988..9b74a44 100644 --- a/sasjs/sasjsconfig.json +++ b/sasjs/sasjsconfig.json @@ -65,7 +65,7 @@ }, { "name": "server", - "serverUrl": "https://sas.analytium.co.uk:5006", + "serverUrl": "https://sas.analytium.co.uk:5007", "serverType": "SASJS", "httpsAgentOptions": { "allowInsecureRequests": false @@ -86,6 +86,7 @@ "server", "viya", "tests/sas9only", + "tests/serveronly", "tests/viyaonly" ] }, diff --git a/server/ms_createwebservice.sas b/server/ms_createwebservice.sas new file mode 100644 index 0000000..ead0f42 --- /dev/null +++ b/server/ms_createwebservice.sas @@ -0,0 +1,522 @@ +/** + @file ms_createwebservice.sas + @brief Create a Web-Ready Stored Program + @details This macro creates a Stored Program along with the necessary precode + to enable the %webout() macro + + Usage: + + %* compile macros ; + filename mc url "https://raw.githubusercontent.com/sasjs/core/main/all.sas"; + %inc mc; + + %* parmcards lets us write to a text file from open code ; + filename ft15f001 temp; + parmcards4; + %webout(FETCH) + %* do some sas, any inputs are now already WORK tables; + data example1 example2; + set sashelp.class; + run; + %* send data back; + %webout(OPEN) + %webout(ARR,example1) * Array format, fast, suitable for large tables ; + %webout(OBJ,example2) * Object format, easier to work with ; + %webout(CLOSE) + ;;;; + %ms_createwebservice(path=/Public/app/common,name=appInit,code=ft15f001) + + + + For more examples of using these web services with the SASjs Adapter, see: + https://github.com/sasjs/adapter#readme + + @param [in] path= The full SASjs Drive path in which to create the service + @param [in] name= Stored Program name + @param [in] desc= The description of the service (not implemented yet) + @param [in] precode= Space separated list of filerefs, pointing to the code + that needs to be attached to the beginning of the service (optional) + @param [in] code= (ft15f001) Space seperated fileref(s) of the actual code to + be added + @param [in] mDebug= (0) set to 1 to show debug messages in the log + +

SAS Macros

+ @li ms_createfile.sas + @li mf_getuser.sas + @li mf_getuniquename.sas + + @version 9.2 + @author Allan Bowe + +**/ + +%macro ms_createwebservice(path= + ,name=initService + ,precode= + ,code=ft15f001 + ,desc=Not currently used + ,mDebug=0 +)/*/STORE SOURCE*/; + +%if &syscc ge 4 %then %do; + %put &=syscc - &sysmacroname will not execute in this state; + %return; +%end; + +%local mD; +%if &mDebug=1 %then %let mD=; +%else %let mD=%str(*); +%&mD.put Executing ms_createwebservice.sas; +%&mD.put _local_; + +* remove any trailing slash ; +%if "%substr(&path,%length(&path),1)" = "/" %then + %let path=%substr(&path,1,%length(&path)-1); + +/** + * Add webout macro + * These put statements are auto generated - to change the macro, change the + * source (ms_webout) and run `build.py` + */ +filename sasjs temp; +data _null_; + file sasjs lrecl=3000 ; + put "/* Created on %sysfunc(datetime(),datetime19.) by %mf_getuser() */"; +/* WEBOUT BEGIN */ + put ' '; + put '%macro mp_jsonout(action,ds,jref=_webout,dslabel=,fmt=Y '; + put ' ,engine=DATASTEP '; + put ' ,missing=NULL '; + put ' ,showmeta=NO '; + put ')/*/STORE SOURCE*/; '; + put '%local tempds colinfo fmtds i numcols; '; + put '%let numcols=0; '; + put ' '; + put '%if &action=OPEN %then %do; '; + put ' options nobomfile; '; + put ' data _null_;file &jref encoding=''utf-8'' ; '; + put ' put ''{"PROCESSED_DTTM" : "'' "%sysfunc(datetime(),E8601DT26.6)" ''"''; '; + put ' run; '; + put '%end; '; + put '%else %if (&action=ARR or &action=OBJ) %then %do; '; + put ' options validvarname=upcase; '; + put ' data _null_; file &jref encoding=''utf-8'' mod; '; + put ' put ", ""%lowcase(%sysfunc(coalescec(&dslabel,&ds)))"":"; '; + put ' '; + put ' /* grab col defs */ '; + put ' proc contents noprint data=&ds '; + put ' out=_data_(keep=name type length format formatl formatd varnum label); '; + put ' run; '; + put ' %let colinfo=%scan(&syslast,2,.); '; + put ' proc sort data=&colinfo; '; + put ' by varnum; '; + put ' run; '; + put ' /* move meta to mac vars */ '; + put ' data _null_; '; + put ' if _n_=1 then call symputx(''numcols'',nobs,''l''); '; + put ' set &colinfo end=last nobs=nobs; '; + put ' name=upcase(name); '; + put ' /* fix formats */ '; + put ' if type=2 or type=6 then do; '; + put ' typelong=''char''; '; + put ' length fmt $49.; '; + put ' if format='''' then fmt=cats(''$'',length,''.''); '; + put ' else if formatl=0 then fmt=cats(format,''.''); '; + put ' else fmt=cats(format,formatl,''.''); '; + put ' newlen=max(formatl,length); '; + put ' end; '; + put ' else do; '; + put ' typelong=''num''; '; + put ' if format='''' then fmt=''best.''; '; + put ' else if formatl=0 then fmt=cats(format,''.''); '; + put ' else if formatd=0 then fmt=cats(format,formatl,''.''); '; + put ' else fmt=cats(format,formatl,''.'',formatd); '; + put ' /* needs to be wide, for datetimes etc */ '; + put ' newlen=max(length,formatl,24); '; + put ' end; '; + put ' /* 32 char unique name */ '; + put ' newname=''sasjs''!!substr(cats(put(md5(name),$hex32.)),1,27); '; + put ' '; + put ' call symputx(cats(''name'',_n_),name,''l''); '; + put ' call symputx(cats(''newname'',_n_),newname,''l''); '; + put ' call symputx(cats(''len'',_n_),newlen,''l''); '; + put ' call symputx(cats(''length'',_n_),length,''l''); '; + put ' call symputx(cats(''fmt'',_n_),fmt,''l''); '; + put ' call symputx(cats(''type'',_n_),type,''l''); '; + put ' call symputx(cats(''typelong'',_n_),typelong,''l''); '; + put ' call symputx(cats(''label'',_n_),coalescec(label,name),''l''); '; + put ' run; '; + put ' '; + put ' %let tempds=%substr(_%sysfunc(compress(%sysfunc(uuidgen()),-)),1,32); '; + put ' '; + put ' %if &engine=PROCJSON %then %do; '; + put ' %if &missing=STRING %then %do; '; + put ' %put &sysmacroname: Special Missings not supported in proc json.; '; + put ' %put &sysmacroname: Switching to DATASTEP engine; '; + put ' %goto datastep; '; + put ' %end; '; + put ' data &tempds;set &ds; '; + put ' %if &fmt=N %then format _numeric_ best32.;; '; + put ' /* PRETTY is necessary to avoid line truncation in large files */ '; + put ' proc json out=&jref pretty '; + put ' %if &action=ARR %then nokeys ; '; + put ' ;export &tempds / nosastags fmtnumeric; '; + put ' run; '; + put ' %end; '; + put ' %else %if &engine=DATASTEP %then %do; '; + put ' %datastep: '; + put ' %if %sysfunc(exist(&ds)) ne 1 & %sysfunc(exist(&ds,VIEW)) ne 1 '; + put ' %then %do; '; + put ' %put &sysmacroname: &ds NOT FOUND!!!; '; + put ' %return; '; + put ' %end; '; + put ' '; + put ' %if &fmt=Y %then %do; '; + put ' data _data_; '; + put ' /* rename on entry */ '; + put ' set &ds(rename=( '; + put ' %do i=1 %to &numcols; '; + put ' &&name&i=&&newname&i '; + put ' %end; '; + put ' )); '; + put ' %do i=1 %to &numcols; '; + put ' length &&name&i $&&len&i; '; + put ' %if &&typelong&i=num %then %do; '; + put ' &&name&i=left(put(&&newname&i,&&fmt&i)); '; + put ' %end; '; + put ' %else %do; '; + put ' &&name&i=put(&&newname&i,&&fmt&i); '; + put ' %end; '; + put ' drop &&newname&i; '; + put ' %end; '; + put ' if _error_ then call symputx(''syscc'',1012); '; + put ' run; '; + put ' %let fmtds=&syslast; '; + put ' %end; '; + put ' '; + put ' proc format; /* credit yabwon for special null removal */ '; + put ' value bart (default=40) '; + put ' %if &missing=NULL %then %do; '; + put ' ._ - .z = null '; + put ' %end; '; + put ' %else %do; '; + put ' ._ = [quote()] '; + put ' . = null '; + put ' .a - .z = [quote()] '; + put ' %end; '; + put ' other = [best.]; '; + put ' '; + put ' data &tempds; '; + put ' attrib _all_ label=''''; '; + put ' %do i=1 %to &numcols; '; + put ' %if &&typelong&i=char or &fmt=Y %then %do; '; + put ' length &&name&i $32767; '; + put ' format &&name&i $32767.; '; + put ' %end; '; + put ' %end; '; + put ' %if &fmt=Y %then %do; '; + put ' set &fmtds; '; + put ' %end; '; + put ' %else %do; '; + put ' set &ds; '; + put ' %end; '; + put ' format _numeric_ bart.; '; + put ' %do i=1 %to &numcols; '; + put ' %if &&typelong&i=char or &fmt=Y %then %do; '; + put ' if findc(&&name&i,''"\''!!''0A0D09000E0F01021011''x) then do; '; + put ' &&name&i=''"''!!trim( '; + put ' prxchange(''s/"/\\"/'',-1, /* double quote */ '; + put ' prxchange(''s/\x0A/\n/'',-1, /* new line */ '; + put ' prxchange(''s/\x0D/\r/'',-1, /* carriage return */ '; + put ' prxchange(''s/\x09/\\t/'',-1, /* tab */ '; + put ' prxchange(''s/\x00/\\u0000/'',-1, /* NUL */ '; + put ' prxchange(''s/\x0E/\\u000E/'',-1, /* SS */ '; + put ' prxchange(''s/\x0F/\\u000F/'',-1, /* SF */ '; + put ' prxchange(''s/\x01/\\u0001/'',-1, /* SOH */ '; + put ' prxchange(''s/\x02/\\u0002/'',-1, /* STX */ '; + put ' prxchange(''s/\x10/\\u0010/'',-1, /* DLE */ '; + put ' prxchange(''s/\x11/\\u0011/'',-1, /* DC1 */ '; + put ' prxchange(''s/\\/\\\\/'',-1,&&name&i) '; + put ' ))))))))))))!!''"''; '; + put ' end; '; + put ' else &&name&i=quote(cats(&&name&i)); '; + put ' %end; '; + put ' %end; '; + put ' run; '; + put ' '; + put ' /* write to temp loc to avoid _webout truncation '; + put ' - https://support.sas.com/kb/49/325.html */ '; + put ' filename _sjs temp lrecl=131068 encoding=''utf-8''; '; + put ' data _null_; file _sjs lrecl=131068 encoding=''utf-8'' mod ; '; + put ' if _n_=1 then put "["; '; + put ' set &tempds; '; + put ' if _n_>1 then put "," @; put '; + put ' %if &action=ARR %then "[" ; %else "{" ; '; + put ' %do i=1 %to &numcols; '; + put ' %if &i>1 %then "," ; '; + put ' %if &action=OBJ %then """&&name&i"":" ; '; + put ' &&name&i '; + put ' %end; '; + put ' %if &action=ARR %then "]" ; %else "}" ; ; '; + put ' /* now write the long strings to _webout 1 byte at a time */ '; + put ' data _null_; '; + put ' length filein 8 fileid 8; '; + put ' filein=fopen("_sjs",''I'',1,''B''); '; + put ' fileid=fopen("&jref",''A'',1,''B''); '; + put ' rec=''20''x; '; + put ' do while(fread(filein)=0); '; + put ' rc=fget(filein,rec,1); '; + put ' rc=fput(fileid, rec); '; + put ' rc=fwrite(fileid); '; + put ' end; '; + put ' /* close out the table */ '; + put ' rc=fput(fileid, "]"); '; + put ' rc=fwrite(fileid); '; + put ' rc=fclose(filein); '; + put ' rc=fclose(fileid); '; + put ' run; '; + put ' filename _sjs clear; '; + put ' %end; '; + put ' '; + put ' proc sql; '; + put ' drop table &colinfo, &tempds; '; + put ' '; + put ' %if &showmeta=YES %then %do; '; + put ' data _null_; file &jref encoding=''utf-8'' mod; '; + put ' put ", ""$%lowcase(%sysfunc(coalescec(&dslabel,&ds)))"":{""vars"":{"; '; + put ' do i=1 to &numcols; '; + put ' name=quote(trim(symget(cats(''name'',i)))); '; + put ' format=quote(trim(symget(cats(''fmt'',i)))); '; + put ' label=quote(trim(symget(cats(''label'',i)))); '; + put ' length=quote(trim(symget(cats(''length'',i)))); '; + put ' type=quote(trim(symget(cats(''typelong'',i)))); '; + put ' if i>1 then put "," @@; '; + put ' put name '':{"format":'' format '',"label":'' label '; + put ' '',"length":'' length '',"type":'' type ''}''; '; + put ' end; '; + put ' put ''}}''; '; + put ' run; '; + put ' %end; '; + put '%end; '; + put ' '; + put '%else %if &action=CLOSE %then %do; '; + put ' data _null_; file &jref encoding=''utf-8'' mod ; '; + put ' put "}"; '; + put ' run; '; + put '%end; '; + put '%mend mp_jsonout; '; + put ' '; + put '%macro mf_getuser(type=META '; + put ')/*/STORE SOURCE*/; '; + put ' %local user metavar; '; + put ' %if &type=OS %then %let metavar=_secureusername; '; + put ' %else %let metavar=_metaperson; '; + put ' '; + put ' %if %symexist(SYS_COMPUTE_SESSION_OWNER) %then %let user=&SYS_COMPUTE_SESSION_OWNER; '; + put ' %else %if %symexist(&metavar) %then %do; '; + put ' %if %length(&&&metavar)=0 %then %let user=&sysuserid; '; + put ' /* sometimes SAS will add @domain extension - remove for consistency */ '; + put ' /* but be sure to quote in case of usernames with commas */ '; + put ' %else %let user=%unquote(%scan(%quote(&&&metavar),1,@)); '; + put ' %end; '; + put ' %else %let user=&sysuserid; '; + put ' '; + put ' %quote(&user) '; + put ' '; + put '%mend mf_getuser; '; + put ' '; + put '%macro ms_webout(action,ds,dslabel=,fref=_webout,fmt=Y,missing=NULL '; + put ' ,showmeta=NO '; + put '); '; + put '%global _webin_file_count _webin_fileref1 _webin_name1 _program _debug '; + put ' sasjs_tables; '; + put ' '; + put '%local i tempds; '; + put '%let action=%upcase(&action); '; + put ' '; + put '%if &action=FETCH %then %do; '; + put ' %if %str(&_debug) ge 131 %then %do; '; + put ' options mprint notes mprintnest; '; + put ' %end; '; + put ' %let _webin_file_count=%eval(&_webin_file_count+0); '; + put ' /* now read in the data */ '; + put ' %do i=1 %to &_webin_file_count; '; + put ' %if &_webin_file_count=1 %then %do; '; + put ' %let _webin_fileref1=&_webin_fileref; '; + put ' %let _webin_name1=&_webin_name; '; + put ' %end; '; + put ' data _null_; '; + put ' infile &&_webin_fileref&i termstr=crlf; '; + put ' input; '; + put ' call symputx(''input_statement'',_infile_); '; + put ' putlog "&&_webin_name&i input statement: " _infile_; '; + put ' stop; '; + put ' data &&_webin_name&i; '; + put ' infile &&_webin_fileref&i firstobs=2 dsd termstr=crlf encoding=''utf-8''; '; + put ' input &input_statement; '; + put ' %if %str(&_debug) ge 131 %then %do; '; + put ' if _n_<20 then putlog _infile_; '; + put ' %end; '; + put ' run; '; + put ' %let sasjs_tables=&sasjs_tables &&_webin_name&i; '; + put ' %end; '; + put '%end; '; + put ' '; + put '%else %if &action=OPEN %then %do; '; + put ' /* fix encoding */ '; + put ' OPTIONS NOBOMFILE; '; + put ' '; + put ' /* set the header */ '; + put ' %mfs_httpheader(Content-type,application/json) '; + put ' '; + put ' /* setup json */ '; + put ' data _null_;file &fref encoding=''utf-8'' termstr=lf; '; + put ' put ''{"SYSDATE" : "'' "&SYSDATE" ''"''; '; + put ' put '',"SYSTIME" : "'' "&SYSTIME" ''"''; '; + put ' run; '; + put ' '; + put '%end; '; + put ' '; + put '%else %if &action=ARR or &action=OBJ %then %do; '; + put ' %mp_jsonout(&action,&ds,dslabel=&dslabel,fmt=&fmt,jref=&fref '; + put ' ,engine=DATASTEP,missing=&missing,showmeta=&showmeta '; + put ' ) '; + put '%end; '; + put '%else %if &action=CLOSE %then %do; '; + put ' %if %str(&_debug) ge 131 %then %do; '; + put ' /* if debug mode, send back first 10 records of each work table also */ '; + put ' options obs=10; '; + put ' data;run;%let tempds=%scan(&syslast,2,.); '; + put ' ods output Members=&tempds; '; + put ' proc datasets library=WORK memtype=data; '; + put ' %local wtcnt;%let wtcnt=0; '; + put ' data _null_; '; + put ' set &tempds; '; + put ' if not (upcase(name) =:"DATA"); /* ignore temp datasets */ '; + put ' if not (upcase(name)=:"_DATA_"); '; + put ' i+1; '; + put ' call symputx(cats(''wt'',i),name,''l''); '; + put ' call symputx(''wtcnt'',i,''l''); '; + put ' data _null_; file &fref mod encoding=''utf-8'' termstr=lf; '; + put ' put ",""WORK"":{"; '; + put ' %do i=1 %to &wtcnt; '; + put ' %let wt=&&wt&i; '; + put ' data _null_; file &fref mod encoding=''utf-8'' termstr=lf; '; + put ' dsid=open("WORK.&wt",''is''); '; + put ' nlobs=attrn(dsid,''NLOBS''); '; + put ' nvars=attrn(dsid,''NVARS''); '; + put ' rc=close(dsid); '; + put ' if &i>1 then put '',''@; '; + put ' put " ""&wt"" : {"; '; + put ' put ''"nlobs":'' nlobs; '; + put ' put '',"nvars":'' nvars; '; + put ' %mp_jsonout(OBJ,&wt,jref=&fref,dslabel=first10rows,showmeta=YES) '; + put ' data _null_; file &fref mod encoding=''utf-8'' termstr=lf; '; + put ' put "}"; '; + put ' %end; '; + put ' data _null_; file &fref mod encoding=''utf-8'' termstr=lf termstr=lf; '; + put ' put "}"; '; + put ' run; '; + put ' %end; '; + put ' /* close off json */ '; + put ' data _null_;file &fref mod encoding=''utf-8'' termstr=lf; '; + put ' _PROGRAM=quote(trim(resolve(symget(''_PROGRAM'')))); '; + put ' put ",""SYSUSERID"" : ""&sysuserid"" "; '; + put ' put ",""MF_GETUSER"" : ""%mf_getuser()"" "; '; + put ' put ",""_DEBUG"" : ""&_debug"" "; '; + put ' put '',"_PROGRAM" : '' _PROGRAM ; '; + put ' put ",""SYSCC"" : ""&syscc"" "; '; + put ' syserrortext=quote(cats(symget(''SYSERRORTEXT''))); '; + put ' put '',"SYSERRORTEXT" : '' syserrortext; '; + put ' SYSHOSTINFOLONG=quote(trim(symget(''SYSHOSTINFOLONG''))); '; + put ' put '',"SYSHOSTINFOLONG" : '' SYSHOSTINFOLONG; '; + put ' put ",""SYSHOSTNAME"" : ""&syshostname"" "; '; + put ' put ",""SYSPROCESSID"" : ""&SYSPROCESSID"" "; '; + put ' put ",""SYSPROCESSMODE"" : ""&SYSPROCESSMODE"" "; '; + put ' length SYSPROCESSNAME $512; '; + put ' SYSPROCESSNAME=quote(urlencode(cats(SYSPROCESSNAME))); '; + put ' put ",""SYSPROCESSNAME"" : " SYSPROCESSNAME; '; + put ' put ",""SYSJOBID"" : ""&sysjobid"" "; '; + put ' put ",""SYSSCPL"" : ""&sysscpl"" "; '; + put ' put ",""SYSSITE"" : ""&syssite"" "; '; + put ' put ",""SYSTCPIPHOSTNAME"" : ""&SYSTCPIPHOSTNAME"" "; '; + put ' sysvlong=quote(trim(symget(''sysvlong''))); '; + put ' put '',"SYSVLONG" : '' sysvlong; '; + put ' syswarningtext=quote(cats(symget(''SYSWARNINGTEXT''))); '; + put ' put '',"SYSWARNINGTEXT" : '' syswarningtext; '; + put ' put '',"END_DTTM" : "'' "%sysfunc(datetime(),E8601DT26.6)" ''" ''; '; + put ' length autoexec $512; '; + put ' autoexec=quote(urlencode(trim(getoption(''autoexec'')))); '; + put ' put '',"AUTOEXEC" : '' autoexec; '; + put ' length memsize $32; '; + put ' memsize="%sysfunc(INPUTN(%sysfunc(getoption(memsize)), best.),sizekmg.)"; '; + put ' memsize=quote(cats(memsize)); '; + put ' put '',"MEMSIZE" : '' memsize; '; + put ' put "}" @; '; + put ' run; '; + put '%end; '; + put ' '; + put '%mend ms_webout; '; + put ' '; + put '%macro mfs_httpheader(header_name '; + put ' ,header_value '; + put ')/*/STORE SOURCE*/; '; + put '%local fref fid i; '; + put ' '; + put '%if %sysfunc(filename(fref,&sasjs_stpsrv_header_loc)) ne 0 %then %do; '; + put ' %put &=fref &=sasjs_stpsrv_header_loc; '; + put ' %put %str(ERR)OR: %sysfunc(sysmsg()); '; + put ' %return; '; + put '%end; '; + put ' '; + put '%let fid=%sysfunc(fopen(&fref,A)); '; + put ' '; + put '%if &fid=0 %then %do; '; + put ' %put %str(ERR)OR: %sysfunc(sysmsg()); '; + put ' %return; '; + put '%end; '; + put ' '; + put '%let rc=%sysfunc(fput(&fid,%str(&header_name): %str(&header_value))); '; + put '%let rc=%sysfunc(fwrite(&fid)); '; + put ' '; + put '%let rc=%sysfunc(fclose(&fid)); '; + put '%let rc=%sysfunc(filename(&fref)); '; + put ' '; + put '%mend mfs_httpheader; '; +/* WEBOUT END */ + put '%macro webout(action,ds,dslabel=,fmt=,missing=NULL,showmeta=NO);'; + put ' %ms_webout(&action,ds=&ds,dslabel=&dslabel,fmt=&fmt,missing=&missing'; + put ' ,showmeta=&showmeta'; + put ' )'; + put '%mend;'; +run; + +/* add precode and code */ +%local tmpref x fref freflist mod; +%let tmpref=%mf_getuniquefileref(); +%let freflist=&precode &code ; +%do x=1 %to %sysfunc(countw(&freflist)); + %if &x>1 %then %let mod=mod; + + %let fref=%scan(&freflist,&x); + %put &sysmacroname: adding &fref; + data _null_; + file &tmpref lrecl=3000 &mod; + infile &fref; + input; + put _infile_; + run; +%end; + +/* create the web service */ +%ms_createfile(&path/&name..sas, inref=&tmpref,mdebug=&mdebug) + +%put ;%put ;%put ;%put ;%put ;%put ; +%put &sysmacroname: STP &name successfully created in &path; +%put ;%put ;%put ; +%put Check it out here:; +%put ;%put ;%put ; +%put &_sasjs_apiserverurl.&_sasjs_apipath?_PROGRAM=&path/&name; +%put ;%put ;%put ;%put ;%put ;%put ; + +%mend ms_createwebservice; diff --git a/viya/mv_createwebservice.sas b/viya/mv_createwebservice.sas index 9f52d43..d17d7cf 100644 --- a/viya/mv_createwebservice.sas +++ b/viya/mv_createwebservice.sas @@ -458,6 +458,25 @@ data _null_; put ' run; '; put '%end; '; put '%mend mp_jsonout; '; + put ' '; + put '%macro mf_getuser(type=META '; + put ')/*/STORE SOURCE*/; '; + put ' %local user metavar; '; + put ' %if &type=OS %then %let metavar=_secureusername; '; + put ' %else %let metavar=_metaperson; '; + put ' '; + put ' %if %symexist(SYS_COMPUTE_SESSION_OWNER) %then %let user=&SYS_COMPUTE_SESSION_OWNER; '; + put ' %else %if %symexist(&metavar) %then %do; '; + put ' %if %length(&&&metavar)=0 %then %let user=&sysuserid; '; + put ' /* sometimes SAS will add @domain extension - remove for consistency */ '; + put ' /* but be sure to quote in case of usernames with commas */ '; + put ' %else %let user=%unquote(%scan(%quote(&&&metavar),1,@)); '; + put ' %end; '; + put ' %else %let user=&sysuserid; '; + put ' '; + put ' %quote(&user) '; + put ' '; + put '%mend mf_getuser; '; put '%macro mv_webout(action,ds,fref=_mvwtemp,dslabel=,fmt=Y,stream=Y,missing=NULL '; put ' ,showmeta=NO '; put '); '; @@ -632,25 +651,6 @@ data _null_; put '%end; '; put ' '; put '%mend mv_webout; '; - put ' '; - put '%macro mf_getuser(type=META '; - put ')/*/STORE SOURCE*/; '; - put ' %local user metavar; '; - put ' %if &type=OS %then %let metavar=_secureusername; '; - put ' %else %let metavar=_metaperson; '; - put ' '; - put ' %if %symexist(SYS_COMPUTE_SESSION_OWNER) %then %let user=&SYS_COMPUTE_SESSION_OWNER; '; - put ' %else %if %symexist(&metavar) %then %do; '; - put ' %if %length(&&&metavar)=0 %then %let user=&sysuserid; '; - put ' /* sometimes SAS will add @domain extension - remove for consistency */ '; - put ' /* but be sure to quote in case of usernames with commas */ '; - put ' %else %let user=%unquote(%scan(%quote(&&&metavar),1,@)); '; - put ' %end; '; - put ' %else %let user=&sysuserid; '; - put ' '; - put ' %quote(&user) '; - put ' '; - put '%mend mf_getuser; '; /* WEBOUT END */ put '/* if calling viya service with _job param, _program will conflict */'; put '/* so it is provided by SASjs instead as __program */';