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/

-
## Contributors ✨
[](#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
+)
+
+