From 735bab5d262b66147157d1dbc3d3536f914d0dd9 Mon Sep 17 00:00:00 2001 From: munja Date: Sun, 6 Mar 2022 13:44:16 +0000 Subject: [PATCH 1/6] feat: viya4 config --- sasjs/sasjsconfig.json | 48 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/sasjs/sasjsconfig.json b/sasjs/sasjsconfig.json index d7c348f..9841898 100644 --- a/sasjs/sasjsconfig.json +++ b/sasjs/sasjsconfig.json @@ -95,6 +95,54 @@ "tests/sas9only", "tests/viyaonly" ] + }, + { + "name": "viya4", + "serverUrl": "https://azureuse011059.my-trials.sas.com", + "serverType": "SASVIYA", + "appLoc": "/Public/sasjscore", + "macroFolders": [], + "programFolders": [], + "binaryFolders": [], + "buildConfig": { + "initProgram": "", + "termProgram": "", + "buildOutputFileName": "viya4.sas", + "buildOutputFolder": "sasjsbuild", + "buildResultsFolder": "sasjsresults", + "macroVars": {} + }, + "jobConfig": { + "jobFolders": [], + "initProgram": "", + "termProgram": "", + "macroVars": {} + }, + "serviceConfig": { + "serviceFolders": [], + "initProgram": "", + "termProgram": "", + "macroVars": {} + }, + "streamConfig": { + "streamWebFolder": "", + "streamWeb": false, + "webSourcePath": "", + "streamServiceName": "", + "assetPaths": [] + }, + "deployConfig": { + "deployServicePack": true, + "deployScripts": [] + }, + "testConfig": { + "initProgram": "", + "termProgram": "", + "macroVars": {}, + "testSetUp": "", + "testTearDown": "" + }, + "contextName": "SAS Job Execution compute context" } ] } \ No newline at end of file From a8b5107b1a1307b2d316d18f5512e3e6590477ac Mon Sep 17 00:00:00 2001 From: munja Date: Sun, 6 Mar 2022 13:44:42 +0000 Subject: [PATCH 2/6] fix: remove mcf_stpsrv_header function (no longer needed, replaced with mfs_httpheader which is more reliable and faster --- fcmp/mcf_stpsrv_header.sas | 110 ------------------ .../crossplatform/mcf_stpsrv_header.test.sas | 39 ------- 2 files changed, 149 deletions(-) delete mode 100644 fcmp/mcf_stpsrv_header.sas delete mode 100644 tests/crossplatform/mcf_stpsrv_header.test.sas 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/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 From 17ed2240d36baa39ed73ae28b9de34497134eb9b Mon Sep 17 00:00:00 2001 From: munja Date: Sun, 6 Mar 2022 21:01:02 +0000 Subject: [PATCH 3/6] fix: removing lua from mv_getjobcode to enable Viya 4 compatibility --- base/mp_gsubfile.sas | 5 ++ sasjs/sasjsconfig.json | 47 ++---------- tests/crossplatform/mp_gsubfile.test.sas | 13 +++- tests/viyaonly/mv_getjobcode.test.sas | 9 ++- viya/mv_getjobcode.sas | 92 +++++++++++++++++------- 5 files changed, 98 insertions(+), 68 deletions(-) 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/sasjs/sasjsconfig.json b/sasjs/sasjsconfig.json index 9841898..45681df 100644 --- a/sasjs/sasjsconfig.json +++ b/sasjs/sasjsconfig.json @@ -100,49 +100,14 @@ "name": "viya4", "serverUrl": "https://azureuse011059.my-trials.sas.com", "serverType": "SASVIYA", - "appLoc": "/Public/sasjscore", - "macroFolders": [], - "programFolders": [], - "binaryFolders": [], - "buildConfig": { - "initProgram": "", - "termProgram": "", - "buildOutputFileName": "viya4.sas", - "buildOutputFolder": "sasjsbuild", - "buildResultsFolder": "sasjsresults", - "macroVars": {} - }, - "jobConfig": { - "jobFolders": [], - "initProgram": "", - "termProgram": "", - "macroVars": {} - }, - "serviceConfig": { - "serviceFolders": [], - "initProgram": "", - "termProgram": "", - "macroVars": {} - }, - "streamConfig": { - "streamWebFolder": "", - "streamWeb": false, - "webSourcePath": "", - "streamServiceName": "", - "assetPaths": [] - }, + "appLoc": "/Public/temp/macrocore", + "macroFolders": [ + "tests/viyaonly" + ], "deployConfig": { - "deployServicePack": true, - "deployScripts": [] - }, - "testConfig": { - "initProgram": "", - "termProgram": "", - "macroVars": {}, - "testSetUp": "", - "testTearDown": "" + "deployServicePack": true }, "contextName": "SAS Job Execution compute context" } ] -} \ 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_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/viya/mv_getjobcode.sas b/viya/mv_getjobcode.sas index 286741f..0e3703c 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,75 @@ 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 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 +212,6 @@ run; /* clear refs */ filename &fname1 clear; filename &fname2 clear; - filename &fname3 clear; %end; %mend mv_getjobcode; From f709a11dfb5297a96abfdffdbdb7c56052711b16 Mon Sep 17 00:00:00 2001 From: munja Date: Sun, 6 Mar 2022 22:04:51 +0000 Subject: [PATCH 4/6] fix: removing lua dependency from mv_getjoblog to enable viya 4 --- tests/viyaonly/mv_getjoblog.test.sas | 10 ++-- viya/mv_getjoblog.sas | 79 +++++++++------------------- 2 files changed, 31 insertions(+), 58 deletions(-) diff --git a/tests/viyaonly/mv_getjoblog.test.sas b/tests/viyaonly/mv_getjoblog.test.sas index 594f6ab..c68248f 100644 --- a/tests/viyaonly/mv_getjoblog.test.sas +++ b/tests/viyaonly/mv_getjoblog.test.sas @@ -49,8 +49,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 +71,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_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:; From b49e11bc790f30f6b60f8750965c1ca66927d18b Mon Sep 17 00:00:00 2001 From: munja Date: Mon, 7 Mar 2022 09:36:30 +0000 Subject: [PATCH 5/6] fix: upgrading mv_deleteviyafolder for viya 4 (and adding test) --- sasjs/sasjsconfig.json | 2 +- tests/viyaonly/mv_deleteviyafolder.test.sas | 52 +++++++++++++++++++++ tests/viyaonly/mv_getjoblog.test.sas | 1 + viya/mv_deleteviyafolder.sas | 46 +++++++++++------- viya/mv_getjobcode.sas | 6 +++ 5 files changed, 89 insertions(+), 18 deletions(-) create mode 100644 tests/viyaonly/mv_deleteviyafolder.test.sas diff --git a/sasjs/sasjsconfig.json b/sasjs/sasjsconfig.json index 45681df..780bd1f 100644 --- a/sasjs/sasjsconfig.json +++ b/sasjs/sasjsconfig.json @@ -110,4 +110,4 @@ "contextName": "SAS Job Execution compute context" } ] -} +} \ No newline at end of file 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_getjoblog.test.sas b/tests/viyaonly/mv_getjoblog.test.sas index c68248f..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 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 0e3703c..40b1509 100644 --- a/viya/mv_getjobcode.sas +++ b/viya/mv_getjobcode.sas @@ -172,6 +172,12 @@ data _null_; 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); From f7ee012be34f30a62fb7f76dad85cbae257419af Mon Sep 17 00:00:00 2001 From: munja Date: Mon, 7 Mar 2022 10:45:06 +0000 Subject: [PATCH 6/6] fix: updating all.sas --- all.sas | 338 ++++++++++++++++++++++---------------------------------- 1 file changed, 132 insertions(+), 206 deletions(-) diff --git a/all.sas b/all.sas index 6f17814..d665f58 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; @@ -20969,10 +20974,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 @@ -20990,6 +21001,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; @@ -21066,14 +21078,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; @@ -21094,12 +21109,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). @@ -21563,7 +21581,6 @@ libname &libref1 clear; @li mf_getplatform.sas @li mf_getuniquefileref.sas @li mv_getfoldermembers.sas - @li ml_json.sas **/ @@ -21574,7 +21591,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_; @@ -21634,7 +21651,6 @@ run; ) /* prepare request*/ -%local fname1; %let fname1=%mf_getuniquefileref(); proc http method='GET' out=&fname1 &oauth_bearer url="&base_uri&joburi"; @@ -21651,30 +21667,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_; @@ -21699,7 +21766,6 @@ run; /* clear refs */ filename &fname1 clear; filename &fname2 clear; - filename &fname3 clear; %end; %mend mv_getjobcode; @@ -21791,7 +21857,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 **/ @@ -21800,7 +21867,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_; @@ -21859,8 +21926,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 @@ -21880,37 +21947,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'); @@ -21937,7 +21986,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; @@ -21948,14 +21997,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; @@ -21965,47 +22014,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; @@ -22014,7 +22048,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:; @@ -24971,115 +25006,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.