diff --git a/README.md b/README.md index b10007f..ba2e2a4 100644 --- a/README.md +++ b/README.md @@ -47,7 +47,7 @@ Documentation: https://core.sasjs.io This library will not be used for storing data entries (such as formats or datalines). Where this becomes necessary in the future, a new repo will be created, in order to keep the NPM bundle size down (for the benefit of those looking to embed purely macros in their applications). -#### FCMP library (All Platforms) +### FCMP library (All Platforms) - Function and macro names are identical, except for special cases - Prefixes: _mcf_ @@ -217,7 +217,6 @@ If you find this library useful, please leave a [star](https://github.com/sasjs/ ![](https://starchart.cc/sasjs/core.svg) - ## Contributors ✨ [![All Contributors](https://img.shields.io/badge/all_contributors-10-orange.svg?style=flat-square)](#contributors-) diff --git a/base/mp_assertscope.sas b/base/mp_assertscope.sas index f6477d1..786361c 100644 --- a/base/mp_assertscope.sas +++ b/base/mp_assertscope.sas @@ -74,7 +74,8 @@ outds=work.test_results )/*/STORE SOURCE*/; %local ds test_result test_comments del add mod ilist; -%let ilist=%upcase(&sasjs_prefix._FUNCTIONS &ignorelist); +%let ilist=%upcase(&sasjs_prefix._FUNCTIONS SYS_PROCHTTP_STATUS_CODE + SYS_PROCHTTP_STATUS_CODE SYS_PROCHTTP_STATUS_PHRASE &ignorelist); /** * this sets up the global vars, it will also enter STRICT mode. If this @@ -89,7 +90,7 @@ create table &scopeds as select name,offset,value from dictionary.macros - where scope="&scope" and name not in (%mf_getquotedstr(&ilist)) + where scope="&scope" and upcase(name) not in (%mf_getquotedstr(&ilist)) order by name,offset; %end; %else %if &action=COMPARE %then %do; @@ -141,4 +142,4 @@ drop table &ds; %end; -%mend mp_assertscope; \ No newline at end of file +%mend mp_assertscope; diff --git a/base/mp_lockanytable.sas b/base/mp_lockanytable.sas index a47e09b..40ce710 100644 --- a/base/mp_lockanytable.sas +++ b/base/mp_lockanytable.sas @@ -1,7 +1,8 @@ /** @file @brief Mechanism for locking tables to prevent parallel modifications - @details Uses a control table to enable ANY table to be locked for updates. + @details Uses a control table to enable ANY table to be locked for updates + (not just SAS datasets). Only useful if every update uses the macro! Used heavily within [Data Controller for SAS](https://datacontroller.io). @@ -15,7 +16,7 @@ length is 200 characters. @param [out] ctl_ds= (0) The control table which controls the actual locking. Should already be assigned and available. The definition is available by - running mp_coretable.sas as follows: `%mp_coretable(LOCKTABLE)`. + running the mddl_dc_locktable.sas macro. @param [in] loops= (25) Number of times to check for a lock. @param [in] loop_secs= (1) Seconds to wait between each lock attempt diff --git a/base/mp_streamfile.sas b/base/mp_streamfile.sas index b442415..179740f 100644 --- a/base/mp_streamfile.sas +++ b/base/mp_streamfile.sas @@ -12,7 +12,7 @@ %mp_streamfile(contenttype=csv,inloc=/some/where.txt,outname=myfile.txt) - @param [in] contenttype= (TEXTS) Either TEXT, ZIP, CSV, EXCEL + @param [in] contenttype= (TEXT) Either TEXT, ZIP, CSV, EXCEL @param [in] inloc= /path/to/file.ext to be sent @param [in] inref= fileref of file to be sent (if provided, overrides `inloc`) @param [in] iftrue= (1=1) Provide a condition under which to execute. diff --git a/meta/mm_assignlib.sas b/meta/mm_assignlib.sas index b430211..68698aa 100755 --- a/meta/mm_assignlib.sas +++ b/meta/mm_assignlib.sas @@ -40,6 +40,12 @@ /* now try and assign it */ if libname("&libref",,'meta',cats('liburi="',liburi,'";')) ne 0 then do; putlog "&libref could not be assigned"; + putlog liburi=; + /** + * Fetch the system message for display in the abort modal. This is + * not always helpful though. One example, previously received: + * NOTE: Libref XX refers to the same library metadata as libref XX. + */ call symputx('msg',sysmsg(),'l'); if "&mabort"='HARD' then call symputx('mp_abort',1,'l'); end; @@ -61,7 +67,7 @@ %if &mp_abort=1 %then %do; %mp_abort(iftrue= (&mp_abort=1) - ,mac=&sysmacroname + ,mac=mm_assignlib.sas ,msg=&msg ) %return; diff --git a/sasjs/sasjsconfig.json b/sasjs/sasjsconfig.json index 39402a2..d7c348f 100644 --- a/sasjs/sasjsconfig.json +++ b/sasjs/sasjsconfig.json @@ -73,17 +73,24 @@ "name": "server", "serverUrl": "https://sas.analytium.co.uk:5000", "serverType": "SASJS", - "appLoc": "/Shared Data/temp/macrocore", + "httpsAgentOptions": { + "allowInsecureRequests": false + }, + "appLoc": "/sasjs/core", "macroFolders": [ "tests/serveronly" ], + "programFolders": [], + "binaryFolders": [], "deployConfig": { - "deployServicePack": true + "deployServicePack": true, + "deployScripts": [] } }, { "name": "docsonly", "serverType": "SAS9", + "appLoc": "dummy", "macroFolders": [ "tests/sas9only", "tests/viyaonly" diff --git a/server/ms_createfile.sas b/server/ms_createfile.sas new file mode 100644 index 0000000..f38c71d --- /dev/null +++ b/server/ms_createfile.sas @@ -0,0 +1,89 @@ +/** + @file + @brief Creates a file on SASjs Drive + @details Creates a file on SASjs Drive. To use the file as a Stored Program, + it must have a ".sas" extension. + + Example: + + filename stpcode temp; + data _null_; + file stpcode; + put '%put hello world;'; + run; + %ms_createfile(/some/stored/program.sas, inref=stpcode) + + @param [in] driveloc The full path to the file in SASjs Drive + @param [in] inref= (0) The fileref containing the file to create. + @param [in] mdebug= (0) Set to 1 to enable DEBUG messages + +

SAS Macros

+ @li mf_getuniquefileref.sas + @li mf_getuniquename.sas + @li mp_abort.sas + +**/ + +%macro ms_createfile(driveloc + ,inref=0 + ,mdebug=0 + ); + +%local fname0 fname1 boundary fname statcd msg; +%let fname0=%mf_getuniquefileref(); +%let fname1=%mf_getuniquefileref(); +%let boundary=%mf_getuniquename(); + +data _null_; + file &fname0 termstr=crlf; + infile &inref end=eof; + if _n_ = 1 then do; + put "--&boundary."; + put 'Content-Disposition: form-data; name="filePath"'; + put ; + put "&driveloc"; + put "--&boundary"; + put 'Content-Disposition: form-data; name="file"; filename="ignore.sas"'; + put "Content-Type: text/plain"; + put ; + end; + input; + put _infile_; /* add the actual file to be sent */ + if eof then do; + put ; + put "--&boundary--"; + end; +run; + +%if &mdebug=1 %then %do; + data _null_; + infile &fname0; + input; + put _infile_; + run; +%end; + +proc http method='POST' in=&fname0 out=&fname1 + url="&_sasjs_apiserverurl/SASjsApi/drive/file"; + headers "Content-Type"="multipart/form-data; boundary=&boundary"; +%if &mdebug=1 %then %do; + debug level=1; +%end; +run; + +%let statcd=0; +data _null_; + infile &fname1; + input; + putlog _infile_; + if _infile_='{"status":"success"}' then call symputx('statcd',1,'l'); + else call symputx('msg',_infile_,'l'); +run; + +%mp_abort( + iftrue=(&statcd=0) + ,mac=ms_createfile.sas + ,msg=%superq(msg) +) + +%mend ms_createfile; diff --git a/server/ms_runstp.sas b/server/ms_runstp.sas new file mode 100644 index 0000000..0f7e76f --- /dev/null +++ b/server/ms_runstp.sas @@ -0,0 +1,77 @@ +/** + @file + @brief Executes a SASjs Server Stored Program + @details Runs a Stored Program (using POST method) and extracts the webout and + log from the response JSON. + + Example: + + %ms_runstp(/some/stored/program + ,debug=131 + ,outref=weboot + ) + + @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 [out] outref= (outweb) The output fileref to contain the response JSON + (will be created using temp engine) + +

SAS Macros

+ @li mf_getuniquefileref.sas + @li mp_abort.sas + +**/ + +%macro ms_runstp(pgm + ,debug=131 + ,outref=outweb + ,mdebug=0 + ); +%local dbg fname1; +%if &mdebug=1 %then %do; + %put &sysmacroname entry vars:; + %put _local_; +%end; +%else %let dbg=*; +%let fname1=%mf_getuniquefileref(); + +%mp_abort(iftrue=("&pgm"="") + ,mac=&sysmacroname + ,msg=%str(Program not provided) +) + +data _null_; + file &fname1; + infile "&_sasjs_tokenfile"; + input; + put 'Authorization: Bearer' _infile_; +run; + +filename &outref temp; + +/* prepare request*/ +proc http method='POST' headerin=&fname1 out=&outref + url="&_sasjs_apiserverurl.&_sasjs_apipath?_program=&pgm%str(&)_debug=131"; +run; +%if (&SYS_PROCHTTP_STATUS_CODE ne 200 and &SYS_PROCHTTP_STATUS_CODE ne 201) + or &mdebug=1 %then %do; + data _null_;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) +) + + +%if &mdebug=1 %then %do; + %put &sysmacroname exit vars:; + %put _local_; +%end; +%else %do; + /* clear refs */ + filename &fname1 clear; +%end; +%mend ms_runstp; \ No newline at end of file diff --git a/server/ms_webout.sas b/server/ms_webout.sas index 9cc8026..4a45f08 100644 --- a/server/ms_webout.sas +++ b/server/ms_webout.sas @@ -88,9 +88,6 @@ /* setup json */ data _null_;file &fref encoding='utf-8' termstr=lf; - %if %str(&_debug) ge 131 %then %do; - put '>>weboutBEGIN<<'; - %end; put '{"SYSDATE" : "' "&SYSDATE" '"'; put ',"SYSTIME" : "' "&SYSTIME" '"'; run; @@ -170,9 +167,6 @@ memsize=quote(cats(memsize)); put ',"MEMSIZE" : ' memsize; put "}" @; - %if %str(&_debug) ge 131 %then %do; - put '>>weboutEND<<'; - %end; run; %end; diff --git a/tests/serveronly/mfs_httpheader.test.sas b/tests/serveronly/mfs_httpheader.test.sas index 07ab247..baf5ddc 100644 --- a/tests/serveronly/mfs_httpheader.test.sas +++ b/tests/serveronly/mfs_httpheader.test.sas @@ -5,12 +5,16 @@

SAS Macros

@li mfs_httpheader.sas @li mp_assert.sas + @li mp_assertscope.sas **/ %let sasjs_stpsrv_header_loc=%sysfunc(pathname(work))/header.txt; +%mp_assertscope(SNAPSHOT) %mfs_httpheader(Content-type,application/csv) +%mp_assertscope(COMPARE) + data _null_; infile "&sasjs_stpsrv_header_loc"; input; diff --git a/tests/serveronly/ms_createfile.test.sas b/tests/serveronly/ms_createfile.test.sas new file mode 100644 index 0000000..23519aa --- /dev/null +++ b/tests/serveronly/ms_createfile.test.sas @@ -0,0 +1,30 @@ +/** + @file + @brief Testing ms_createfile.sas macro + +

SAS Macros

+ @li ms_createfile.sas + @li mp_assert.sas + @li mp_assertscope.sas + +**/ + + +filename stpcode temp; +data _null_; + file stpcode; + put '%put hello world;'; +run; + +options mprint; +%let fname=%mf_getuniquename(); + +%mp_assertscope(SNAPSHOT) +%ms_createfile(/sasjs/tests/&fname..sas + ,inref=stpcode + ,mdebug=1 +) +%mp_assertscope(COMPARE) + + + diff --git a/tests/serveronly/ms_runstp.test.sas b/tests/serveronly/ms_runstp.test.sas new file mode 100644 index 0000000..0218482 --- /dev/null +++ b/tests/serveronly/ms_runstp.test.sas @@ -0,0 +1,44 @@ +/** + @file + @brief Testing ms_runstp.sas macro + +

SAS Macros

+ @li ms_runstp.sas + @li mp_assert.sas + @li mp_assertscope.sas + +**/ + +%mp_assertscope(SNAPSHOT) +%ms_runstp(/Public/app/frs/allan/services/common/appinit + ,debug=131 + ,outref=weboot +) +%mp_assertscope(COMPARE) + +libname webeen json (weboot); + +data _null_; + infile weboot; + input; + putlog _infile_; +run; + +data work.httpheaders; + set webeen.httpheaders; + call symputx('test1',content_type); +run; + +data work.log; + set webeen.log; + put (_all_)(=); + if _n_>10 then stop; +run; + +%mp_assert( + iftrue=("&test1"="application/json"), + desc=Checking line was created, + outds=work.test_results +) + +