diff --git a/all.sas b/all.sas
index 036b596..dcc57a6 100644
--- a/all.sas
+++ b/all.sas
@@ -7573,6 +7573,11 @@ create table &outds as
outfile=0
)/*/STORE SOURCE*/;
+ %if "%substr(&sysver,1,4)"="V.04" %then %do;
+ %put %str(ERR)OR: Viya 4 does not support the IO library in lua;
+ %return;
+ %end;
+
%ml_gsubfile()
%mend mp_gsubfile;
@@ -21001,10 +21006,16 @@ libname &libref1a clear;
%mv_deleteviyafolder(path=/Public/test)
- @param path= The full path of the folder to be deleted
- @param access_token_var= The global macro variable to contain the access token
- @param grant_type= valid values are "password" or "authorization_code" (unquoted).
- The default is authorization_code.
+ @param [in] path= The full path of the folder to be deleted
+ @param [in] access_token_var= (ACCESS_TOKEN) The global macro variable to
+ contain the access token
+ @param [in] grant_type= (sas_services) Valid values are:
+ @li password
+ @li authorization_code
+ @li detect - will check if access_token exists, if not will use sas_services
+ if a SASStudioV session else authorization_code. Default option.
+ @li sas_services - will use oauth_bearer=sas_services.
+ @param [in] mdebug= (0) Set to 1 to enable DEBUG messages
@version VIYA V.03.04
@@ -21022,6 +21033,7 @@ libname &libref1a clear;
%macro mv_deleteviyafolder(path=
,access_token_var=ACCESS_TOKEN
,grant_type=sas_services
+ ,mdebug=0
);
%local oauth_bearer;
%if &grant_type=detect %then %do;
@@ -21098,14 +21110,17 @@ run;
%let libref1a=%mf_getuniquelibref();
libname &libref1a JSON fileref=&fname1a;
-data _null_;
- set &libref1a..items_links;
- if href=:'/folders/folders' then return;
- if rel='deleteResource' then
- call execute('proc http method="DELETE" url='!!quote("&base_uri"!!trim(href))
- !!'; headers "Authorization"="Bearer &&&access_token_var" '
- !!' "Accept"="*/*";run; /**/');
-run;
+%if %mf_existds(&libref1a..items_links) %then %do;
+ data _null_;
+ set &libref1a..items_links;
+ if href=:'/folders/folders' then return;
+ if rel='deleteResource' then
+ call execute('proc http method="DELETE" url='
+ !!quote("&base_uri"!!trim(href))
+ !!'; headers "Authorization"="Bearer &&&access_token_var" '
+ !!' "Accept"="*/*";run; /**/');
+ run;
+%end;
%put &sysmacroname: perform the delete operation ;
%local fname2;
@@ -21126,12 +21141,15 @@ run;
%end;
%else %put &sysmacroname: &path successfully deleted;
-/* clear refs */
-filename &fname1 clear;
-filename &fname2 clear;
-libname &libref1 clear;
+%if &mdebug=0 %then %do;
+ /* clear refs */
+ filename &fname1 clear;
+ filename &fname2 clear;
+ libname &libref1 clear;
+%end;
-%mend mv_deleteviyafolder;/**
+%mend mv_deleteviyafolder;
+/**
@file mv_getclients.sas
@brief Get a list of Viya Clients
@details First, be sure you have an access token (which requires an app token).
@@ -21595,7 +21613,6 @@ libname &libref1 clear;
@li mf_getplatform.sas
@li mf_getuniquefileref.sas
@li mv_getfoldermembers.sas
- @li ml_json.sas
**/
@@ -21606,7 +21623,7 @@ libname &libref1 clear;
,grant_type=sas_services
,mdebug=0
);
-%local dbg;
+%local dbg bufsize varcnt fname1 fname2 errmsg;
%if &mdebug=1 %then %do;
%put &sysmacroname entry vars:;
%put _local_;
@@ -21666,7 +21683,6 @@ run;
)
/* prepare request*/
-%local fname1;
%let fname1=%mf_getuniquefileref();
proc http method='GET' out=&fname1 &oauth_bearer
url="&base_uri&joburi";
@@ -21683,30 +21699,81 @@ run;
,msg=%str(&SYS_PROCHTTP_STATUS_CODE &SYS_PROCHTTP_STATUS_PHRASE)
)
%end;
-%local fname2 fname3 fpath1 fpath2 fpath3;
-%let fname2=%mf_getuniquefileref();
-%let fname3=%mf_getuniquefileref();
-%let fpath1=%sysfunc(pathname(&fname1));
-%let fpath2=%sysfunc(pathname(&fname2));
-%let fpath3=%sysfunc(pathname(&fname3));
-/* compile the lua JSON module */
-%ml_json()
-/* read using LUA - this allows the code to be of any length */
+%let fname2=%mf_getuniquefileref();
+filename &fname2 temp ;
+
+/* cannot use lua IO package as not available in Viya 4 */
+/* so use data step to read the JSON until the string `"code":"` is found */
data _null_;
- file "&fpath3..lua";
- put '
- infile = io.open (sas.symget("fpath1"), "r")
- outfile = io.open (sas.symget("fpath2"), "w")
- io.input(infile)
- local resp=json.decode(io.read())
- local job=resp["code"]
- outfile:write(job)
- io.close(infile)
- io.close(outfile)
- ';
+ file &fname2 recfm=n;
+ infile &fname1 lrecl=1 recfm=n;
+ input sourcechar $ 1. @@;
+ format sourcechar hex2.;
+ retain startwrite 0;
+ if startwrite=0 and sourcechar='"' then do;
+ reentry:
+ input sourcechar $ 1. @@;
+ if sourcechar='c' then do;
+ reentry2:
+ input sourcechar $ 1. @@;
+ if sourcechar='o' then do;
+ input sourcechar $ 1. @@;
+ if sourcechar='d' then do;
+ input sourcechar $ 1. @@;
+ if sourcechar='e' then do;
+ input sourcechar $ 1. @@;
+ if sourcechar='"' then do;
+ input sourcechar $ 1. @@;
+ if sourcechar=':' then do;
+ input sourcechar $ 1. @@;
+ if sourcechar='"' then do;
+ putlog 'code found';
+ startwrite=1;
+ input sourcechar $ 1. @@;
+ end;
+ end;
+ else if sourcechar='c' then goto reentry2;
+ end;
+ end;
+ else if sourcechar='"' then goto reentry;
+ end;
+ else if sourcechar='"' then goto reentry;
+ end;
+ else if sourcechar='"' then goto reentry;
+ end;
+ else if sourcechar='"' then goto reentry;
+ end;
+ /* once the `"code":"` string is found, write until unescaped `"` is found */
+ if startwrite=1 then do;
+ if sourcechar='\' then do;
+ input sourcechar $ 1. @@;
+ if sourcechar in ('"','\') then put sourcechar char1.;
+ else if sourcechar='n' then put '0A'x;
+ else if sourcechar='r' then put '0D'x;
+ else if sourcechar='t' then put '09'x;
+ else if sourcechar='u' then do;
+ length uni $4;
+ input uni $ 4. @@;
+ sourcechar=unicode('\u'!!uni);
+ put sourcechar char1.;
+ end;
+ else do;
+ call symputx('errmsg',"Uncaught escape char: "!!sourcechar,'l');
+ call symputx('syscc',99);
+ stop;
+ end;
+ end;
+ else if sourcechar='"' then stop;
+ else put sourcechar char1.;
+ end;
run;
-%inc "&fpath3..lua";
+
+%mp_abort(iftrue=("&syscc"="99")
+ ,mac=mv_getjobcode
+ ,msg=%str(&errmsg)
+)
+
/* export to desired destination */
%if "&outref"="0" %then %do;
data _null_;
@@ -21731,7 +21798,6 @@ run;
/* clear refs */
filename &fname1 clear;
filename &fname2 clear;
- filename &fname3 clear;
%end;
%mend mv_getjobcode;
@@ -21823,7 +21889,8 @@ run;
@li mp_abort.sas
@li mf_getplatform.sas
@li mf_existfileref.sas
- @li ml_json.sas
+ @li mf_getuniquefileref.sas
+ @li mf_getuniquelibref.sas
**/
@@ -21832,7 +21899,7 @@ run;
,grant_type=sas_services
,mdebug=0
);
-%local dbg;
+%local dbg libref1 libref2 loglocation fname1 fname2;
%if &mdebug=1 %then %do;
%put &sysmacroname entry vars:;
%put _local_;
@@ -21891,8 +21958,8 @@ options noquotelenmax;
%let base_uri=%mf_getplatform(VIYARESTAPI);
/* prepare request*/
-%local fname1;
%let fname1=%mf_getuniquefileref();
+%let fname2=%mf_getuniquefileref();
proc http method='GET' out=&fname1 &oauth_bearer
url="&base_uri&uri";
headers
@@ -21912,37 +21979,19 @@ run;
,msg=%str(&SYS_PROCHTTP_STATUS_CODE &SYS_PROCHTTP_STATUS_PHRASE)
)
%end;
-%local fname2 fname3 fpath1 fpath2 fpath3;
-%let fname2=%mf_getuniquefileref();
-%let fname3=%mf_getuniquefileref();
-%let fpath1=%sysfunc(pathname(&fname1));
-%let fpath2=%sysfunc(pathname(&fname2));
-%let fpath3=%sysfunc(pathname(&fname3));
-/* compile the lua JSON module */
-%ml_json()
-/* read using LUA - this allows the code to be of any length */
+%let libref1=%mf_getuniquelibref();
+libname &libref1 JSON fileref=&fname1;
data _null_;
- file "&fpath3..lua";
- put '
- infile = io.open (sas.symget("fpath1"), "r")
- outfile = io.open (sas.symget("fpath2"), "w")
- io.input(infile)
- local resp=json.decode(io.read())
- local logloc=resp["logLocation"]
- outfile:write(logloc)
- io.close(infile)
- io.close(outfile)
- ';
+ set &libref1..root;
+ call symputx('loglocation',loglocation,'l');
run;
-%inc "&fpath3..lua";
-/* get log path*/
+
+/* validate log path*/
%let errflg=1;
-%let errmsg=No entry in &fname2 fileref;
+%let errmsg=No loglocation entry in &fname1 fileref;
data _null_;
- infile &fname2;
- input;
- uri=cats(_infile_);
+ uri=symget('loglocation');
if length(uri)<12 then do;
call symputx('errflg',1);
call symputx('errmsg',"URI is invalid (too short) - '&uri'",'l');
@@ -21969,7 +22018,7 @@ run;
/* we have a log uri - now fetch the log */
%&dbg.put &sysmacroname: querying &base_uri&logloc/content;
-proc http method='GET' out=&fname1 &oauth_bearer
+proc http method='GET' out=&fname2 &oauth_bearer
url="&base_uri&logloc/content?limit=10000";
headers
%if &grant_type=authorization_code %then %do;
@@ -21980,14 +22029,14 @@ run;
%if &mdebug=1 %then %do;
%put &sysmacroname: fetching log content from &base_uri&logloc/content;
- data _null_;infile &fname1;input;putlog _infile_;run;
+ data _null_;infile &fname2;input;putlog _infile_;run;
%end;
%if &SYS_PROCHTTP_STATUS_CODE=400 %then %do;
/* fetch log from parent session */
%let logloc=%substr(&logloc,1,%index(&logloc,%str(/jobs/))-1);
%&dbg.put &sysmacroname: Now querying &base_uri&logloc/log/content;
- proc http method='GET' out=&fname1 &oauth_bearer
+ proc http method='GET' out=&fname2 &oauth_bearer
url="&base_uri&logloc/log/content?limit=10000";
headers
%if &grant_type=authorization_code %then %do;
@@ -21997,47 +22046,32 @@ run;
run;
%if &mdebug=1 %then %do;
%put &sysmacroname: fetching log content from &base_uri&logloc/log/content;
- data _null_;infile &fname1;input;putlog _infile_;run;
+ data _null_;infile &fname2;input;putlog _infile_;run;
%end;
%end;
%if &SYS_PROCHTTP_STATUS_CODE ne 200 and &SYS_PROCHTTP_STATUS_CODE ne 201
%then %do;
%if &mdebug ne 1 %then %do; /* have already output above */
- data _null_;infile &fname1;input;putlog _infile_;run;
+ data _null_;infile &fname2;input;putlog _infile_;run;
%end;
%mp_abort(mac=&sysmacroname
,msg=%str(logfetch: &SYS_PROCHTTP_STATUS_CODE &SYS_PROCHTTP_STATUS_PHRASE)
)
%end;
-data _null_;
- file "&fpath3..lua";
- put '
- infile = io.open (sas.symget("fpath1"), "r")
- outfile = io.open (sas.symget("fpath2"), "w")
- io.input(infile)
- local resp=json.decode(io.read())
- for i, v in pairs(resp["items"]) do
- outfile:write(v.line,"\n")
- end
- io.close(infile)
- io.close(outfile)
- ';
-run;
-%inc "&fpath3..lua";
-/* write log out to the specified fileref */
+%let libref2=%mf_getuniquelibref();
+libname &libref2 JSON fileref=&fname2;
data _null_;
- infile &fname2 end=last;
file &outref mod;
if _n_=1 then do;
put "/** SASJS Viya Job Log Extract start: &uri **/";
end;
- input;
- put _infile_;
+ set &libref2..items end=last;
%if &mdebug=1 %then %do;
- putlog _infile_;
+ putlog line;
%end;
+ put line;
if last then do;
put "/** SASJS Viya Job Log Extract end: &uri **/";
end;
@@ -22046,7 +22080,8 @@ run;
%if &mdebug=0 %then %do;
filename &fname1 clear;
filename &fname2 clear;
- filename &fname3 clear;
+ libname &libref1 clear;
+ libname &libref2 clear;
%end;
%else %do;
%put &sysmacroname exit vars:;
@@ -25003,115 +25038,6 @@ endsub;
%end;
%mend mcf_length;/**
- @file
- @brief Provides a replacement for the stpsrv_header function
- @details The stpsrv_header is normally a built-in function, used to set the
- headers for SAS 9 Stored Processes as documented here:
- https://go.documentation.sas.com/doc/en/itechcdc/9.4/stpug/srvhead.htm
-
- The purpose of this custom function is to provide a replacement when running
- similar code as a web service against
- [sasjs/server](https://github.com/sasjs/server). It operates by creating a
- text file with the headers. The location of this text file is determined by
- a macro variable (`sasjs_stpsrv_header_loc`) which needs to be injected into
- each service by the calling process, eg:
-
- %let sasjs_stpsrv_header_loc = C:/temp/some_uuid/stpsrv_header.txt;
-
- Note - the function works by appending headers to the file. If multiple same-
- named headers are provided, they will all be appended - the calling process
- needs to pick up the last one. This will mean removing the attribute if the
- final record has an empty value.
-
- The function takes the following (positional) parameters:
-
- | PARAMETER | DESCRIPTION |
- |------------|-------------|
- | name $ | name of the header attribute to create|
- | value $ | value of the header attribute|
-
- It returns 0 if successful, or -1 if an error occured.
-
- Usage:
-
- %let sasjs_stpsrv_header_loc=%sysfunc(pathname(work))/stpsrv_header.txt;
-
- %mcf_stpsrv_header(wrap=YES, insert_cmplib=YES)
-
- data _null_;
- rc=stpsrv_header('Content-type','application/text');
- rc=stpsrv_header('Content-disposition',"attachment; filename=file.txt");
- run;
-
- data _null_;
- infile "&sasjs_stpsrv_header_loc";
- input;
- putlog _infile_;
- run;
-
-
- @param [out] wrap= (NO) Choose YES to add the proc fcmp wrapper.
- @param [out] lib= (work) The output library in which to create the catalog.
- @param [out] cat= (sasjs) The output catalog in which to create the package.
- @param [out] pkg= (utils) The output package in which to create the function.
- Uses a 3 part format: libref.catalog.package
- @param [out] insert_cmplib= DEPRECATED - The CMPLIB option is checked and
- values inserted only if needed.
-
-
SAS Macros
- @li mcf_init.sas
-
- Related Programs
- @li mcf_stpsrv_header.test.sas
- @li mp_init.sas
-
-**/
-
-%macro mcf_stpsrv_header(wrap=NO
- ,insert_cmplib=DEPRECATED
- ,lib=WORK
- ,cat=SASJS
- ,pkg=UTILS
-)/*/STORE SOURCE*/;
-%local i var cmpval found;
-%if %mcf_init(stpsrv_header)=1 %then %return;
-
-%if &wrap=YES %then %do;
- proc fcmp outlib=&lib..&cat..&pkg;
-%end;
-
-function stpsrv_header(name $, value $);
- length loc $128 val $512;
- loc=symget('sasjs_stpsrv_header_loc');
- val=trim(name)!!': '!!value;
- length fref $8;
- rc=filename(fref,loc);
- if (rc ne 0) then return( -1 );
- fid = fopen(fref,'a');
- if (fid = 0) then return( -1 );
- rc=fput(fid, val);
- rc=fwrite(fid);
- rc=fclose(fid);
- rc=filename(fref);
- return(0);
-endsub;
-
-%if &wrap=YES %then %do;
- quit;
-%end;
-
-/* insert the CMPLIB if not already there */
-%let cmpval=%sysfunc(getoption(cmplib));
-%let found=0;
-%do i=1 %to %sysfunc(countw(&cmpval,%str( %(%))));
- %let var=%scan(&cmpval,&i,%str( %(%)));
- %if &var=&lib..&cat %then %let found=1;
-%end;
-%if &found=0 %then %do;
- options insert=(CMPLIB=(&lib..&cat));
-%end;
-
-%mend mcf_stpsrv_header;/**
@file
@brief Adds a string to a file
@details Creates an fcmp function for appending a string to an external file.
diff --git a/base/mp_gsubfile.sas b/base/mp_gsubfile.sas
index 28fef41..8aa02ea 100644
--- a/base/mp_gsubfile.sas
+++ b/base/mp_gsubfile.sas
@@ -48,6 +48,11 @@
outfile=0
)/*/STORE SOURCE*/;
+ %if "%substr(&sysver,1,4)"="V.04" %then %do;
+ %put %str(ERR)OR: Viya 4 does not support the IO library in lua;
+ %return;
+ %end;
+
%ml_gsubfile()
%mend mp_gsubfile;
diff --git a/fcmp/mcf_stpsrv_header.sas b/fcmp/mcf_stpsrv_header.sas
deleted file mode 100644
index 288e07c..0000000
--- a/fcmp/mcf_stpsrv_header.sas
+++ /dev/null
@@ -1,110 +0,0 @@
-/**
- @file
- @brief Provides a replacement for the stpsrv_header function
- @details The stpsrv_header is normally a built-in function, used to set the
- headers for SAS 9 Stored Processes as documented here:
- https://go.documentation.sas.com/doc/en/itechcdc/9.4/stpug/srvhead.htm
-
- The purpose of this custom function is to provide a replacement when running
- similar code as a web service against
- [sasjs/server](https://github.com/sasjs/server). It operates by creating a
- text file with the headers. The location of this text file is determined by
- a macro variable (`sasjs_stpsrv_header_loc`) which needs to be injected into
- each service by the calling process, eg:
-
- %let sasjs_stpsrv_header_loc = C:/temp/some_uuid/stpsrv_header.txt;
-
- Note - the function works by appending headers to the file. If multiple same-
- named headers are provided, they will all be appended - the calling process
- needs to pick up the last one. This will mean removing the attribute if the
- final record has an empty value.
-
- The function takes the following (positional) parameters:
-
- | PARAMETER | DESCRIPTION |
- |------------|-------------|
- | name $ | name of the header attribute to create|
- | value $ | value of the header attribute|
-
- It returns 0 if successful, or -1 if an error occured.
-
- Usage:
-
- %let sasjs_stpsrv_header_loc=%sysfunc(pathname(work))/stpsrv_header.txt;
-
- %mcf_stpsrv_header(wrap=YES, insert_cmplib=YES)
-
- data _null_;
- rc=stpsrv_header('Content-type','application/text');
- rc=stpsrv_header('Content-disposition',"attachment; filename=file.txt");
- run;
-
- data _null_;
- infile "&sasjs_stpsrv_header_loc";
- input;
- putlog _infile_;
- run;
-
-
- @param [out] wrap= (NO) Choose YES to add the proc fcmp wrapper.
- @param [out] lib= (work) The output library in which to create the catalog.
- @param [out] cat= (sasjs) The output catalog in which to create the package.
- @param [out] pkg= (utils) The output package in which to create the function.
- Uses a 3 part format: libref.catalog.package
- @param [out] insert_cmplib= DEPRECATED - The CMPLIB option is checked and
- values inserted only if needed.
-
- SAS Macros
- @li mcf_init.sas
-
- Related Programs
- @li mcf_stpsrv_header.test.sas
- @li mp_init.sas
-
-**/
-
-%macro mcf_stpsrv_header(wrap=NO
- ,insert_cmplib=DEPRECATED
- ,lib=WORK
- ,cat=SASJS
- ,pkg=UTILS
-)/*/STORE SOURCE*/;
-%local i var cmpval found;
-%if %mcf_init(stpsrv_header)=1 %then %return;
-
-%if &wrap=YES %then %do;
- proc fcmp outlib=&lib..&cat..&pkg;
-%end;
-
-function stpsrv_header(name $, value $);
- length loc $128 val $512;
- loc=symget('sasjs_stpsrv_header_loc');
- val=trim(name)!!': '!!value;
- length fref $8;
- rc=filename(fref,loc);
- if (rc ne 0) then return( -1 );
- fid = fopen(fref,'a');
- if (fid = 0) then return( -1 );
- rc=fput(fid, val);
- rc=fwrite(fid);
- rc=fclose(fid);
- rc=filename(fref);
- return(0);
-endsub;
-
-%if &wrap=YES %then %do;
- quit;
-%end;
-
-/* insert the CMPLIB if not already there */
-%let cmpval=%sysfunc(getoption(cmplib));
-%let found=0;
-%do i=1 %to %sysfunc(countw(&cmpval,%str( %(%))));
- %let var=%scan(&cmpval,&i,%str( %(%)));
- %if &var=&lib..&cat %then %let found=1;
-%end;
-%if &found=0 %then %do;
- options insert=(CMPLIB=(&lib..&cat));
-%end;
-
-%mend mcf_stpsrv_header;
\ No newline at end of file
diff --git a/sasjs/sasjsconfig.json b/sasjs/sasjsconfig.json
index d7c348f..780bd1f 100644
--- a/sasjs/sasjsconfig.json
+++ b/sasjs/sasjsconfig.json
@@ -95,6 +95,19 @@
"tests/sas9only",
"tests/viyaonly"
]
+ },
+ {
+ "name": "viya4",
+ "serverUrl": "https://azureuse011059.my-trials.sas.com",
+ "serverType": "SASVIYA",
+ "appLoc": "/Public/temp/macrocore",
+ "macroFolders": [
+ "tests/viyaonly"
+ ],
+ "deployConfig": {
+ "deployServicePack": true
+ },
+ "contextName": "SAS Job Execution compute context"
}
]
}
\ No newline at end of file
diff --git a/tests/crossplatform/mcf_stpsrv_header.test.sas b/tests/crossplatform/mcf_stpsrv_header.test.sas
deleted file mode 100644
index 5b87d57..0000000
--- a/tests/crossplatform/mcf_stpsrv_header.test.sas
+++ /dev/null
@@ -1,39 +0,0 @@
-/**
- @file
- @brief Testing mcf_stpsrv_header macro
-
- SAS Macros
- @li mcf_stpsrv_header.sas
- @li mp_assert.sas
-
-**/
-
-%let sasjs_stpsrv_header_loc=%sysfunc(pathname(work))/stpsrv_header.txt;
-
-%mcf_stpsrv_header(wrap=YES, insert_cmplib=YES)
-
-data _null_;
- rc=stpsrv_header('Content-type','application/text');
- rc=stpsrv_header('Content-disposition',"attachment; filename=file.txt");
-run;
-
-%let test1=FAIL;
-%let test2=FAIL;
-
-data _null_;
- infile "&sasjs_stpsrv_header_loc";
- input;
- if _n_=1 and _infile_='Content-type: application/text'
- then call symputx('test1','PASS');
- else if _n_=2 & _infile_='Content-disposition: attachment; filename=file.txt'
- then call symputx('test2','PASS');
-run;
-
-%mp_assert(
- iftrue=(%str(&test1)=%str(PASS)),
- desc=Check first header line
-)
-%mp_assert(
- iftrue=(%str(&test2)=%str(PASS)),
- desc=Check second header line
-)
\ No newline at end of file
diff --git a/tests/crossplatform/mp_gsubfile.test.sas b/tests/crossplatform/mp_gsubfile.test.sas
index 9695392..407f269 100644
--- a/tests/crossplatform/mp_gsubfile.test.sas
+++ b/tests/crossplatform/mp_gsubfile.test.sas
@@ -8,6 +8,13 @@
**/
+
+%macro gsubtest();
+%if "%substr(&sysver,1,4)"="V.04" %then %do;
+ %put %str(ERR)OR: Viya 4 does not support the IO library in lua;
+ %return;
+%end;
+
/**
* test 1 - simple replace
*/
@@ -63,4 +70,8 @@ run;
iftrue=("&strcheck2b"="&str2"),
desc=Check that multi line replacement was successful (line3),
outds=work.test_results
-)
\ No newline at end of file
+)
+
+%mend gsubtest;
+
+%gsubtest()
diff --git a/tests/viyaonly/mv_deleteviyafolder.test.sas b/tests/viyaonly/mv_deleteviyafolder.test.sas
new file mode 100644
index 0000000..594c037
--- /dev/null
+++ b/tests/viyaonly/mv_deleteviyafolder.test.sas
@@ -0,0 +1,52 @@
+/**
+ @file
+ @brief Testing mv_deleteviyafolder macro function
+
+ SAS Macros
+ @li mf_uid.sas
+ @li mfv_existfolder.sas
+ @li mp_assert.sas
+ @li mp_assertscope.sas
+ @li mv_createfolder.sas
+ @li mv_deleteviyafolder.sas
+
+**/
+
+options mprint sgen;
+
+%let folder=%mf_uid();
+%let tgtfolder=&mcTestAppLoc/temp/&folder;
+
+/* create a folder */
+%mv_createfolder(path=&tgtfolder)
+
+
+%mp_assert(
+ iftrue=(%mfv_existfolder(&tgtfolder)=1),
+ desc=Check if created folder exists
+)
+
+%mp_assertscope(SNAPSHOT)
+%mv_deleteviyafolder(path=&tgtfolder)
+/* ignore proc json vars */
+%mp_assertscope(COMPARE
+ ,ignorelist=MCLIB0_JADP1LEN MCLIB0_JADP2LEN MCLIB0_JADVLEN MCLIB2_JADP1LEN
+ MCLIB2_JADVLEN
+)
+
+%mp_assert(
+ iftrue=(%mfv_existfolder(&tgtfolder)=0),
+ desc=Check if deleted folder is gone
+)
+
+/* delete folder with content */
+%mv_createfolder(path=&tgtfolder/content/and/stuff)
+%mp_assert(
+ iftrue=(%mfv_existfolder(&tgtfolder/content/and/stuff)=1),
+ desc=Check if folder with content exists
+)
+%mv_deleteviyafolder(path=&tgtfolder)
+%mp_assert(
+ iftrue=(%mfv_existfolder(&tgtfolder)=0),
+ desc=Check if deleted folder with subfolders is gone
+)
diff --git a/tests/viyaonly/mv_getjobcode.test.sas b/tests/viyaonly/mv_getjobcode.test.sas
index 7eeffd4..0d8c2a5 100644
--- a/tests/viyaonly/mv_getjobcode.test.sas
+++ b/tests/viyaonly/mv_getjobcode.test.sas
@@ -4,6 +4,7 @@
SAS Macros
@li mp_assert.sas
+ @li mp_assertscope.sas
@li mv_createjob.sas
@li mv_getjobcode.sas
@@ -27,11 +28,17 @@ run;
)
/* now get the code back */
+%mp_assertscope(SNAPSHOT)
%mv_getjobcode(
path=&mcTestAppLoc/services/temp,
name=some_job,
outref=mycode
)
+/* exclude automatic proc json macro variables from scope check */
+%mp_assertscope(COMPARE,
+ ignorelist=MCLIB2_JADP1LEN MCLIB2_JADP2LEN MCLIB2_JADPNUM MCLIB2_JADVLEN
+ MCLIB2_JADP3LEN
+)
%let diditexist=NO;
data work.test1;
@@ -46,4 +53,4 @@ run;
%mp_assert(
iftrue=(&diditexist=NO),
desc=Check if the code that was sent was successfully retrieved
-)
\ No newline at end of file
+)
diff --git a/tests/viyaonly/mv_getjoblog.test.sas b/tests/viyaonly/mv_getjoblog.test.sas
index 594f6ab..7e3e31b 100644
--- a/tests/viyaonly/mv_getjoblog.test.sas
+++ b/tests/viyaonly/mv_getjoblog.test.sas
@@ -4,6 +4,7 @@
SAS Macros
@li mp_assert.sas
+ @li mp_assertscope.sas
@li mv_createjob.sas
@li mv_jobexecute.sas
@li mv_jobwaitfor.sas
@@ -49,8 +50,12 @@ data _null_;
run;
%* Finally, fetch the log;
-%mv_getjoblog(uri=%str(&uri),outref=mylog)
-
+%mp_assertscope(SNAPSHOT)
+%mv_getjoblog(uri=%str(&uri),outref=mylog,mdebug=1)
+/* ignore auto proc json vars */
+%mp_assertscope(COMPARE
+ ,ignorelist=MCLIB2_JADP2LEN MCLIB2_JADPNUM MCLIB2_JADVLEN
+)
data _null_;
infile mylog end=eof;
@@ -67,4 +72,4 @@ run;
%mp_assert(
iftrue=(%str(&found)=1),
desc=Check if the log was still fetched even though endsas was submitted
-)
\ No newline at end of file
+)
diff --git a/viya/mv_deleteviyafolder.sas b/viya/mv_deleteviyafolder.sas
index d69c85b..f83e5cb 100644
--- a/viya/mv_deleteviyafolder.sas
+++ b/viya/mv_deleteviyafolder.sas
@@ -8,10 +8,16 @@
%mv_deleteviyafolder(path=/Public/test)
- @param path= The full path of the folder to be deleted
- @param access_token_var= The global macro variable to contain the access token
- @param grant_type= valid values are "password" or "authorization_code" (unquoted).
- The default is authorization_code.
+ @param [in] path= The full path of the folder to be deleted
+ @param [in] access_token_var= (ACCESS_TOKEN) The global macro variable to
+ contain the access token
+ @param [in] grant_type= (sas_services) Valid values are:
+ @li password
+ @li authorization_code
+ @li detect - will check if access_token exists, if not will use sas_services
+ if a SASStudioV session else authorization_code. Default option.
+ @li sas_services - will use oauth_bearer=sas_services.
+ @param [in] mdebug= (0) Set to 1 to enable DEBUG messages
@version VIYA V.03.04
@@ -29,6 +35,7 @@
%macro mv_deleteviyafolder(path=
,access_token_var=ACCESS_TOKEN
,grant_type=sas_services
+ ,mdebug=0
);
%local oauth_bearer;
%if &grant_type=detect %then %do;
@@ -105,14 +112,17 @@ run;
%let libref1a=%mf_getuniquelibref();
libname &libref1a JSON fileref=&fname1a;
-data _null_;
- set &libref1a..items_links;
- if href=:'/folders/folders' then return;
- if rel='deleteResource' then
- call execute('proc http method="DELETE" url='!!quote("&base_uri"!!trim(href))
- !!'; headers "Authorization"="Bearer &&&access_token_var" '
- !!' "Accept"="*/*";run; /**/');
-run;
+%if %mf_existds(&libref1a..items_links) %then %do;
+ data _null_;
+ set &libref1a..items_links;
+ if href=:'/folders/folders' then return;
+ if rel='deleteResource' then
+ call execute('proc http method="DELETE" url='
+ !!quote("&base_uri"!!trim(href))
+ !!'; headers "Authorization"="Bearer &&&access_token_var" '
+ !!' "Accept"="*/*";run; /**/');
+ run;
+%end;
%put &sysmacroname: perform the delete operation ;
%local fname2;
@@ -133,9 +143,11 @@ run;
%end;
%else %put &sysmacroname: &path successfully deleted;
-/* clear refs */
-filename &fname1 clear;
-filename &fname2 clear;
-libname &libref1 clear;
+%if &mdebug=0 %then %do;
+ /* clear refs */
+ filename &fname1 clear;
+ filename &fname2 clear;
+ libname &libref1 clear;
+%end;
-%mend mv_deleteviyafolder;
\ No newline at end of file
+%mend mv_deleteviyafolder;
diff --git a/viya/mv_getjobcode.sas b/viya/mv_getjobcode.sas
index 286741f..40b1509 100644
--- a/viya/mv_getjobcode.sas
+++ b/viya/mv_getjobcode.sas
@@ -33,7 +33,6 @@
@li mf_getplatform.sas
@li mf_getuniquefileref.sas
@li mv_getfoldermembers.sas
- @li ml_json.sas
**/
@@ -44,7 +43,7 @@
,grant_type=sas_services
,mdebug=0
);
-%local dbg;
+%local dbg bufsize varcnt fname1 fname2 errmsg;
%if &mdebug=1 %then %do;
%put &sysmacroname entry vars:;
%put _local_;
@@ -104,7 +103,6 @@ run;
)
/* prepare request*/
-%local fname1;
%let fname1=%mf_getuniquefileref();
proc http method='GET' out=&fname1 &oauth_bearer
url="&base_uri&joburi";
@@ -121,30 +119,81 @@ run;
,msg=%str(&SYS_PROCHTTP_STATUS_CODE &SYS_PROCHTTP_STATUS_PHRASE)
)
%end;
-%local fname2 fname3 fpath1 fpath2 fpath3;
-%let fname2=%mf_getuniquefileref();
-%let fname3=%mf_getuniquefileref();
-%let fpath1=%sysfunc(pathname(&fname1));
-%let fpath2=%sysfunc(pathname(&fname2));
-%let fpath3=%sysfunc(pathname(&fname3));
-/* compile the lua JSON module */
-%ml_json()
-/* read using LUA - this allows the code to be of any length */
+%let fname2=%mf_getuniquefileref();
+filename &fname2 temp ;
+
+/* cannot use lua IO package as not available in Viya 4 */
+/* so use data step to read the JSON until the string `"code":"` is found */
data _null_;
- file "&fpath3..lua";
- put '
- infile = io.open (sas.symget("fpath1"), "r")
- outfile = io.open (sas.symget("fpath2"), "w")
- io.input(infile)
- local resp=json.decode(io.read())
- local job=resp["code"]
- outfile:write(job)
- io.close(infile)
- io.close(outfile)
- ';
+ file &fname2 recfm=n;
+ infile &fname1 lrecl=1 recfm=n;
+ input sourcechar $ 1. @@;
+ format sourcechar hex2.;
+ retain startwrite 0;
+ if startwrite=0 and sourcechar='"' then do;
+ reentry:
+ input sourcechar $ 1. @@;
+ if sourcechar='c' then do;
+ reentry2:
+ input sourcechar $ 1. @@;
+ if sourcechar='o' then do;
+ input sourcechar $ 1. @@;
+ if sourcechar='d' then do;
+ input sourcechar $ 1. @@;
+ if sourcechar='e' then do;
+ input sourcechar $ 1. @@;
+ if sourcechar='"' then do;
+ input sourcechar $ 1. @@;
+ if sourcechar=':' then do;
+ input sourcechar $ 1. @@;
+ if sourcechar='"' then do;
+ putlog 'code found';
+ startwrite=1;
+ input sourcechar $ 1. @@;
+ end;
+ end;
+ else if sourcechar='c' then goto reentry2;
+ end;
+ end;
+ else if sourcechar='"' then goto reentry;
+ end;
+ else if sourcechar='"' then goto reentry;
+ end;
+ else if sourcechar='"' then goto reentry;
+ end;
+ else if sourcechar='"' then goto reentry;
+ end;
+ /* once the `"code":"` string is found, write until unescaped `"` is found */
+ if startwrite=1 then do;
+ if sourcechar='\' then do;
+ input sourcechar $ 1. @@;
+ if sourcechar in ('"','\') then put sourcechar char1.;
+ else if sourcechar='n' then put '0A'x;
+ else if sourcechar='r' then put '0D'x;
+ else if sourcechar='t' then put '09'x;
+ else if sourcechar='u' then do;
+ length uni $4;
+ input uni $ 4. @@;
+ sourcechar=unicode('\u'!!uni);
+ put sourcechar char1.;
+ end;
+ else do;
+ call symputx('errmsg',"Uncaught escape char: "!!sourcechar,'l');
+ call symputx('syscc',99);
+ stop;
+ end;
+ end;
+ else if sourcechar='"' then stop;
+ else put sourcechar char1.;
+ end;
run;
-%inc "&fpath3..lua";
+
+%mp_abort(iftrue=("&syscc"="99")
+ ,mac=mv_getjobcode
+ ,msg=%str(&errmsg)
+)
+
/* export to desired destination */
%if "&outref"="0" %then %do;
data _null_;
@@ -169,7 +218,6 @@ run;
/* clear refs */
filename &fname1 clear;
filename &fname2 clear;
- filename &fname3 clear;
%end;
%mend mv_getjobcode;
diff --git a/viya/mv_getjoblog.sas b/viya/mv_getjoblog.sas
index ea97e7c..5e7d1a9 100644
--- a/viya/mv_getjoblog.sas
+++ b/viya/mv_getjoblog.sas
@@ -86,7 +86,8 @@
@li mp_abort.sas
@li mf_getplatform.sas
@li mf_existfileref.sas
- @li ml_json.sas
+ @li mf_getuniquefileref.sas
+ @li mf_getuniquelibref.sas
**/
@@ -95,7 +96,7 @@
,grant_type=sas_services
,mdebug=0
);
-%local dbg;
+%local dbg libref1 libref2 loglocation fname1 fname2;
%if &mdebug=1 %then %do;
%put &sysmacroname entry vars:;
%put _local_;
@@ -154,8 +155,8 @@ options noquotelenmax;
%let base_uri=%mf_getplatform(VIYARESTAPI);
/* prepare request*/
-%local fname1;
%let fname1=%mf_getuniquefileref();
+%let fname2=%mf_getuniquefileref();
proc http method='GET' out=&fname1 &oauth_bearer
url="&base_uri&uri";
headers
@@ -175,37 +176,19 @@ run;
,msg=%str(&SYS_PROCHTTP_STATUS_CODE &SYS_PROCHTTP_STATUS_PHRASE)
)
%end;
-%local fname2 fname3 fpath1 fpath2 fpath3;
-%let fname2=%mf_getuniquefileref();
-%let fname3=%mf_getuniquefileref();
-%let fpath1=%sysfunc(pathname(&fname1));
-%let fpath2=%sysfunc(pathname(&fname2));
-%let fpath3=%sysfunc(pathname(&fname3));
-/* compile the lua JSON module */
-%ml_json()
-/* read using LUA - this allows the code to be of any length */
+%let libref1=%mf_getuniquelibref();
+libname &libref1 JSON fileref=&fname1;
data _null_;
- file "&fpath3..lua";
- put '
- infile = io.open (sas.symget("fpath1"), "r")
- outfile = io.open (sas.symget("fpath2"), "w")
- io.input(infile)
- local resp=json.decode(io.read())
- local logloc=resp["logLocation"]
- outfile:write(logloc)
- io.close(infile)
- io.close(outfile)
- ';
+ set &libref1..root;
+ call symputx('loglocation',loglocation,'l');
run;
-%inc "&fpath3..lua";
-/* get log path*/
+
+/* validate log path*/
%let errflg=1;
-%let errmsg=No entry in &fname2 fileref;
+%let errmsg=No loglocation entry in &fname1 fileref;
data _null_;
- infile &fname2;
- input;
- uri=cats(_infile_);
+ uri=symget('loglocation');
if length(uri)<12 then do;
call symputx('errflg',1);
call symputx('errmsg',"URI is invalid (too short) - '&uri'",'l');
@@ -232,7 +215,7 @@ run;
/* we have a log uri - now fetch the log */
%&dbg.put &sysmacroname: querying &base_uri&logloc/content;
-proc http method='GET' out=&fname1 &oauth_bearer
+proc http method='GET' out=&fname2 &oauth_bearer
url="&base_uri&logloc/content?limit=10000";
headers
%if &grant_type=authorization_code %then %do;
@@ -243,14 +226,14 @@ run;
%if &mdebug=1 %then %do;
%put &sysmacroname: fetching log content from &base_uri&logloc/content;
- data _null_;infile &fname1;input;putlog _infile_;run;
+ data _null_;infile &fname2;input;putlog _infile_;run;
%end;
%if &SYS_PROCHTTP_STATUS_CODE=400 %then %do;
/* fetch log from parent session */
%let logloc=%substr(&logloc,1,%index(&logloc,%str(/jobs/))-1);
%&dbg.put &sysmacroname: Now querying &base_uri&logloc/log/content;
- proc http method='GET' out=&fname1 &oauth_bearer
+ proc http method='GET' out=&fname2 &oauth_bearer
url="&base_uri&logloc/log/content?limit=10000";
headers
%if &grant_type=authorization_code %then %do;
@@ -260,47 +243,32 @@ run;
run;
%if &mdebug=1 %then %do;
%put &sysmacroname: fetching log content from &base_uri&logloc/log/content;
- data _null_;infile &fname1;input;putlog _infile_;run;
+ data _null_;infile &fname2;input;putlog _infile_;run;
%end;
%end;
%if &SYS_PROCHTTP_STATUS_CODE ne 200 and &SYS_PROCHTTP_STATUS_CODE ne 201
%then %do;
%if &mdebug ne 1 %then %do; /* have already output above */
- data _null_;infile &fname1;input;putlog _infile_;run;
+ data _null_;infile &fname2;input;putlog _infile_;run;
%end;
%mp_abort(mac=&sysmacroname
,msg=%str(logfetch: &SYS_PROCHTTP_STATUS_CODE &SYS_PROCHTTP_STATUS_PHRASE)
)
%end;
-data _null_;
- file "&fpath3..lua";
- put '
- infile = io.open (sas.symget("fpath1"), "r")
- outfile = io.open (sas.symget("fpath2"), "w")
- io.input(infile)
- local resp=json.decode(io.read())
- for i, v in pairs(resp["items"]) do
- outfile:write(v.line,"\n")
- end
- io.close(infile)
- io.close(outfile)
- ';
-run;
-%inc "&fpath3..lua";
-/* write log out to the specified fileref */
+%let libref2=%mf_getuniquelibref();
+libname &libref2 JSON fileref=&fname2;
data _null_;
- infile &fname2 end=last;
file &outref mod;
if _n_=1 then do;
put "/** SASJS Viya Job Log Extract start: &uri **/";
end;
- input;
- put _infile_;
+ set &libref2..items end=last;
%if &mdebug=1 %then %do;
- putlog _infile_;
+ putlog line;
%end;
+ put line;
if last then do;
put "/** SASJS Viya Job Log Extract end: &uri **/";
end;
@@ -309,7 +277,8 @@ run;
%if &mdebug=0 %then %do;
filename &fname1 clear;
filename &fname2 clear;
- filename &fname3 clear;
+ libname &libref1 clear;
+ libname &libref2 clear;
%end;
%else %do;
%put &sysmacroname exit vars:;