From 667198e5c0f3f44d03d7252956c007a5990d8e10 Mon Sep 17 00:00:00 2001 From: allan Date: Sun, 18 May 2025 18:48:18 +0100 Subject: [PATCH] feat: mfv_getpathuri macro to get the uri of a file or folder Also refactoring mv_createfolder.sas --- base/mf_mimetype.sas | 6 +- sasjs/sasjsconfig.json | 6 +- tests/testinit.sas | 2 +- tests/viyaonly/mfv_getpathuri.test.sas | 35 +++++++ tests/viyaonly/mv_createfile.test.sas | 17 +++- tests/viyaonly/mv_createfolder.test.sas | 16 ++++ viya/mfv_getpathuri.sas | 48 ++++++++++ viya/mv_createfile.sas | 120 ++++++++++++++++++------ viya/mv_createfolder.sas | 26 +++-- 9 files changed, 229 insertions(+), 47 deletions(-) create mode 100644 tests/viyaonly/mfv_getpathuri.test.sas create mode 100644 viya/mfv_getpathuri.sas diff --git a/base/mf_mimetype.sas b/base/mf_mimetype.sas index 18055e9..c134428 100644 --- a/base/mf_mimetype.sas +++ b/base/mf_mimetype.sas @@ -431,10 +431,6 @@ %else %if &ext=movie %then %do;%str(video/x-sgi-movie)%end; %else %if &ext=ice %then %do;%str(x-conference/x-cooltalk)%end; %else %if "&ext"="in" %then %do;%str(text/plain)%end; -%else %do; - %put %str(WARN)ING: extension &ext not found!; - %put %str(WARN)ING- Returning text/plain.; - %str(text/plain) -%end; +%else %do;%str(application/octet-stream)%end; %mend mf_mimetype; \ No newline at end of file diff --git a/sasjs/sasjsconfig.json b/sasjs/sasjsconfig.json index ab77752..7a8b058 100644 --- a/sasjs/sasjsconfig.json +++ b/sasjs/sasjsconfig.json @@ -31,12 +31,16 @@ "targets": [ { "name": "viya", - "serverUrl": "", + "serverUrl": "https://viya-f0g8ht62vq.engage.sas.com", "serverType": "SASVIYA", "httpsAgentOptions": { "allowInsecureRequests": false }, "appLoc": "/Public/app/macrocore", + "deployConfig": { + "deployServicePack": true, + "deployScripts": [] + }, "macroFolders": [ "viya", "tests/viyaonly" diff --git a/tests/testinit.sas b/tests/testinit.sas index d9344ee..854b048 100644 --- a/tests/testinit.sas +++ b/tests/testinit.sas @@ -10,7 +10,7 @@ **/ /* location in metadata or SAS Drive for temporary files */ -%let mcTestAppLoc=/tmp/tests/sasjs/core/%mf_uid(); +%let mcTestAppLoc=/Public/testresults/sasjs_core/%mf_uid(); /* set defaults */ %mp_init() diff --git a/tests/viyaonly/mfv_getpathuri.test.sas b/tests/viyaonly/mfv_getpathuri.test.sas new file mode 100644 index 0000000..ef3a866 --- /dev/null +++ b/tests/viyaonly/mfv_getpathuri.test.sas @@ -0,0 +1,35 @@ +/** + @file + @brief Testing mfv_getpathuri macro function + +

SAS Macros

+ @li mf_uid.sas + @li mfv_getpathuri.sas + @li mp_assert.sas + @li mv_createfile.sas + +**/ + +options mprint sgen; + +%let file=%mf_uid(); + +/* create a folder */ +filename somefile temp; +data _null_; + file somefile; + put 'hello testings'; +run; +%let path=&mcTestAppLoc/temp; +%mv_createfile(path=&path, name=&file..txt,inref=somefile) + + +%mp_assert( + iftrue=(%mfv_existfile(&path/&file..txt)=1), + desc=Check if created file exists +) + +%mp_assert( + iftrue=(%length(%mfv_getpathuri(&path/&file..txt))>0), + desc=Check that a URI was returned +) \ No newline at end of file diff --git a/tests/viyaonly/mv_createfile.test.sas b/tests/viyaonly/mv_createfile.test.sas index 52ea2fc..708bb64 100644 --- a/tests/viyaonly/mv_createfile.test.sas +++ b/tests/viyaonly/mv_createfile.test.sas @@ -21,20 +21,33 @@ data _null_; file somefile; put 'hello testings'; run; -%mv_createfile(path=&mcTestAppLoc/temp, name=&file..txt,inref=somefile) +%mv_createfile(path=&mcTestAppLoc/temp, name=&file..txt,inref=somefile,mdebug=1) %mp_assert( iftrue=(%mfv_existfile(&mcTestAppLoc/temp/&file..txt)=1), desc=Check if created file exists ) +%put TEST 2 - html file; +filename f2 temp; +data _null_; + file f2; + put '

Hello world

'; +run; +%mv_createfile(path=&mcTestAppLoc/temp, name=test.html,inref=f2,mdebug=1) + +%mp_assert( + iftrue=(%mfv_existfile(&mcTestAppLoc/temp/test.html)=1), + desc=Check if created file exists +) + %put TEST 2 - dataset upload ; data temp; x=1; run; filename ds "%sysfunc(pathname(work))/temp.sas7bdat"; -%mv_createfile(path=&mcTestAppLoc/temp, name=&file..sas7bdat,inref=ds) +%mv_createfile(path=&mcTestAppLoc/temp, name=&file..sas7bdat,inref=ds,mdebug=1) %mp_assert( iftrue=(%mfv_existfile(&mcTestAppLoc/temp/&file..sas7bdat)=1), diff --git a/tests/viyaonly/mv_createfolder.test.sas b/tests/viyaonly/mv_createfolder.test.sas index ce10507..605d818 100644 --- a/tests/viyaonly/mv_createfolder.test.sas +++ b/tests/viyaonly/mv_createfolder.test.sas @@ -29,4 +29,20 @@ run; %mp_assert( iftrue=(&test=1), desc=Check if temp folder can be successfully created +) + +/* create a folder without output dataset as part of the original macro */ +%mv_createfolder(path=&mcTestAppLoc/temp/&folder/folder2,outds=folders2) + +%let test=0; +data _null_; + set work.folders2; + putlog (_all_)(=); + if not missing(self_uri) and not missing(parent_uri) + then call symputx('test2',1); +run; + +%mp_assert( + iftrue=(&test2=1), + desc=Check if outds param works ) \ No newline at end of file diff --git a/viya/mfv_getpathuri.sas b/viya/mfv_getpathuri.sas new file mode 100644 index 0000000..285ff66 --- /dev/null +++ b/viya/mfv_getpathuri.sas @@ -0,0 +1,48 @@ +/** + @file + @brief Returns the uri of a file or folder + @details The automatic variable _FILESRVC_[fref]_URI is used after assigning + a fileref using the filesrvc engine. + + Usage: + + %put %mfv_existfile(/Public/folder/file.txt); + %put %mfv_existfile(/Public/folder); + + @param [in] filepath The full path to the file on SAS drive + (eg /Public/myfile.txt) + +

SAS Macros

+ @li mf_abort.sas + @li mf_getuniquefileref.sas + +

Related Macros

+ @li mfv_existfile.sas + @li mfv_existfolder.sas + + @version 3.5 + @author [Allan Bowe](https://www.linkedin.com/in/allanbowe/) +**/ + +%macro mfv_getpathuri(filepath +)/*/STORE SOURCE*/; + + %mf_abort( + iftrue=(&syscc ne 0), + msg=Cannot enter &sysmacroname with syscc=&syscc + ) + + %local fref rc path name; + %let fref=%mf_getuniquefileref(); + %let name=%scan(&filepath,-1,/); + %let path=%substr(&filepath,1,%length(&filepath)-%length(&name)-1); + + %if %sysfunc(filename(fref,,filesrvc,folderPath="&path" filename="&name"))=0 + %then %do;&&_FILESRVC_&fref._URI%let rc=%sysfunc(filename(fref)); + %end; + %else %do; + %put &sysmacroname: did not find &filepath; + %let syscc=0; + %end; + +%mend mfv_getpathuri; \ No newline at end of file diff --git a/viya/mv_createfile.sas b/viya/mv_createfile.sas index 20196c8..bcf98c1 100644 --- a/viya/mv_createfile.sas +++ b/viya/mv_createfile.sas @@ -1,8 +1,12 @@ /** @file - @brief Creates a file in SAS Drive - @details Creates a file in SAS Drive and adds the appropriate content type. + @brief Creates a file in SAS Drive using the API method + @details Creates a file in SAS Drive using the API interface. If the parent folder does not exist, it is created. + The API approach is more flexible than using the filesrvc engine of the + filename statement, as it provides more options. + + SAS docs: https://developer.sas.com/rest-apis/files/createNewFile Usage: @@ -14,49 +18,54 @@ %mv_createfile(path=/Public/temp,name=newfile.txt,inref=myfile) - @param [in] path= The parent folder in which to create the file + @param [in] path= The parent (SAS Drive) folder in which to create the file @param [in] name= The name of the file to be created @param [in] inref= The fileref pointing to the file to be uploaded @param [in] intype= (BINARY) The type of the input data. Valid values: @li BINARY File is copied byte for byte using the mp_binarycopy.sas macro. @li BASE64 File will be first decoded using the mp_base64.sas macro, then loaded byte by byte to SAS Drive. - @param [in] contentdisp= (inline) Content Disposition. Example values: + @param [in] contentdisp= Content Disposition. Example values: @li inline @li attachment - @param [in] ctype= (0) Set a default HTTP Content-Type header to be returned - with the file when the content is retrieved from the Files service. + @param [in] ctype= (0) The actual MIME type of the file (if blank will be + determined based on file extension)) @param [in] access_token_var= The global macro variable to contain the access token, if using authorization_code grant type. @param [in] grant_type= (sas_services) Valid values are: @li password @li authorization_code @li sas_services + @param [out] outds= (_null_) Output dataset with the uri of the new file @param [in] mdebug= (0) Set to 1 to enable DEBUG messages - @version VIYA V.03.05 - @author Allan Bowe, source: https://github.com/sasjs/core -

SAS Macros

+ @li mf_getplatform.sas @li mf_getuniquefileref.sas + @li mf_getuniquename.sas @li mf_isblank.sas + @li mf_mimetype.sas @li mp_abort.sas @li mp_base64copy.sas @li mp_binarycopy.sas @li mv_createfolder.sas +

Related Macros

+ @li mv_createfile.sas + **/ %macro mv_createfile(path= ,name= ,inref= ,intype=BINARY - ,contentdisp=inline + ,contentdisp= ,ctype=0 ,access_token_var=ACCESS_TOKEN ,grant_type=sas_services ,mdebug=0 + ,outds=_null_ ); %local dbg; %if &mdebug=1 %then %do; @@ -91,38 +100,87 @@ ,msg=%str(name value with length >1 must be provided) ) +/* prep the source file */ +%local fref; +%let fref=%mf_getuniquefileref(); + +%if %upcase(&intype)=BINARY %then %do; + %mp_binarycopy(inref=&inref, outref=&fref) +%end; +%else %if %upcase(&intype)=BASE64 %then %do; + %mp_base64copy(inref=&inref, outref=&fref, action=DECODE) +%end; +%else %put %str(ERR)OR: invalid value for intype: &intype; + + +%if &mdebug=1 %then %do; + data _null_; + infile &fref lrecl=32767; + input; + put _infile_; + run; +%end; + + /* create folder if it does not already exist */ +%local folderds parenturi; +%let folderds=%mf_getuniquename(prefix=folderds); %mv_createfolder(path=&path ,access_token_var=&access_token_var ,grant_type=&grant_type ,mdebug=&mdebug + ,outds=&folderds ) +data _null_; + set &folderds; + call symputx('self_uri',self_uri,'l'); +run; -/* create file with relevant options */ -%local fref; -%let fref=%mf_getuniquefileref(); -filename &fref filesrvc - folderPath="&path" - filename="&name" - cdisp="&contentdisp" -%if "&ctype" ne "0" %then %do; - ctype="&ctype" -%end; - lrecl=1048544; -%if &intype=BINARY %then %do; - %mp_binarycopy(inref=&inref, outref=&fref) -%end; -%else %if &intype=BASE64 %then %do; - %mp_base64copy(inref=&inref, outref=&fref, action=DECODE) -%end; - -filename &fref clear; +options noquotelenmax; %local base_uri; /* location of rest apis */ %let base_uri=%mf_getplatform(VIYARESTAPI); -%put &sysmacroname: File &name successfully created in &path; -%put &sysmacroname:;%put; + +/* fetch job info */ +%local fname1; +%let fname1=%mf_getuniquefileref(); +proc http method='POST' out=&fname1 &oauth_bearer in=&fref + %if "&ctype" = "0" %then %do; + ct="%mf_mimetype(%scan(&name,-1,.))" + %end; + %else %do; + ct="&ctype" + %end; + url="&base_uri/files/files?parentFolderUri=&self_uri"; + + headers "Accept"="application/json" + %if &grant_type=authorization_code %then %do; + "Authorization"="Bearer &&&access_token_var" + %end; + "Content-Disposition"= "&contentdisp filename=""&name""; name=""&name"";"; +run; +%put &=SYS_PROCHTTP_STATUS_CODE; +%put &=SYS_PROCHTTP_STATUS_PHRASE; +%mp_abort(iftrue=(&SYS_PROCHTTP_STATUS_CODE ne 201) + ,mac=&sysmacroname + ,msg=%str(&SYS_PROCHTTP_STATUS_CODE &SYS_PROCHTTP_STATUS_PHRASE) +) +%local libref2; +%let libref2=%mf_getuniquelibref(); +libname &libref2 JSON fileref=&fname1; +%put Grabbing the follow on link ; +data &outds; + set &libref2..links end=last; + if rel='createChild' then do; + call symputx('href',quote(cats("&base_uri",href)),'l'); + &dbg put (_all_)(=); + end; +run; + + + +%put &sysmacroname: File &name successfully created:;%put; %put &base_uri/SASJobExecution?_file=&path/&name;%put; %put &sysmacroname:; diff --git a/viya/mv_createfolder.sas b/viya/mv_createfolder.sas index 3ff7279..1fca4d3 100644 --- a/viya/mv_createfolder.sas +++ b/viya/mv_createfolder.sas @@ -17,15 +17,16 @@ @li sas_services @param [in] mdebug=(0) set to 1 to enable DEBUG messages + @param [out] outds=(_null_) Optionally create an output dataset which will + contain the uri (self_uri) of the created (and parent) folder. - @version VIYA V.03.04 - @author Allan Bowe, source: https://github.com/sasjs/core

SAS Macros

@li mp_abort.sas @li mf_getuniquefileref.sas @li mf_getuniquelibref.sas @li mf_isblank.sas + @li mfv_getpathuri.sas @li mf_getplatform.sas @li mfv_existfolder.sas @@ -36,6 +37,7 @@ ,access_token_var=ACCESS_TOKEN ,grant_type=sas_services ,mdebug=0 + ,outds=_null_ ); %local dbg; %if &mdebug=1 %then %do; @@ -46,6 +48,11 @@ %if %mfv_existfolder(&path)=1 %then %do; %put &sysmacroname: &path already exists; + data &outds; + self_uri="%mfv_getpathuri(&path)"; + output; + stop; + run; %return; %end; @@ -80,12 +87,12 @@ options noquotelenmax; %local subfolder_cnt; /* determine the number of subfolders */ %let subfolder_cnt=%sysfunc(countw(&path,/)); -%local href; /* resource address (none for root) */ -%let href="/folders/folders?parentFolderUri=/folders/folders/none"; - %local base_uri; /* location of rest apis */ %let base_uri=%mf_getplatform(VIYARESTAPI); +%local href; /* resource address (none for root) */ +%let href="&base_uri/folders/folders?parentFolderUri=/folders/folders/none"; + %local x newpath subfolder; %do x=1 %to &subfolder_cnt; %let subfolder=%scan(&path,&x,%str(/)); @@ -115,7 +122,7 @@ options noquotelenmax; %put &sysmacroname following check to see if &newpath exists:; %put _local_; data _null_; - set &fname1; + infile &fname1; input; putlog _infile_; run; @@ -167,12 +174,17 @@ options noquotelenmax; %let libref2=%mf_getuniquelibref(); libname &libref2 JSON fileref=&fname2; %put &sysmacroname &newpath now created. Grabbing the follow on link ; - data _null_; + data &outds; set &libref2..links; if rel='createChild' then do; call symputx('href',quote(cats("&base_uri",href)),'l'); &dbg put (_all_)(=); end; + if method='GET' and rel='self' then do; + self_uri=uri; + output; + end; + keep self_uri ; run; libname &libref2 clear;