diff --git a/README.md b/README.md
index ded8a4c..af53089 100644
--- a/README.md
+++ b/README.md
@@ -195,7 +195,7 @@ When contributing to this library, it is therefore important to ensure that all
We are currently on major release v4. Breaking changes should be marked with the [deprecated](https://www.doxygen.nl/manual/commands.html#cmddeprecated) doxygen tag. The following changes are planned when the next major (breaking) release becomes necessary:
-* (None as yet)
+* mp_testservice.sas to be renamed as mp_execute.sas (as it doesn't actually test anything)
## Star Gazing
diff --git a/all.sas b/all.sas
index 627583f..2cd6c57 100644
--- a/all.sas
+++ b/all.sas
@@ -3720,20 +3720,22 @@ Usage:
%webout(OBJ,example2) * Object format, easier to work with ;
%webout(CLOSE)
;;;;
- %mp_createwebservice(path=/Public/app/common,name=appInit,code=ft15f001,replace=YES)
+ %mp_createwebservice(path=/Public/app/common,name=appInit,replace=YES)
SAS Macros
@li mf_getplatform.sas
@li mm_createwebservice.sas
@li mv_createwebservice.sas
- @param path= The full folder path where the service will be created
- @param name= Service name. Avoid spaces.
- @param desc= The description of the service (optional)
- @param precode= Space separated list of filerefs, pointing to the code that
- needs to be attached to the beginning of the service (optional)
- @param code= Space seperated fileref(s) of the actual code to be added
- @param replace= select YES to replace any existing service in that location
+ @param [in,out] path= The full folder path where the service will be created
+ @param [in,out] name= Service name. Avoid spaces.
+ @param [in] desc= The description of the service (optional)
+ @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] replace= (YES) Select YES to replace any existing service in that
+ location
@version 9.2
@@ -4020,6 +4022,7 @@ run;
data _null_;
set work.&tempds end=last;
length fref $8;
+ fref='';
rc=filename(fref,filepath);
rc=fdelete(fref);
if rc then do;
@@ -4119,7 +4122,8 @@ data &out_ds(compress=no
keep=file_or_folder filepath filename ext msg directory level
);
length directory filepath $500 fref fref2 $8 file_or_folder $6 filename $80
- ext $20 msg $200;
+ ext $20 msg $200 foption $16;
+ if _n_=1 then call missing(of _all_);
retain level &level;
%if &fref=0 %then %do;
rc = filename(fref, "&path");
@@ -4130,7 +4134,13 @@ data &out_ds(compress=no
%end;
if rc = 0 then do;
did = dopen(fref);
- directory=dinfo(did,'Directory');
+ /* attribute is OS-dependent - could be "Directory" or "Directory Name" */
+ numopts=doptnum(did);
+ do i=1 to numopts;
+ foption=doptname(did,i);
+ if foption=:'Directory' then i=numopts;
+ end;
+ directory=dinfo(did,foption);
if did=0 then do;
putlog "NOTE: This directory is empty - " directory;
msg=sysmsg();
@@ -4653,24 +4663,57 @@ quit;
%put NOTE-;%put NOTE-;
%mend mp_ds2cards;/**
@file
- @brief Export a dataset to a CSV file
- @details Export to a file or a fileref
+ @brief Export a dataset to a CSV file WITH leading blanks
+ @details Export a dataset to a file or fileref, retaining leading blanks.
+
Usage:
%mp_ds2csv(sashelp.class,outref="%sysfunc(pathname(work))/file.csv")
- @param ds The dataset to be exported
- @param outfile= The output filename - should be quoted.
- @param outref= The output fileref (takes precedence if provided)
- @param outencoding= The output encoding to use (unquoted)
+ Why use mp_ds2csv over, say, proc export?
+
+ 1. Ability to retain leading blanks (this is a major one)
+ 2. Control the header format
+ 3. Simple one-liner
+
+ @param [in] ds The dataset to be exported
+ @param [in] dlm= (COMMA) The delimeter to apply. For SASJS, will always be
+ COMMA. Supported values:
+ @li COMMA
+ @li SEMICOLON
+ @param [in] headerformat= (LABEL) The format to use for the header section.
+ Valid values:
+ @li LABEL - Use the variable label (or name, if blank)
+ @li NAME - Use the variable name
+ @li SASJS - Used to create sasjs-formatted input CSVs, eg for use in
+ mp_testservice.sas
+ @param [out] outfile= The output filename - should be quoted.
+ @param [out] outref= (0) The output fileref (takes precedence if provided)
+ @param [in] outencoding= (0) The output encoding to use (unquoted)
+ @param [in] termstr= (CRLF) The line seperator to use. For SASJS, will
+ always be CRLF. Valid values:
+ @li CRLF
+ @li LF
+
+ SAS Macros
+ @li mf_getvarlist.sas
+ @li mf_getvartype.sas
@version 9.2
@author Allan Bowe (credit mjsq)
**/
-%macro mp_ds2csv(ds, outref=0, outfile=, outencoding=0
+%macro mp_ds2csv(ds
+ ,dlm=COMMA
+ ,outref=0
+ ,outfile=
+ ,outencoding=0
+ ,headerformat=LABEL
+ ,termstr=CRLF
)/*/STORE SOURCE*/;
+%local outloc delim i varlist var vcnt vat dsv;
+
%if not %sysfunc(exist(&ds)) %then %do;
%put %str(WARN)ING: &ds does not exist;
%return;
@@ -4681,33 +4724,91 @@ quit;
%if &outencoding=0 %then %let outencoding=;
%else %let outencoding=encoding="&outencoding";
-%local outloc;
%if &outref=0 %then %let outloc=&outfile;
%else %let outloc=&outref;
+%if &headerformat=SASJS %then %do;
+ %let delim=",";
+ %let termstr=CRLF;
+%end;
+%else %if &dlm=COMMA %then %let delim=",";
+%else %let delim=";";
+
/* credit to mjsq - https://stackoverflow.com/a/55642267 */
/* first get headers */
data _null_;
- file &outloc dlm=',' dsd &outencoding lrecl=32767;
- length header $ 2000;
+ file &outloc &outencoding lrecl=32767 termstr=&termstr;
+ length header $ 2000 varnm $32;
dsid=open("&ds.","i");
num=attrn(dsid,"nvars");
do i=1 to num;
- header = cats(coalescec(varlabel(dsid,i),varname(dsid,i)));
+ varnm=upcase(varname(dsid,i));
+ %if &headerformat=NAME %then %do;
+ header=cats(varnm,&delim);
+ %end;
+ %else %if &headerformat=LABEL %then %do;
+ header = cats(coalescec(varlabel(dsid,i),varnm),&delim);
+ %end;
+ %else %if &headerformat=SASJS %then %do;
+ if vartype(dsid,i)='C' then header=cats(varnm,':$char',varlen(dsid,i),'.');
+ else header=cats(varnm,':best.');
+ %end;
+ %else %do;
+ %put &sysmacroname: Invalid headerformat value (&headerformat);
+ %return;
+ %end;
put header @;
end;
rc=close(dsid);
run;
+%let varlist=%mf_getvarlist(&ds);
+%let vcnt=%sysfunc(countw(&varlist));
+
+/**
+ * The $quote modifier (without a width) will take the length from the variable
+ * and increase by two. However this will lead to truncation where the value
+ * contains double quotes (which are doubled up). To get around this, scan the
+ * data to see the max number of double quotes, so that the appropriate width
+ * can be applied in the subsequent step.
+ */
+data _null_;
+ set &ds end=last;
+%do i=1 %to &vcnt;
+ %let var=%scan(&varlist,&i);
+ %if %mf_getvartype(&ds,&var)=C %then %do;
+ %let dsv1=%mf_getuniquename(prefix=csvcol1_);
+ %let dsv2=%mf_getuniquename(prefix=csvcol2_);
+ retain &dsv1 0;
+ &dsv2=length(&var)+countc(&var,'"');
+ if &dsv2>&dsv1 then &dsv1=&dsv2;
+ if last then call symputx(
+ "vlen&i"
+ /* should be no shorter than varlen, and no longer than 32767 */
+ ,cats('$quote',min(&dsv1+2,32767),'.')
+ ,'l'
+ );
+ %end;
+%end;
+
+%let vat=@;
/* next, export data */
data _null_;
set &ds.;
- file &outloc mod dlm=',' dsd &outencoding lrecl=32767;
- put (_all_) (+0);
+ file &outloc mod dlm=&delim dsd &outencoding lrecl=32767 termstr=&termstr;
+ %do i=1 %to &vcnt;
+ %let var=%scan(&varlist,&i);
+ %if &i=&vcnt %then %let vat=;
+ %if %mf_getvartype(&ds,&var)=N %then %do;
+ put &var &vat;
+ %end;
+ %else %do;
+ put &var &&vlen&i "," &vat;
+ %end;
+ %end;
run;
-
%mend mp_ds2csv;/**
@file
@brief Converts every value in a dataset to formatted value
@@ -7869,11 +7970,9 @@ data _null_;
run;
options
- noautocorrect /* disallow misspelled procedure names */
compress=CHAR /* default is none so ensure we have something! */
datastmtchk=ALLKEYWORDS /* protection from overwriting input datasets */
- dsoptions=note2err /* undocumented - convert bad NOTEs to ERRs */
- %str(err)orcheck=STRICT /* catch errs in libname/filename statements */
+ errorcheck=STRICT /* catch errs in libname/filename statements */
fmterr /* ensure err when a format cannot be found */
mergenoby=%str(ERR)OR /* throw err when a merge has no BY variables */
missing=. /* changing this can cause hard to detect errs */
@@ -7885,6 +7984,10 @@ options
validvarname=V7 /* avoid special characters etc in variable names */
varinitchk=%str(ERR)OR /* avoid data mistakes from variable name typos */
varlenchk=%str(ERR)OR /* fail hard if truncation (data loss) can result */
+%if %substr(&sysver,1,1) ne 4 %then %do;
+ noautocorrect /* disallow misspelled procedure names */
+ dsoptions=note2err /* undocumented - convert bad NOTEs to ERRs */
+%end;
;
%mend mp_init;/**
@@ -8046,7 +8149,12 @@ options
));
%do i=1 %to &numcols;
length &&name&i $&&len&i;
- &&name&i=left(put(&&newname&i,&&fmt&i));
+ %if &&typelong&i=num %then %do;
+ &&name&i=left(put(&&newname&i,&&fmt&i));
+ %end;
+ %else %do;
+ &&name&i=put(&&newname&i,&&fmt&i);
+ %end;
drop &&newname&i;
%end;
if _error_ then call symputx('syscc',1012);
@@ -10842,14 +10950,12 @@ libname &lib clear;
%mend mp_testjob;/**
- @file mp_testservice.sas
- @brief Will execute a test against a SASjs web service on SAS 9 or Viya
+ @file
+ @brief Will execute a SASjs web service on SAS 9 or Viya
@details Prepares the input files and retrieves the resulting datasets from
the response JSON.
- %mp_testjob(
- duration=60*5
- )
+
Note - the _webout fileref should NOT be assigned prior to running this macro.
@@ -10857,6 +10963,10 @@ libname &lib clear;
@param [in] inputfiles=(0) A list of space seperated fileref:filename pairs as
follows:
inputfiles=inref:filename inref2:filename2
+ @param [in] inputdatasets= (0) All datasets in this space seperated list are
+ converted into SASJS-formatted CSVs (see mp_ds2csv.sas) files and added to
+ the list of `inputfiles` for ingestion. The dataset will be sent with the
+ same name (no need for a colon modifier).
@param [in] inputparams=(0) A dataset containing name/value pairs in the
following format:
|name:$32|value:$1000|
@@ -10881,9 +10991,13 @@ libname &lib clear;
@li mf_getuniquename.sas
@li mp_abort.sas
@li mp_binarycopy.sas
+ @li mp_ds2csv.sas
@li mv_getjobresult.sas
@li mv_jobflow.sas
+ Related Programs
+ @li mp_testservice.test.sas
+
@version 9.4
@author Allan Bowe
@@ -10891,6 +11005,7 @@ libname &lib clear;
%macro mp_testservice(program,
inputfiles=0,
+ inputdatasets=0,
inputparams=0,
debug=log,
mdebug=0,
@@ -10899,7 +11014,7 @@ libname &lib clear;
viyaresult=WEBOUT_JSON,
viyacontext=SAS Job Execution compute context
)/*/STORE SOURCE*/;
-%local dbg;
+%local dbg pcnt fref1 webref i webcount var platform;
%if &mdebug=1 %then %do;
%put &sysmacroname entry vars:;
%put _local_;
@@ -10907,7 +11022,6 @@ libname &lib clear;
%else %let dbg=*;
/* sanitise inputparams */
-%local pcnt;
%let pcnt=0;
%if &inputparams ne 0 %then %do;
data _null_;
@@ -10929,17 +11043,25 @@ libname &lib clear;
)
%end;
+/* convert inputdatasets to filerefs */
+%if "&inputdatasets" ne "0" %then %do;
+ %if %quote(&inputfiles)=0 %then %let inputfiles=;
+ %do i=1 %to %sysfunc(countw(&inputdatasets,%str( )));
+ %let var=%scan(&inputdatasets,&i,%str( ));
+ %local dsref&i;
+ %let dsref&i=%mf_getuniquefileref();
+ %mp_ds2csv(&var,outref=&&dsref&i,headerformat=SASJS)
+ %let inputfiles=&inputfiles &&dsref&i:%scan(&var,-1,.);
+ %end;
+%end;
+
-%local fref1 webref;
%let fref1=%mf_getuniquefileref();
%let webref=%mf_getuniquefileref();
-
-%local platform;
%let platform=%mf_getplatform();
%if &platform=SASMETA %then %do;
/* parse the input files */
- %local webcount i var;
%if %quote(&inputfiles) ne 0 %then %do;
%let webcount=%sysfunc(countw(&inputfiles));
%put &=webcount;
@@ -13347,6 +13469,7 @@ run;
data &outds (keep=stpuri prompturi fileuri texturi);
length stpuri prompturi fileuri texturi serveruri $256 ;
+ if _n_=1 then call missing (of _all_);
set &outds;
/* final checks on uris */
@@ -13750,7 +13873,12 @@ data _null_;
put ' )); ';
put ' %do i=1 %to &numcols; ';
put ' length &&name&i $&&len&i; ';
- put ' &&name&i=left(put(&&newname&i,&&fmt&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); ';
@@ -15731,7 +15859,11 @@ run;
filename __mc1 temp;
filename __mc2 temp;
-data &outds; length serveruri servername $200; stop;run;
+data &outds;
+ length serveruri servername $200;
+ call missing (of _all_);
+ stop;
+run;
%do x=1 %to &repocnt;
options metarepository=&&repo&x;
proc metadata in=
@@ -15750,13 +15882,16 @@ data &outds; length serveruri servername $200; stop;run;
data _null_;
file __mc2;
put '';
- put "/GetMetadataObjects/Objects/ServerContext";
+ put "/GetMetadataObjects/Objects/ServerContext";
+ put "";
put '';
- put "/GetMetadataObjects/Objects/ServerContext/@Id";
+ put "/GetMetadataObjects/Objects/ServerContext/@Id";
+ put "";
put "characterstring200";
put '';
put '';
- put "/GetMetadataObjects/Objects/ServerContext/@Name";
+ put "/GetMetadataObjects/Objects/ServerContext/@Name";
+ put "";
put "characterstring200";
put '';
put '
';
@@ -18881,7 +19016,12 @@ data _null_;
put ' )); ';
put ' %do i=1 %to &numcols; ';
put ' length &&name&i $&&len&i; ';
- put ' &&name&i=left(put(&&newname&i,&&fmt&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); ';
diff --git a/base/mp_createwebservice.sas b/base/mp_createwebservice.sas
index 72b25d7..a719f17 100644
--- a/base/mp_createwebservice.sas
+++ b/base/mp_createwebservice.sas
@@ -24,20 +24,22 @@ Usage:
%webout(OBJ,example2) * Object format, easier to work with ;
%webout(CLOSE)
;;;;
- %mp_createwebservice(path=/Public/app/common,name=appInit,code=ft15f001,replace=YES)
+ %mp_createwebservice(path=/Public/app/common,name=appInit,replace=YES)
SAS Macros
@li mf_getplatform.sas
@li mm_createwebservice.sas
@li mv_createwebservice.sas
- @param path= The full folder path where the service will be created
- @param name= Service name. Avoid spaces.
- @param desc= The description of the service (optional)
- @param precode= Space separated list of filerefs, pointing to the code that
- needs to be attached to the beginning of the service (optional)
- @param code= Space seperated fileref(s) of the actual code to be added
- @param replace= select YES to replace any existing service in that location
+ @param [in,out] path= The full folder path where the service will be created
+ @param [in,out] name= Service name. Avoid spaces.
+ @param [in] desc= The description of the service (optional)
+ @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] replace= (YES) Select YES to replace any existing service in that
+ location
@version 9.2
diff --git a/base/mp_deletefolder.sas b/base/mp_deletefolder.sas
index ee31d77..55b1e3f 100644
--- a/base/mp_deletefolder.sas
+++ b/base/mp_deletefolder.sas
@@ -51,6 +51,7 @@
data _null_;
set work.&tempds end=last;
length fref $8;
+ fref='';
rc=filename(fref,filepath);
rc=fdelete(fref);
if rc then do;
diff --git a/base/mp_dirlist.sas b/base/mp_dirlist.sas
index b5f9dfd..a34f193 100644
--- a/base/mp_dirlist.sas
+++ b/base/mp_dirlist.sas
@@ -82,7 +82,8 @@ data &out_ds(compress=no
keep=file_or_folder filepath filename ext msg directory level
);
length directory filepath $500 fref fref2 $8 file_or_folder $6 filename $80
- ext $20 msg $200;
+ ext $20 msg $200 foption $16;
+ if _n_=1 then call missing(of _all_);
retain level &level;
%if &fref=0 %then %do;
rc = filename(fref, "&path");
@@ -93,7 +94,13 @@ data &out_ds(compress=no
%end;
if rc = 0 then do;
did = dopen(fref);
- directory=dinfo(did,'Directory');
+ /* attribute is OS-dependent - could be "Directory" or "Directory Name" */
+ numopts=doptnum(did);
+ do i=1 to numopts;
+ foption=doptname(did,i);
+ if foption=:'Directory' then i=numopts;
+ end;
+ directory=dinfo(did,foption);
if did=0 then do;
putlog "NOTE: This directory is empty - " directory;
msg=sysmsg();
diff --git a/base/mp_ds2csv.sas b/base/mp_ds2csv.sas
index 1b556be..165013c 100644
--- a/base/mp_ds2csv.sas
+++ b/base/mp_ds2csv.sas
@@ -1,23 +1,56 @@
/**
@file
- @brief Export a dataset to a CSV file
- @details Export to a file or a fileref
+ @brief Export a dataset to a CSV file WITH leading blanks
+ @details Export a dataset to a file or fileref, retaining leading blanks.
+
Usage:
%mp_ds2csv(sashelp.class,outref="%sysfunc(pathname(work))/file.csv")
- @param ds The dataset to be exported
- @param outfile= The output filename - should be quoted.
- @param outref= The output fileref (takes precedence if provided)
- @param outencoding= The output encoding to use (unquoted)
+ Why use mp_ds2csv over, say, proc export?
+
+ 1. Ability to retain leading blanks (this is a major one)
+ 2. Control the header format
+ 3. Simple one-liner
+
+ @param [in] ds The dataset to be exported
+ @param [in] dlm= (COMMA) The delimeter to apply. For SASJS, will always be
+ COMMA. Supported values:
+ @li COMMA
+ @li SEMICOLON
+ @param [in] headerformat= (LABEL) The format to use for the header section.
+ Valid values:
+ @li LABEL - Use the variable label (or name, if blank)
+ @li NAME - Use the variable name
+ @li SASJS - Used to create sasjs-formatted input CSVs, eg for use in
+ mp_testservice.sas
+ @param [out] outfile= The output filename - should be quoted.
+ @param [out] outref= (0) The output fileref (takes precedence if provided)
+ @param [in] outencoding= (0) The output encoding to use (unquoted)
+ @param [in] termstr= (CRLF) The line seperator to use. For SASJS, will
+ always be CRLF. Valid values:
+ @li CRLF
+ @li LF
+
+ SAS Macros
+ @li mf_getvarlist.sas
+ @li mf_getvartype.sas
@version 9.2
@author Allan Bowe (credit mjsq)
**/
-%macro mp_ds2csv(ds, outref=0, outfile=, outencoding=0
+%macro mp_ds2csv(ds
+ ,dlm=COMMA
+ ,outref=0
+ ,outfile=
+ ,outencoding=0
+ ,headerformat=LABEL
+ ,termstr=CRLF
)/*/STORE SOURCE*/;
+%local outloc delim i varlist var vcnt vat dsv;
+
%if not %sysfunc(exist(&ds)) %then %do;
%put %str(WARN)ING: &ds does not exist;
%return;
@@ -28,31 +61,89 @@
%if &outencoding=0 %then %let outencoding=;
%else %let outencoding=encoding="&outencoding";
-%local outloc;
%if &outref=0 %then %let outloc=&outfile;
%else %let outloc=&outref;
+%if &headerformat=SASJS %then %do;
+ %let delim=",";
+ %let termstr=CRLF;
+%end;
+%else %if &dlm=COMMA %then %let delim=",";
+%else %let delim=";";
+
/* credit to mjsq - https://stackoverflow.com/a/55642267 */
/* first get headers */
data _null_;
- file &outloc dlm=',' dsd &outencoding lrecl=32767;
- length header $ 2000;
+ file &outloc &outencoding lrecl=32767 termstr=&termstr;
+ length header $ 2000 varnm $32;
dsid=open("&ds.","i");
num=attrn(dsid,"nvars");
do i=1 to num;
- header = cats(coalescec(varlabel(dsid,i),varname(dsid,i)));
+ varnm=upcase(varname(dsid,i));
+ %if &headerformat=NAME %then %do;
+ header=cats(varnm,&delim);
+ %end;
+ %else %if &headerformat=LABEL %then %do;
+ header = cats(coalescec(varlabel(dsid,i),varnm),&delim);
+ %end;
+ %else %if &headerformat=SASJS %then %do;
+ if vartype(dsid,i)='C' then header=cats(varnm,':$char',varlen(dsid,i),'.');
+ else header=cats(varnm,':best.');
+ %end;
+ %else %do;
+ %put &sysmacroname: Invalid headerformat value (&headerformat);
+ %return;
+ %end;
put header @;
end;
rc=close(dsid);
run;
+%let varlist=%mf_getvarlist(&ds);
+%let vcnt=%sysfunc(countw(&varlist));
+
+/**
+ * The $quote modifier (without a width) will take the length from the variable
+ * and increase by two. However this will lead to truncation where the value
+ * contains double quotes (which are doubled up). To get around this, scan the
+ * data to see the max number of double quotes, so that the appropriate width
+ * can be applied in the subsequent step.
+ */
+data _null_;
+ set &ds end=last;
+%do i=1 %to &vcnt;
+ %let var=%scan(&varlist,&i);
+ %if %mf_getvartype(&ds,&var)=C %then %do;
+ %let dsv1=%mf_getuniquename(prefix=csvcol1_);
+ %let dsv2=%mf_getuniquename(prefix=csvcol2_);
+ retain &dsv1 0;
+ &dsv2=length(&var)+countc(&var,'"');
+ if &dsv2>&dsv1 then &dsv1=&dsv2;
+ if last then call symputx(
+ "vlen&i"
+ /* should be no shorter than varlen, and no longer than 32767 */
+ ,cats('$quote',min(&dsv1+2,32767),'.')
+ ,'l'
+ );
+ %end;
+%end;
+
+%let vat=@;
/* next, export data */
data _null_;
set &ds.;
- file &outloc mod dlm=',' dsd &outencoding lrecl=32767;
- put (_all_) (+0);
+ file &outloc mod dlm=&delim dsd &outencoding lrecl=32767 termstr=&termstr;
+ %do i=1 %to &vcnt;
+ %let var=%scan(&varlist,&i);
+ %if &i=&vcnt %then %let vat=;
+ %if %mf_getvartype(&ds,&var)=N %then %do;
+ put &var &vat;
+ %end;
+ %else %do;
+ put &var &&vlen&i "," &vat;
+ %end;
+ %end;
run;
-
%mend mp_ds2csv;
\ No newline at end of file
diff --git a/base/mp_init.sas b/base/mp_init.sas
index 1e191ac..e94a27d 100644
--- a/base/mp_init.sas
+++ b/base/mp_init.sas
@@ -53,11 +53,9 @@ data _null_;
run;
options
- noautocorrect /* disallow misspelled procedure names */
compress=CHAR /* default is none so ensure we have something! */
datastmtchk=ALLKEYWORDS /* protection from overwriting input datasets */
- dsoptions=note2err /* undocumented - convert bad NOTEs to ERRs */
- %str(err)orcheck=STRICT /* catch errs in libname/filename statements */
+ errorcheck=STRICT /* catch errs in libname/filename statements */
fmterr /* ensure err when a format cannot be found */
mergenoby=%str(ERR)OR /* throw err when a merge has no BY variables */
missing=. /* changing this can cause hard to detect errs */
@@ -69,6 +67,10 @@ options
validvarname=V7 /* avoid special characters etc in variable names */
varinitchk=%str(ERR)OR /* avoid data mistakes from variable name typos */
varlenchk=%str(ERR)OR /* fail hard if truncation (data loss) can result */
+%if %substr(&sysver,1,1) ne 4 %then %do;
+ noautocorrect /* disallow misspelled procedure names */
+ dsoptions=note2err /* undocumented - convert bad NOTEs to ERRs */
+%end;
;
%mend mp_init;
\ No newline at end of file
diff --git a/base/mp_jsonout.sas b/base/mp_jsonout.sas
index 5a3443d..251850e 100644
--- a/base/mp_jsonout.sas
+++ b/base/mp_jsonout.sas
@@ -157,7 +157,12 @@
));
%do i=1 %to &numcols;
length &&name&i $&&len&i;
- &&name&i=left(put(&&newname&i,&&fmt&i));
+ %if &&typelong&i=num %then %do;
+ &&name&i=left(put(&&newname&i,&&fmt&i));
+ %end;
+ %else %do;
+ &&name&i=put(&&newname&i,&&fmt&i);
+ %end;
drop &&newname&i;
%end;
if _error_ then call symputx('syscc',1012);
diff --git a/base/mp_testservice.sas b/base/mp_testservice.sas
index b16a991..c6c2215 100644
--- a/base/mp_testservice.sas
+++ b/base/mp_testservice.sas
@@ -1,12 +1,10 @@
/**
- @file mp_testservice.sas
- @brief Will execute a test against a SASjs web service on SAS 9 or Viya
+ @file
+ @brief Will execute a SASjs web service on SAS 9 or Viya
@details Prepares the input files and retrieves the resulting datasets from
the response JSON.
- %mp_testjob(
- duration=60*5
- )
+
Note - the _webout fileref should NOT be assigned prior to running this macro.
@@ -14,6 +12,10 @@
@param [in] inputfiles=(0) A list of space seperated fileref:filename pairs as
follows:
inputfiles=inref:filename inref2:filename2
+ @param [in] inputdatasets= (0) All datasets in this space seperated list are
+ converted into SASJS-formatted CSVs (see mp_ds2csv.sas) files and added to
+ the list of `inputfiles` for ingestion. The dataset will be sent with the
+ same name (no need for a colon modifier).
@param [in] inputparams=(0) A dataset containing name/value pairs in the
following format:
|name:$32|value:$1000|
@@ -38,9 +40,13 @@
@li mf_getuniquename.sas
@li mp_abort.sas
@li mp_binarycopy.sas
+ @li mp_ds2csv.sas
@li mv_getjobresult.sas
@li mv_jobflow.sas
+ Related Programs
+ @li mp_testservice.test.sas
+
@version 9.4
@author Allan Bowe
@@ -48,6 +54,7 @@
%macro mp_testservice(program,
inputfiles=0,
+ inputdatasets=0,
inputparams=0,
debug=log,
mdebug=0,
@@ -56,7 +63,7 @@
viyaresult=WEBOUT_JSON,
viyacontext=SAS Job Execution compute context
)/*/STORE SOURCE*/;
-%local dbg;
+%local dbg pcnt fref1 webref i webcount var platform;
%if &mdebug=1 %then %do;
%put &sysmacroname entry vars:;
%put _local_;
@@ -64,7 +71,6 @@
%else %let dbg=*;
/* sanitise inputparams */
-%local pcnt;
%let pcnt=0;
%if &inputparams ne 0 %then %do;
data _null_;
@@ -86,17 +92,25 @@
)
%end;
+/* convert inputdatasets to filerefs */
+%if "&inputdatasets" ne "0" %then %do;
+ %if %quote(&inputfiles)=0 %then %let inputfiles=;
+ %do i=1 %to %sysfunc(countw(&inputdatasets,%str( )));
+ %let var=%scan(&inputdatasets,&i,%str( ));
+ %local dsref&i;
+ %let dsref&i=%mf_getuniquefileref();
+ %mp_ds2csv(&var,outref=&&dsref&i,headerformat=SASJS)
+ %let inputfiles=&inputfiles &&dsref&i:%scan(&var,-1,.);
+ %end;
+%end;
+
-%local fref1 webref;
%let fref1=%mf_getuniquefileref();
%let webref=%mf_getuniquefileref();
-
-%local platform;
%let platform=%mf_getplatform();
%if &platform=SASMETA %then %do;
/* parse the input files */
- %local webcount i var;
%if %quote(&inputfiles) ne 0 %then %do;
%let webcount=%sysfunc(countw(&inputfiles));
%put &=webcount;
diff --git a/meta/mm_createstp.sas b/meta/mm_createstp.sas
index 722e6c2..e4f5196 100755
--- a/meta/mm_createstp.sas
+++ b/meta/mm_createstp.sas
@@ -177,6 +177,7 @@ run;
data &outds (keep=stpuri prompturi fileuri texturi);
length stpuri prompturi fileuri texturi serveruri $256 ;
+ if _n_=1 then call missing (of _all_);
set &outds;
/* final checks on uris */
diff --git a/meta/mm_createwebservice.sas b/meta/mm_createwebservice.sas
index e90f2fd..371ebde 100644
--- a/meta/mm_createwebservice.sas
+++ b/meta/mm_createwebservice.sas
@@ -190,7 +190,12 @@ data _null_;
put ' )); ';
put ' %do i=1 %to &numcols; ';
put ' length &&name&i $&&len&i; ';
- put ' &&name&i=left(put(&&newname&i,&&fmt&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); ';
diff --git a/meta/mm_getservercontexts.sas b/meta/mm_getservercontexts.sas
index 4d8d2a7..dc92547 100644
--- a/meta/mm_getservercontexts.sas
+++ b/meta/mm_getservercontexts.sas
@@ -41,7 +41,11 @@ run;
filename __mc1 temp;
filename __mc2 temp;
-data &outds; length serveruri servername $200; stop;run;
+data &outds;
+ length serveruri servername $200;
+ call missing (of _all_);
+ stop;
+run;
%do x=1 %to &repocnt;
options metarepository=&&repo&x;
proc metadata in=
@@ -60,13 +64,16 @@ data &outds; length serveruri servername $200; stop;run;
data _null_;
file __mc2;
put '';
- put "/GetMetadataObjects/Objects/ServerContext";
+ put "/GetMetadataObjects/Objects/ServerContext";
+ put "";
put '';
- put "/GetMetadataObjects/Objects/ServerContext/@Id";
+ put "/GetMetadataObjects/Objects/ServerContext/@Id";
+ put "";
put "characterstring200";
put '';
put '';
- put "/GetMetadataObjects/Objects/ServerContext/@Name";
+ put "/GetMetadataObjects/Objects/ServerContext/@Name";
+ put "";
put "characterstring200";
put '';
put '
';
diff --git a/tests/crossplatform/mp_ds2csv.test.sas b/tests/crossplatform/mp_ds2csv.test.sas
new file mode 100644
index 0000000..1dcaaee
--- /dev/null
+++ b/tests/crossplatform/mp_ds2csv.test.sas
@@ -0,0 +1,96 @@
+/**
+ @file
+ @brief Testing mp_ds2csv.sas macro
+
+ SAS Macros
+ @li mp_ds2csv.sas
+ @li mp_assert.sas
+ @li mp_assertscope.sas
+
+**/
+
+data work.somedata;
+ x=1;
+ y=' t"w"o';
+ z=.z;
+ label x='x factor';
+run;
+
+/**
+ * Test 1 - default CSV
+ */
+%mp_assertscope(SNAPSHOT)
+%mp_ds2csv(work.somedata,outfile="&sasjswork/test1.csv")
+%mp_assertscope(COMPARE)
+
+%let test1b=FAIL;
+data _null_;
+ infile "&sasjswork/test1.csv";
+ input;
+ list;
+ if _n_=1 then call symputx('test1a',_infile_);
+ else if _infile_='1,"t""w""o",Z' then call symputx('test1b','PASS');
+run;
+
+%mp_assert(
+ iftrue=("&test1a"="x factor,Y,Z"),
+ desc=Checking header row Test 1,
+ outds=work.test_results
+)
+%mp_assert(
+ iftrue=("&test1b"="PASS"),
+ desc=Checking data row Test 1,
+ outds=work.test_results
+)
+
+/**
+ * Test 2 - NAME header with fileref and semicolons
+ */
+filename test2 "&sasjswork/test2.csv";
+%mp_ds2csv(work.somedata,outref=test2,dlm=SEMICOLON,headerformat=NAME)
+
+%let test2b=FAIL;
+data _null_;
+ infile test2;
+ input;
+ list;
+ if _n_=1 then call symputx('test2a',_infile_);
+ else if _infile_='1;"t""w""o";Z' then call symputx('test2b','PASS');
+run;
+
+%mp_assert(
+ iftrue=("&test2a"="X;Y;Z"),
+ desc=Checking header row Test 2,
+ outds=work.test_results
+)
+%mp_assert(
+ iftrue=("&test2b"="PASS"),
+ desc=Checking data row Test 2,
+ outds=work.test_results
+)
+
+/**
+ * Test 3 - SASjs format
+ */
+filename test3 "&sasjswork/test3.csv";
+%mp_ds2csv(work.somedata,outref=test3,headerformat=SASJS)
+
+%let test3b=FAIL;
+data _null_;
+ infile test3;
+ input;
+ list;
+ if _n_=1 then call symputx('test3a',_infile_);
+ else if _infile_='1;"t""w""o";Z' then call symputx('test3b','PASS');
+run;
+
+%mp_assert(
+ iftrue=("&test3a"="X:best. Y:$char7. Z:best."),
+ desc=Checking header row Test 3,
+ outds=work.test_results
+)
+%mp_assert(
+ iftrue=("&test3b"="PASS"),
+ desc=Checking data row Test 3,
+ outds=work.test_results
+)
\ No newline at end of file
diff --git a/tests/crossplatform/mp_testservice.test.sas b/tests/crossplatform/mp_testservice.test.sas
new file mode 100644
index 0000000..069539f
--- /dev/null
+++ b/tests/crossplatform/mp_testservice.test.sas
@@ -0,0 +1,97 @@
+/**
+ @file
+ @brief Testing mp_testservice.sas macro
+
+ Be sure to run %let mcTestAppLoc=/Public/temp/macrocore; when
+ runnin in Studio
+
+ SAS Macros
+ @li mp_createwebservice.sas
+ @li mp_testservice.sas
+ @li mp_assert.sas
+
+**/
+
+
+filename ft15f001 temp;
+parmcards4;
+ %webout(FETCH)
+ %webout(OPEN)
+ %macro x();
+ %do i=1 %to &_webin_file_count;
+ %webout(OBJ,&&_webin_name&i,missing=STRING)
+ %end;
+ %mend x; %x()
+ %webout(CLOSE)
+;;;;
+%mp_createwebservice(path=&mcTestAppLoc/services,name=sendObj)
+
+%mp_assert(
+ iftrue=(&syscc=0),
+ desc=No errors after service creation,
+ outds=work.test_results
+)
+
+/**
+ * Test 1 - send a dataset
+ */
+data work.somedata1 work.somedata2;
+ x=1;
+ y=' t"w"o';
+ z=.z;
+ label x='x factor';
+ output;
+run;
+
+%mp_testservice(&mcTestAppLoc/services/sendObj,
+ inputdatasets=work.somedata1 work.somedata2,
+ debug=log,
+ mdebug=1,
+ outlib=testlib1,
+ outref=test1
+)
+
+%global test1a test1b test1c test1d;
+data _null_;
+ infile test1;
+ input;
+ if _n_=3 then do;
+ if _infile_=', "somedata1":' then call symputx('test1a','PASS');
+ else putlog _n_= _infile_=;
+ end;
+ else if _n_=5 then do;
+ if _infile_='{"X":1 ,"Y":" t\"w\"o" ,"Z":"Z" }' then
+ call symputx('test1b','PASS');
+ else putlog _n_= _infile_=;
+ end;
+ else if _n_=6 then do;
+ if _infile_='], "somedata2":' then call symputx('test1c','PASS');
+ else putlog _n_= _infile_=;
+ end;
+ else if _n_=8 then do;
+ if _infile_='{"X":1 ,"Y":" t\"w\"o" ,"Z":"Z" }' then
+ call symputx('test1d','PASS');
+ else putlog _n_= _infile_=;
+ end;
+run;
+
+%mp_assert(
+ iftrue=(&test1a=PASS),
+ desc=Test 1 table 1 name,
+ outds=work.test_results
+)
+%mp_assert(
+ iftrue=(&test1b=PASS),
+ desc=Test 1 table 1 values,
+ outds=work.test_results
+)
+%mp_assert(
+ iftrue=(&test1c=PASS),
+ desc=Test 1 table 2 name,
+ outds=work.test_results
+)
+%mp_assert(
+ iftrue=(&test1d=PASS),
+ desc=Test 1 table 2 values,
+ outds=work.test_results
+)
\ No newline at end of file
diff --git a/viya/mv_createwebservice.sas b/viya/mv_createwebservice.sas
index 045690e..e0768bd 100644
--- a/viya/mv_createwebservice.sas
+++ b/viya/mv_createwebservice.sas
@@ -334,7 +334,12 @@ data _null_;
put ' )); ';
put ' %do i=1 %to &numcols; ';
put ' length &&name&i $&&len&i; ';
- put ' &&name&i=left(put(&&newname&i,&&fmt&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); ';