mirror of
https://github.com/sasjs/core.git
synced 2025-12-10 14:04:36 +00:00
feat: creating new mx_ suite of macros!
also adding new mx_getcode macro
This commit is contained in:
92
README.md
92
README.md
@@ -31,14 +31,14 @@ Documentation: https://core.sasjs.io
|
||||
|
||||
## Components
|
||||
|
||||
### BASE library (All Platforms)
|
||||
### BASE folder (All Platforms)
|
||||
|
||||
- OS independent
|
||||
- Works on all SAS Platforms
|
||||
- No X command
|
||||
- Prefixes: _mf_, _mp_
|
||||
|
||||
### DDL library (All Platforms)
|
||||
### DDL folder (All Platforms)
|
||||
|
||||
- OS independent
|
||||
- Works on all SAS Platforms
|
||||
@@ -47,45 +47,13 @@ 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 folder (All Platforms)
|
||||
|
||||
- Function and macro names are identical, except for special cases
|
||||
- Prefixes: _mcf_
|
||||
|
||||
The fcmp macros are used to generate fcmp functions, and can be used with or without the `proc fcmp` wrapper.
|
||||
|
||||
### META library (SAS9 only)
|
||||
|
||||
Macros used in SAS EBI, which connect to the metadata server.
|
||||
|
||||
- OS independent
|
||||
- Metadata aware
|
||||
- No X command
|
||||
- Prefixes: _mm_
|
||||
|
||||
### SERVER library (@sasjs/server only)
|
||||
These macros are used for building applications using [@sasjs/server](https://server.sasjs.io) - an open source REST API for Desktop SAS.
|
||||
|
||||
- OS independent
|
||||
- @sasjs/server aware
|
||||
- No X command
|
||||
- Prefixes: _ms_
|
||||
|
||||
### VIYA library (Viya only)
|
||||
|
||||
Macros used for interfacing with SAS Viya.
|
||||
|
||||
- OS independent
|
||||
- No X command
|
||||
- Prefixes: _mv_, _mvf_
|
||||
|
||||
### METAX library (SAS9 only)
|
||||
|
||||
- OS specific
|
||||
- Metadata aware
|
||||
- X command enabled
|
||||
- Prefixes: _mmw_,_mmu_,_mmx_
|
||||
|
||||
### LUA library
|
||||
|
||||
Wait - this is a macro library - what is LUA doing here? Well, it is a little known fact that you CAN run LUA within a SAS Macro. It has to be written to a text file with a `.lua` extension, from where you can `%include` it. So, without using the `proc lua` wrapper.
|
||||
@@ -106,13 +74,61 @@ run;
|
||||
|
||||
- Prefixes: _ml_
|
||||
|
||||
### META folder (SAS9 only)
|
||||
|
||||
Macros used in SAS EBI, which connect to the metadata server.
|
||||
|
||||
- OS independent
|
||||
- Metadata aware
|
||||
- No X command
|
||||
- Prefixes: _mm_
|
||||
|
||||
### METAX folder (SAS9 only)
|
||||
|
||||
- OS specific
|
||||
- Metadata aware
|
||||
- X command enabled
|
||||
- Prefixes: _mmw_,_mmu_,_mmx_
|
||||
|
||||
### SERVER folder (@sasjs/server only)
|
||||
These macros are used for building applications using [@sasjs/server](https://server.sasjs.io) - an open source REST API for Desktop SAS.
|
||||
|
||||
- OS independent
|
||||
- @sasjs/server aware
|
||||
- No X command
|
||||
- Prefixes: _ms_
|
||||
|
||||
### VIYA folder (Viya only)
|
||||
|
||||
Macros used for interfacing with SAS Viya.
|
||||
|
||||
- OS independent
|
||||
- No X command
|
||||
- Prefixes: _mv_, _mvf_
|
||||
|
||||
### XPLATFORM folder (Viya, Meta, and Server)
|
||||
|
||||
Sometimes it is helpful to use a macro that can be used interchangeably regardless of the server type on which is is running (SASVIYA, SAS9, SASJS).
|
||||
|
||||
- OS independent
|
||||
- No X command
|
||||
- Prefixes: _mx_
|
||||
|
||||
## Installation
|
||||
|
||||
First, download the repo to a location your SAS system can access. Then update your sasautos path to include the components you wish to have available, eg:
|
||||
|
||||
```sas
|
||||
options insert=(sasautos="/your/path/macrocore/base");
|
||||
options insert=(sasautos="/your/path/macrocore/meta");
|
||||
%let repoloc=/your/path/core;
|
||||
options insert=(sasautos="&repoloc/base");
|
||||
options insert=(sasautos="&repoloc/ddl");
|
||||
options insert=(sasautos="&repoloc/fcmp");
|
||||
options insert=(sasautos="&repoloc/lua");
|
||||
options insert=(sasautos="&repoloc/meta");
|
||||
options insert=(sasautos="&repoloc/metax");
|
||||
options insert=(sasautos="&repoloc/server");
|
||||
options insert=(sasautos="&repoloc/viya");
|
||||
options insert=(sasautos="&repoloc/xplatform");
|
||||
```
|
||||
|
||||
The above can be done directly in your sas program, via an autoexec, or an initialisation program.
|
||||
@@ -142,7 +158,7 @@ filename mc url "https://raw.githubusercontent.com/sasjs/core/main/all.sas";
|
||||
- _mp_ for macro procedures (which generate sas code)
|
||||
- _ms_ for macro procedures that will only work with [@sasjs/server](https://github.com/sasjs/server)
|
||||
- _mv_ for macro procedures that will only work in Viya
|
||||
- _mx_ for macros that are XCMD enabled (working on both windows and unix)
|
||||
- _mx_ for macros that work on Viya, SAS 9 EBI and SASjs Server
|
||||
- follow verb-noun convention
|
||||
- unix style line endings (lf)
|
||||
- individual lines should be no more than 80 characters long
|
||||
|
||||
@@ -5,8 +5,12 @@
|
||||
|
||||
%put %mf_getplatform();
|
||||
|
||||
returns:
|
||||
SASMETA (or SASVIYA)
|
||||
returns one of:
|
||||
|
||||
@li SASMETA
|
||||
@li SASVIYA
|
||||
@li SASJS
|
||||
@li BASESAS
|
||||
|
||||
@param switch the param for which to return a platform specific variable
|
||||
|
||||
@@ -68,4 +72,4 @@
|
||||
%else %if &switch=VIYARESTAPI %then %do;
|
||||
%mf_trimstr(%sysfunc(getoption(servicesbaseurl)),/)
|
||||
%end;
|
||||
%mend mf_getplatform;
|
||||
%mend mf_getplatform;
|
||||
|
||||
@@ -33,4 +33,4 @@
|
||||
%if %sysfunc(findc(%str(&val),,kd)) %then %do;0%end;
|
||||
%else %do;1%end;
|
||||
|
||||
%mend mf_isint;
|
||||
%mend mf_isint;
|
||||
|
||||
@@ -1,50 +1,13 @@
|
||||
/**
|
||||
@file mp_createwebservice.sas
|
||||
@brief Create a web service in SAS 9 or Viya
|
||||
@details Creates a SASJS ready Stored Process in SAS 9 or Job Execution
|
||||
Service in SAS Viya
|
||||
@brief Create a web service in SAS 9, Viya or SASjs Server
|
||||
@details This is actually a wrapper for mx_createwebservice.sas, remaining
|
||||
for legacy purposes. For new apps, use mx_createwebservice.sas.
|
||||
|
||||
Usage:
|
||||
|
||||
%* compile macros ;
|
||||
filename mc url "https://raw.githubusercontent.com/sasjs/core/main/all.sas";
|
||||
%inc mc;
|
||||
|
||||
%* write some code;
|
||||
filename ft15f001 temp;
|
||||
parmcards4;
|
||||
%* fetch any data from frontend ;
|
||||
%webout(FETCH)
|
||||
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)
|
||||
;;;;
|
||||
%mp_createwebservice(path=/Public/app/common,name=appInit,replace=YES)
|
||||
|
||||
<h4> SAS Macros </h4>
|
||||
@li mf_getplatform.sas
|
||||
@li mm_createwebservice.sas
|
||||
@li ms_createwebservice.sas
|
||||
@li mv_createwebservice.sas
|
||||
@li mx_createwebservice.sas
|
||||
|
||||
@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
|
||||
@param [in] mDebug= (0) set to 1 to show debug messages in the log
|
||||
|
||||
@version 9.2
|
||||
@author Allan Bowe
|
||||
|
||||
**/
|
||||
|
||||
@@ -57,40 +20,13 @@ Usage:
|
||||
,mdebug=0
|
||||
)/*/STORE SOURCE*/;
|
||||
|
||||
%if &syscc ge 4 %then %do;
|
||||
%put syscc=&syscc - &sysmacroname will not execute in this state;
|
||||
%return;
|
||||
%end;
|
||||
|
||||
%local platform; %let platform=%mf_getplatform();
|
||||
%if &platform=SASVIYA %then %do;
|
||||
%if "&path"="HOME" %then %let path=/Users/&sysuserid/My Folder;
|
||||
%mv_createwebservice(path=&path
|
||||
%mx_createwebservice(path=&path
|
||||
,name=&name
|
||||
,code=&code
|
||||
,precode=&precode
|
||||
,code=&code
|
||||
,desc=&desc
|
||||
,replace=&replace
|
||||
)
|
||||
%end;
|
||||
%else %if &platform=SASJS %then %do;
|
||||
%if "&path"="HOME" %then %let path=/Users/&_sasjs_username/My Folder;
|
||||
%ms_createwebservice(path=&path
|
||||
,name=&name
|
||||
,code=&code
|
||||
,precode=&precode
|
||||
,mdebug=&mdebug
|
||||
)
|
||||
%end;
|
||||
%else %do;
|
||||
%if "&path"="HOME" %then %let path=/User Folders/&_METAPERSON/My Folder;
|
||||
%mm_createwebservice(path=&path
|
||||
,name=&name
|
||||
,code=&code
|
||||
,precode=&precode
|
||||
,desc=&desc
|
||||
,replace=&replace
|
||||
)
|
||||
%end;
|
||||
|
||||
%mend mp_createwebservice;
|
||||
|
||||
@@ -1,53 +1,11 @@
|
||||
/**
|
||||
@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.
|
||||
|
||||
|
||||
|
||||
Note - the _webout fileref should NOT be assigned prior to running this macro.
|
||||
|
||||
@param [in] program The _PROGRAM endpoint to test
|
||||
@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|
|
||||
|---|---|
|
||||
|stpmacname|some value|
|
||||
|mustbevalidname|can be anything, oops, %abort!!|
|
||||
|
||||
@param [in] debug= (log) Provide the _debug value
|
||||
@param [in] mdebug= (0) Set to 1 to provide macro debugging
|
||||
@param [in] viyaresult= (WEBOUT_JSON) The Viya result type to return. For
|
||||
more info, see mv_getjobresult.sas
|
||||
@param [in] viyacontext= (SAS Job Execution compute context) The Viya compute
|
||||
context on which to run the service
|
||||
@param [out] outlib= (0) Output libref to contain the final tables. Set to
|
||||
0 if the service output is not in JSON format.
|
||||
@param [out] outref= (0) Output fileref to create, to contain the full _webout
|
||||
response.
|
||||
@brief To be deprecated. Will execute a SASjs web service on SAS 9 or Viya
|
||||
@details Use the mx_testservice.sas macro instead (documentation can be
|
||||
found there)
|
||||
|
||||
<h4> SAS Macros </h4>
|
||||
@li mf_getplatform.sas
|
||||
@li mf_getuniquefileref.sas
|
||||
@li mf_getuniquename.sas
|
||||
@li mp_abort.sas
|
||||
@li mp_binarycopy.sas
|
||||
@li mp_chop.sas
|
||||
@li mp_ds2csv.sas
|
||||
@li ms_testservice.sas
|
||||
@li mv_getjobresult.sas
|
||||
@li mv_jobflow.sas
|
||||
|
||||
<h4> Related Programs </h4>
|
||||
@li mp_testservice.test.sas
|
||||
@li mx_testservice.sas
|
||||
|
||||
@version 9.4
|
||||
@author Allan Bowe
|
||||
@@ -65,237 +23,17 @@
|
||||
viyaresult=WEBOUT_JSON,
|
||||
viyacontext=SAS Job Execution compute context
|
||||
)/*/STORE SOURCE*/;
|
||||
%local dbg pcnt fref1 fref2 webref webrefpath i webcount var platform;
|
||||
%if &mdebug=1 %then %do;
|
||||
%put &sysmacroname entry vars:;
|
||||
%put _local_;
|
||||
%end;
|
||||
%else %let dbg=*;
|
||||
|
||||
/* sanitise inputparams */
|
||||
%let pcnt=0;
|
||||
%if &inputparams ne 0 %then %do;
|
||||
data _null_;
|
||||
set &inputparams;
|
||||
if not nvalid(name,'v7') then putlog (_all_)(=);
|
||||
else if name in (
|
||||
'program','inputfiles','inputparams','debug','outlib','outref'
|
||||
) then putlog (_all_)(=);
|
||||
else do;
|
||||
x+1;
|
||||
call symputx(name,quote(cats(value)),'l');
|
||||
call symputx(cats('pval',x),name,'l');
|
||||
call symputx('pcnt',x,'l');
|
||||
end;
|
||||
run;
|
||||
%mp_abort(iftrue= (%mf_nobs(&inputparams) ne &pcnt)
|
||||
,mac=&sysmacroname
|
||||
,msg=%str(Invalid values in &inputparams)
|
||||
)
|
||||
%end;
|
||||
%mx_testservice(&program,
|
||||
inputfiles=&inputfiles,
|
||||
inputdatasets=&inputdatasets,
|
||||
inputparams=&inputparams,
|
||||
debug=&debug,
|
||||
mdebug=&mdebug,
|
||||
outlib=&outlib,
|
||||
outref=&outref,
|
||||
viyaresult=&viyaresult,
|
||||
viyacontext=&viyacontext
|
||||
)
|
||||
|
||||
/* 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;
|
||||
|
||||
%let platform=%mf_getplatform();
|
||||
%let fref1=%mf_getuniquefileref();
|
||||
%let fref2=%mf_getuniquefileref();
|
||||
%let webref=%mf_getuniquefileref();
|
||||
%let webrefpath=%sysfunc(pathname(work))/%mf_getuniquename();
|
||||
/* mp_chop requires a physical path as input */
|
||||
filename &webref "&webrefpath";
|
||||
|
||||
%if &platform=SASMETA %then %do;
|
||||
|
||||
/* parse the input files */
|
||||
%if %quote(&inputfiles) ne 0 %then %do;
|
||||
%let webcount=%sysfunc(countw(&inputfiles));
|
||||
%put &=webcount;
|
||||
%do i=1 %to &webcount;
|
||||
%let var=%scan(&inputfiles,&i,%str( ));
|
||||
%local webfref&i webname&i;
|
||||
%let webref&i=%scan(&var,1,%str(:));
|
||||
%let webname&i=%scan(&var,2,%str(:));
|
||||
%put webref&i=&&webref&i;
|
||||
%put webname&i=&&webname&i;
|
||||
%end;
|
||||
%end;
|
||||
%else %let webcount=0;
|
||||
|
||||
proc stp program="&program";
|
||||
inputparam _program="&program"
|
||||
%do i=1 %to &webcount;
|
||||
%if &webcount=1 %then %do;
|
||||
_webin_fileref="&&webref&i"
|
||||
_webin_name="&&webname&i"
|
||||
%end;
|
||||
%else %do;
|
||||
_webin_fileref&i="&&webref&i"
|
||||
_webin_name&i="&&webname&i"
|
||||
%end;
|
||||
%end;
|
||||
_webin_file_count="&webcount"
|
||||
_debug="&debug"
|
||||
%do i=1 %to &pcnt;
|
||||
/* resolve name only, proc stp fetches value */
|
||||
&&pval&i=&&&&&&pval&i
|
||||
%end;
|
||||
;
|
||||
%do i=1 %to &webcount;
|
||||
inputfile &&webref&i;
|
||||
%end;
|
||||
outputfile _webout=&webref;
|
||||
run;
|
||||
|
||||
data _null_;
|
||||
infile &webref;
|
||||
file &fref1;
|
||||
input;
|
||||
length line $10000;
|
||||
if index(_infile_,'>>weboutBEGIN<<') then do;
|
||||
line=tranwrd(_infile_,'>>weboutBEGIN<<','');
|
||||
put line;
|
||||
end;
|
||||
else if index(_infile_,'>>weboutEND<<') then do;
|
||||
line=tranwrd(_infile_,'>>weboutEND<<','');
|
||||
put line;
|
||||
stop;
|
||||
end;
|
||||
else put _infile_;
|
||||
run;
|
||||
data _null_;
|
||||
infile &fref1;
|
||||
input;
|
||||
put _infile_;
|
||||
run;
|
||||
%if &outlib ne 0 %then %do;
|
||||
libname &outlib json (&fref1);
|
||||
%end;
|
||||
%if &outref ne 0 %then %do;
|
||||
filename &outref temp;
|
||||
%mp_binarycopy(inref=&webref,outref=&outref)
|
||||
%end;
|
||||
|
||||
%end;
|
||||
%else %if &platform=SASVIYA %then %do;
|
||||
|
||||
/* prepare inputparams */
|
||||
%local ds1;
|
||||
%let ds1=%mf_getuniquename();
|
||||
%if "&inputparams" ne "0" %then %do;
|
||||
proc transpose data=&inputparams out=&ds1;
|
||||
id name;
|
||||
var value;
|
||||
run;
|
||||
%end;
|
||||
%else %do;
|
||||
data &ds1;run;
|
||||
%end;
|
||||
|
||||
/* parse the input files - convert to sasjs params */
|
||||
%local webcount i var sasjs_tables;
|
||||
%if %quote(&inputfiles) ne 0 %then %do;
|
||||
%let webcount=%sysfunc(countw(&inputfiles));
|
||||
%put &=webcount;
|
||||
%do i=1 %to &webcount;
|
||||
%let var=%scan(&inputfiles,&i,%str( ));
|
||||
%local webfref&i webname&i sasjs&i.data;
|
||||
%let webref&i=%scan(&var,1,%str(:));
|
||||
%let webname&i=%scan(&var,2,%str(:));
|
||||
%put webref&i=&&webref&i;
|
||||
%put webname&i=&&webname&i;
|
||||
|
||||
%let sasjs_tables=&sasjs_tables &&webname&i;
|
||||
data _null_;
|
||||
infile &&webref&i lrecl=32767;
|
||||
input;
|
||||
if _n_=1 then call symputx("sasjs&i.data",_infile_);
|
||||
else call symputx(
|
||||
"sasjs&i.data",cats(symget("sasjs&i.data"),'0D0A'x,_infile_)
|
||||
);
|
||||
putlog "&sysmacroname infile: " _infile_;
|
||||
run;
|
||||
data &ds1;
|
||||
set &ds1;
|
||||
length sasjs&i.data $32767 sasjs_tables $1000;
|
||||
sasjs&i.data=symget("sasjs&i.data");
|
||||
sasjs_tables=symget("sasjs_tables");
|
||||
run;
|
||||
%end;
|
||||
%end;
|
||||
%else %let webcount=0;
|
||||
|
||||
data &ds1;
|
||||
retain _program "&program";
|
||||
retain _contextname "&viyacontext";
|
||||
set &ds1;
|
||||
putlog "&sysmacroname inputparams:";
|
||||
putlog (_all_)(=);
|
||||
run;
|
||||
|
||||
%mv_jobflow(inds=&ds1
|
||||
,maxconcurrency=1
|
||||
,outds=work.results
|
||||
,outref=&fref1
|
||||
,mdebug=&mdebug
|
||||
)
|
||||
/* show the log */
|
||||
data _null_;
|
||||
infile &fref1;
|
||||
input;
|
||||
putlog _infile_;
|
||||
run;
|
||||
/* get the uri to fetch results */
|
||||
data _null_;
|
||||
set work.results;
|
||||
call symputx('uri',uri);
|
||||
putlog "&sysmacroname: fetching results for " uri;
|
||||
run;
|
||||
/* fetch results from webout.json */
|
||||
%mv_getjobresult(uri=&uri,
|
||||
result=&viyaresult,
|
||||
outref=&outref,
|
||||
outlib=&outlib,
|
||||
mdebug=&mdebug
|
||||
)
|
||||
|
||||
%end;
|
||||
%else %if &platform=SASJS %then %do;
|
||||
|
||||
%ms_testservice(&program
|
||||
,inputfiles=&inputfiles
|
||||
,inputdatasets=&inputdatasets
|
||||
,inputparams=&inputparams
|
||||
,debug=&debug
|
||||
,mdebug=&mdebug
|
||||
,outlib=&outlib
|
||||
,outref=&outref
|
||||
)
|
||||
|
||||
%end;
|
||||
%else %do;
|
||||
%put %str(ERR)OR: Unrecognised platform: &platform;
|
||||
%end;
|
||||
|
||||
%if &mdebug=0 %then %do;
|
||||
filename &fref1 clear;
|
||||
%if &platform ne SASJS %then %do;
|
||||
filename &fref2 clear;
|
||||
filename &webref clear;
|
||||
%end;
|
||||
%end;
|
||||
%else %do;
|
||||
%put &sysmacroname exit vars:;
|
||||
%put _local_;
|
||||
%end;
|
||||
|
||||
%mend mp_testservice;
|
||||
%mend mp_testservice;
|
||||
|
||||
2
build.py
2
build.py
@@ -102,7 +102,7 @@ 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', 'xplatform']
|
||||
for folder in folders:
|
||||
filenames = [fn for fn in Path(
|
||||
'./' + folder).iterdir() if fn.match("*.sas")]
|
||||
|
||||
13
main.dox
13
main.dox
@@ -103,4 +103,15 @@
|
||||
* No X command
|
||||
* Prefixes: _mddl_
|
||||
|
||||
*/
|
||||
*/
|
||||
|
||||
/*! \dir xplatform
|
||||
* \brief Cross Platform, works on all SAS servers (Viya, EBI, SASjs)
|
||||
* \details Useful when you need to run a single piece of code against Viya,
|
||||
SAS 9 with metadata, or SASjs on Base SAS.
|
||||
|
||||
* OS independent
|
||||
* No X command
|
||||
* Prefixes: _mx_
|
||||
|
||||
*/
|
||||
|
||||
38
tests/xplatform/mx_getcode.test.sas
Normal file
38
tests/xplatform/mx_getcode.test.sas
Normal file
@@ -0,0 +1,38 @@
|
||||
/**
|
||||
@file
|
||||
@brief Testing mx_getcode.test.sas macro
|
||||
|
||||
Be sure to run <code>%let mcTestAppLoc=/Public/temp/macrocore;</code> when
|
||||
running in Studio
|
||||
|
||||
<h4> SAS Macros </h4>
|
||||
@li mf_uid.sas
|
||||
@li mp_assert.sas
|
||||
@li mx_createwebservice.sas
|
||||
@li mx_getcode.sas
|
||||
@li mx_testservice.sas
|
||||
|
||||
**/
|
||||
|
||||
/* first create a service */
|
||||
|
||||
%let item=%mf_uid();;
|
||||
|
||||
%global test1;
|
||||
%let test1=FAIL;
|
||||
|
||||
filename ft15f001 temp;
|
||||
parmcards4;
|
||||
%let test1=SUCCESS;
|
||||
;;;;
|
||||
%mx_createwebservice(path=&mcTestAppLoc/temp,name=&item)
|
||||
|
||||
%mx_getcode(&mcTestAppLoc/temp/&item,testref1)
|
||||
|
||||
%inc testref1/lrecl=1000;
|
||||
|
||||
%mp_assert(
|
||||
iftrue=(&test1=SUCCESS),
|
||||
desc=code was successfully fetched,
|
||||
outds=work.test_results
|
||||
)
|
||||
@@ -1,13 +1,13 @@
|
||||
/**
|
||||
@file
|
||||
@brief Testing mp_testservice.sas macro
|
||||
@brief Testing mx_testservice.sas macro
|
||||
|
||||
Be sure to run <code>%let mcTestAppLoc=/Public/temp/macrocore;</code> when
|
||||
runnin in Studio
|
||||
|
||||
<h4> SAS Macros </h4>
|
||||
@li mp_createwebservice.sas
|
||||
@li mp_testservice.sas
|
||||
@li mx_testservice.sas
|
||||
@li mp_assert.sas
|
||||
|
||||
**/
|
||||
@@ -29,7 +29,7 @@ parmcards4;
|
||||
%mend x; %x()
|
||||
%webout(CLOSE)
|
||||
;;;;
|
||||
%mp_createwebservice(path=&mcTestAppLoc/services,name=sendObj)
|
||||
%mx_createwebservice(path=&mcTestAppLoc/services,name=sendObj)
|
||||
|
||||
%mp_assert(
|
||||
iftrue=(&syscc=0),
|
||||
@@ -48,7 +48,7 @@ data work.somedata1 work.somedata2;
|
||||
output;
|
||||
run;
|
||||
|
||||
%mp_testservice(&mcTestAppLoc/services/sendObj,
|
||||
%mx_testservice(&mcTestAppLoc/services/sendObj,
|
||||
inputdatasets=work.somedata1 work.somedata2,
|
||||
debug=log,
|
||||
mdebug=1,
|
||||
96
xplatform/mx_createwebservice.sas
Normal file
96
xplatform/mx_createwebservice.sas
Normal file
@@ -0,0 +1,96 @@
|
||||
/**
|
||||
@file mx_createwebservice.sas
|
||||
@brief Create a web service in SAS 9, Viya or SASjs
|
||||
@details Creates a SASJS ready Stored Process in SAS 9, a Job Execution
|
||||
Service in SAS Viya, and a Stored Program on SASjs Server
|
||||
|
||||
Usage:
|
||||
|
||||
%* compile macros ;
|
||||
filename mc url "https://raw.githubusercontent.com/sasjs/core/main/all.sas";
|
||||
%inc mc;
|
||||
|
||||
%* write some code;
|
||||
filename ft15f001 temp;
|
||||
parmcards4;
|
||||
%* fetch any data from frontend ;
|
||||
%webout(FETCH)
|
||||
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)
|
||||
;;;;
|
||||
%mp_createwebservice(path=/Public/app/common,name=appInit,replace=YES)
|
||||
|
||||
<h4> SAS Macros </h4>
|
||||
@li mf_getplatform.sas
|
||||
@li mm_createwebservice.sas
|
||||
@li ms_createwebservice.sas
|
||||
@li mv_createwebservice.sas
|
||||
|
||||
@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
|
||||
@param [in] mDebug= (0) set to 1 to show debug messages in the log
|
||||
|
||||
@version 9.2
|
||||
@author Allan Bowe
|
||||
|
||||
**/
|
||||
|
||||
%macro mx_createwebservice(path=HOME
|
||||
,name=initService
|
||||
,precode=
|
||||
,code=ft15f001
|
||||
,desc=This service was created by the mp_createwebservice macro
|
||||
,replace=YES
|
||||
,mdebug=0
|
||||
)/*/STORE SOURCE*/;
|
||||
|
||||
%if &syscc ge 4 %then %do;
|
||||
%put syscc=&syscc - &sysmacroname will not execute in this state;
|
||||
%return;
|
||||
%end;
|
||||
|
||||
%local platform; %let platform=%mf_getplatform();
|
||||
%if &platform=SASVIYA %then %do;
|
||||
%if "&path"="HOME" %then %let path=/Users/&sysuserid/My Folder;
|
||||
%mv_createwebservice(path=&path
|
||||
,name=&name
|
||||
,code=&code
|
||||
,precode=&precode
|
||||
,desc=&desc
|
||||
,replace=&replace
|
||||
)
|
||||
%end;
|
||||
%else %if &platform=SASJS %then %do;
|
||||
%if "&path"="HOME" %then %let path=/Users/&_sasjs_username/My Folder;
|
||||
%ms_createwebservice(path=&path
|
||||
,name=&name
|
||||
,code=&code
|
||||
,precode=&precode
|
||||
,mdebug=&mdebug
|
||||
)
|
||||
%end;
|
||||
%else %do;
|
||||
%if "&path"="HOME" %then %let path=/User Folders/&_METAPERSON/My Folder;
|
||||
%mm_createwebservice(path=&path
|
||||
,name=&name
|
||||
,code=&code
|
||||
,precode=&precode
|
||||
,desc=&desc
|
||||
,replace=&replace
|
||||
)
|
||||
%end;
|
||||
|
||||
%mend mx_createwebservice;
|
||||
57
xplatform/mx_getcode.sas
Normal file
57
xplatform/mx_getcode.sas
Normal file
@@ -0,0 +1,57 @@
|
||||
/**
|
||||
@file
|
||||
@brief Fetches code from Viya Job, SAS 9 STP, or SASjs Server STP
|
||||
@details When building applications that run on multiple flavours of SAS, it
|
||||
is convenient to use a single macro (like this one) to fetch the source
|
||||
code from a Viya Job, SAS 9 Stored Process, or SASjs Stored Program.
|
||||
|
||||
The alternative would be to compile a generic macro in target-specific
|
||||
folders (SASVIYA, SAS9 and SASJS). This avoids compiling unnecessary macros
|
||||
at the expense of a more complex sasjsconfig.json setup.
|
||||
|
||||
|
||||
@param [in] loc The full path to the Viya Job, SAS 9 Stored Process or SASjs
|
||||
Stored Program in Drive or Metadata, WITHOUT the .sas extension (SASjs only)
|
||||
@param [out] outref= (0) The fileref to create, which will contain the source
|
||||
code.
|
||||
|
||||
<h4> SAS Macros </h4>
|
||||
@li mf_getplatform.sas
|
||||
@li mf_getuniquename.sas
|
||||
@li mm_getstpcode.sas
|
||||
@li ms_getfile.sas
|
||||
|
||||
@author Allan Bowe
|
||||
|
||||
**/
|
||||
|
||||
%macro mx_getcode(loc,outref=0
|
||||
)/*/STORE SOURCE*/;
|
||||
|
||||
%local platform name shortloc;
|
||||
%let platform=%mf_getplatform();
|
||||
|
||||
%if &platform=SASJS %then %do;
|
||||
%ms_getfile(&loc..sas, outref=&outref)
|
||||
%end;
|
||||
%else %if &platform=SAS9 %then %do;
|
||||
%mm_getstpcode(tree=&loc,outloc=&outref)
|
||||
%end;
|
||||
%else %if &platform=SASVIYA %then %do;
|
||||
/* extract name & path from &loc */
|
||||
data _null_;
|
||||
loc=symget('loc');
|
||||
name=scan(loc,-1,'/');
|
||||
shortloc=substr(loc,1,length(loc)-length(name)-1);
|
||||
call symputx('name',name,'l');
|
||||
call symputx('shortloc',shortloc,'l');
|
||||
run;
|
||||
%mv_getjobcode(
|
||||
path=&shortloc,
|
||||
name=&name,
|
||||
outref=&outref
|
||||
)
|
||||
%end;
|
||||
%else %put &sysmacroname: &platform is unsupported!!!;
|
||||
|
||||
%mend mx_getcode;
|
||||
299
xplatform/mx_testservice.sas
Normal file
299
xplatform/mx_testservice.sas
Normal file
@@ -0,0 +1,299 @@
|
||||
/**
|
||||
@file
|
||||
@brief Will execute a SASjs web service on SAS 9, Viya or SASjs Server
|
||||
@details Prepares the input files and retrieves the resulting datasets from
|
||||
the response JSON.
|
||||
|
||||
Note - the _webout fileref should NOT be assigned prior to running this macro.
|
||||
|
||||
@param [in] program The _PROGRAM endpoint to test
|
||||
@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|
|
||||
|---|---|
|
||||
|stpmacname|some value|
|
||||
|mustbevalidname|can be anything, oops, %abort!!|
|
||||
|
||||
@param [in] debug= (log) Provide the _debug value
|
||||
@param [in] mdebug= (0) Set to 1 to provide macro debugging
|
||||
@param [in] viyaresult= (WEBOUT_JSON) The Viya result type to return. For
|
||||
more info, see mv_getjobresult.sas
|
||||
@param [in] viyacontext= (SAS Job Execution compute context) The Viya compute
|
||||
context on which to run the service
|
||||
@param [out] outlib= (0) Output libref to contain the final tables. Set to
|
||||
0 if the service output is not in JSON format.
|
||||
@param [out] outref= (0) Output fileref to create, to contain the full _webout
|
||||
response.
|
||||
|
||||
<h4> SAS Macros </h4>
|
||||
@li mf_getplatform.sas
|
||||
@li mf_getuniquefileref.sas
|
||||
@li mf_getuniquename.sas
|
||||
@li mp_abort.sas
|
||||
@li mp_binarycopy.sas
|
||||
@li mp_chop.sas
|
||||
@li mp_ds2csv.sas
|
||||
@li ms_testservice.sas
|
||||
@li mv_getjobresult.sas
|
||||
@li mv_jobflow.sas
|
||||
|
||||
<h4> Related Programs </h4>
|
||||
@li mx_testservice.test.sas
|
||||
|
||||
@version 9.4
|
||||
@author Allan Bowe
|
||||
|
||||
**/
|
||||
|
||||
%macro mx_testservice(program,
|
||||
inputfiles=0,
|
||||
inputdatasets=0,
|
||||
inputparams=0,
|
||||
debug=log,
|
||||
mdebug=0,
|
||||
outlib=0,
|
||||
outref=0,
|
||||
viyaresult=WEBOUT_JSON,
|
||||
viyacontext=SAS Job Execution compute context
|
||||
);
|
||||
%local dbg pcnt fref1 fref2 webref webrefpath i webcount var platform;
|
||||
%if &mdebug=1 %then %do;
|
||||
%put &sysmacroname entry vars:;
|
||||
%put _local_;
|
||||
%end;
|
||||
%else %let dbg=*;
|
||||
|
||||
/* sanitise inputparams */
|
||||
%let pcnt=0;
|
||||
%if &inputparams ne 0 %then %do;
|
||||
data _null_;
|
||||
set &inputparams;
|
||||
if not nvalid(name,'v7') then putlog (_all_)(=);
|
||||
else if name in (
|
||||
'program','inputfiles','inputparams','debug','outlib','outref'
|
||||
) then putlog (_all_)(=);
|
||||
else do;
|
||||
x+1;
|
||||
call symputx(name,quote(cats(value)),'l');
|
||||
call symputx(cats('pval',x),name,'l');
|
||||
call symputx('pcnt',x,'l');
|
||||
end;
|
||||
run;
|
||||
%mp_abort(iftrue= (%mf_nobs(&inputparams) ne &pcnt)
|
||||
,mac=&sysmacroname
|
||||
,msg=%str(Invalid values in &inputparams)
|
||||
)
|
||||
%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;
|
||||
|
||||
%let platform=%mf_getplatform();
|
||||
%let fref1=%mf_getuniquefileref();
|
||||
%let fref2=%mf_getuniquefileref();
|
||||
%let webref=%mf_getuniquefileref();
|
||||
%let webrefpath=%sysfunc(pathname(work))/%mf_getuniquename();
|
||||
/* mp_chop requires a physical path as input */
|
||||
filename &webref "&webrefpath";
|
||||
|
||||
%if &platform=SASMETA %then %do;
|
||||
|
||||
/* parse the input files */
|
||||
%if %quote(&inputfiles) ne 0 %then %do;
|
||||
%let webcount=%sysfunc(countw(&inputfiles));
|
||||
%put &=webcount;
|
||||
%do i=1 %to &webcount;
|
||||
%let var=%scan(&inputfiles,&i,%str( ));
|
||||
%local webfref&i webname&i;
|
||||
%let webref&i=%scan(&var,1,%str(:));
|
||||
%let webname&i=%scan(&var,2,%str(:));
|
||||
%put webref&i=&&webref&i;
|
||||
%put webname&i=&&webname&i;
|
||||
%end;
|
||||
%end;
|
||||
%else %let webcount=0;
|
||||
|
||||
proc stp program="&program";
|
||||
inputparam _program="&program"
|
||||
%do i=1 %to &webcount;
|
||||
%if &webcount=1 %then %do;
|
||||
_webin_fileref="&&webref&i"
|
||||
_webin_name="&&webname&i"
|
||||
%end;
|
||||
%else %do;
|
||||
_webin_fileref&i="&&webref&i"
|
||||
_webin_name&i="&&webname&i"
|
||||
%end;
|
||||
%end;
|
||||
_webin_file_count="&webcount"
|
||||
_debug="&debug"
|
||||
%do i=1 %to &pcnt;
|
||||
/* resolve name only, proc stp fetches value */
|
||||
&&pval&i=&&&&&&pval&i
|
||||
%end;
|
||||
;
|
||||
%do i=1 %to &webcount;
|
||||
inputfile &&webref&i;
|
||||
%end;
|
||||
outputfile _webout=&webref;
|
||||
run;
|
||||
|
||||
data _null_;
|
||||
infile &webref;
|
||||
file &fref1;
|
||||
input;
|
||||
length line $10000;
|
||||
if index(_infile_,'>>weboutBEGIN<<') then do;
|
||||
line=tranwrd(_infile_,'>>weboutBEGIN<<','');
|
||||
put line;
|
||||
end;
|
||||
else if index(_infile_,'>>weboutEND<<') then do;
|
||||
line=tranwrd(_infile_,'>>weboutEND<<','');
|
||||
put line;
|
||||
stop;
|
||||
end;
|
||||
else put _infile_;
|
||||
run;
|
||||
data _null_;
|
||||
infile &fref1;
|
||||
input;
|
||||
put _infile_;
|
||||
run;
|
||||
%if &outlib ne 0 %then %do;
|
||||
libname &outlib json (&fref1);
|
||||
%end;
|
||||
%if &outref ne 0 %then %do;
|
||||
filename &outref temp;
|
||||
%mp_binarycopy(inref=&webref,outref=&outref)
|
||||
%end;
|
||||
|
||||
%end;
|
||||
%else %if &platform=SASVIYA %then %do;
|
||||
|
||||
/* prepare inputparams */
|
||||
%local ds1;
|
||||
%let ds1=%mf_getuniquename();
|
||||
%if "&inputparams" ne "0" %then %do;
|
||||
proc transpose data=&inputparams out=&ds1;
|
||||
id name;
|
||||
var value;
|
||||
run;
|
||||
%end;
|
||||
%else %do;
|
||||
data &ds1;run;
|
||||
%end;
|
||||
|
||||
/* parse the input files - convert to sasjs params */
|
||||
%local webcount i var sasjs_tables;
|
||||
%if %quote(&inputfiles) ne 0 %then %do;
|
||||
%let webcount=%sysfunc(countw(&inputfiles));
|
||||
%put &=webcount;
|
||||
%do i=1 %to &webcount;
|
||||
%let var=%scan(&inputfiles,&i,%str( ));
|
||||
%local webfref&i webname&i sasjs&i.data;
|
||||
%let webref&i=%scan(&var,1,%str(:));
|
||||
%let webname&i=%scan(&var,2,%str(:));
|
||||
%put webref&i=&&webref&i;
|
||||
%put webname&i=&&webname&i;
|
||||
|
||||
%let sasjs_tables=&sasjs_tables &&webname&i;
|
||||
data _null_;
|
||||
infile &&webref&i lrecl=32767;
|
||||
input;
|
||||
if _n_=1 then call symputx("sasjs&i.data",_infile_);
|
||||
else call symputx(
|
||||
"sasjs&i.data",cats(symget("sasjs&i.data"),'0D0A'x,_infile_)
|
||||
);
|
||||
putlog "&sysmacroname infile: " _infile_;
|
||||
run;
|
||||
data &ds1;
|
||||
set &ds1;
|
||||
length sasjs&i.data $32767 sasjs_tables $1000;
|
||||
sasjs&i.data=symget("sasjs&i.data");
|
||||
sasjs_tables=symget("sasjs_tables");
|
||||
run;
|
||||
%end;
|
||||
%end;
|
||||
%else %let webcount=0;
|
||||
|
||||
data &ds1;
|
||||
retain _program "&program";
|
||||
retain _contextname "&viyacontext";
|
||||
set &ds1;
|
||||
putlog "&sysmacroname inputparams:";
|
||||
putlog (_all_)(=);
|
||||
run;
|
||||
|
||||
%mv_jobflow(inds=&ds1
|
||||
,maxconcurrency=1
|
||||
,outds=work.results
|
||||
,outref=&fref1
|
||||
,mdebug=&mdebug
|
||||
)
|
||||
/* show the log */
|
||||
data _null_;
|
||||
infile &fref1;
|
||||
input;
|
||||
putlog _infile_;
|
||||
run;
|
||||
/* get the uri to fetch results */
|
||||
data _null_;
|
||||
set work.results;
|
||||
call symputx('uri',uri);
|
||||
putlog "&sysmacroname: fetching results for " uri;
|
||||
run;
|
||||
/* fetch results from webout.json */
|
||||
%mv_getjobresult(uri=&uri,
|
||||
result=&viyaresult,
|
||||
outref=&outref,
|
||||
outlib=&outlib,
|
||||
mdebug=&mdebug
|
||||
)
|
||||
|
||||
%end;
|
||||
%else %if &platform=SASJS %then %do;
|
||||
|
||||
%ms_testservice(&program
|
||||
,inputfiles=&inputfiles
|
||||
,inputdatasets=&inputdatasets
|
||||
,inputparams=&inputparams
|
||||
,debug=&debug
|
||||
,mdebug=&mdebug
|
||||
,outlib=&outlib
|
||||
,outref=&outref
|
||||
)
|
||||
|
||||
%end;
|
||||
%else %do;
|
||||
%put %str(ERR)OR: Unrecognised platform: &platform;
|
||||
%end;
|
||||
|
||||
%if &mdebug=0 %then %do;
|
||||
filename &fref1 clear;
|
||||
%if &platform ne SASJS %then %do;
|
||||
filename &fref2 clear;
|
||||
filename &webref clear;
|
||||
%end;
|
||||
%end;
|
||||
%else %do;
|
||||
%put &sysmacroname exit vars:;
|
||||
%put _local_;
|
||||
%end;
|
||||
|
||||
%mend mx_testservice;
|
||||
Reference in New Issue
Block a user