1
0
mirror of https://github.com/sasjs/core.git synced 2026-06-09 04:10:20 +00:00

Compare commits

...

22 Commits

Author SHA1 Message Date
Allan Bowe 1fb52d5f73 fix: installing doxygen in pipeline 2026-05-01 09:21:26 +00:00
Allan Bowe 0c90ac8deb feat: auto-deploy docs to core.sasjs.io 2026-05-01 09:19:10 +00:00
Allan Bowe fdf0a1b514 Merge pull request #421 from sasjs/mv_getviyafileextparams
Mv getviyafileextparams
2026-04-29 12:59:00 +01:00
github-actions 4d15f4ebf5 chore: updating all.sas 2026-04-29 11:50:49 +00:00
4gl 402337a952 fix: avoid strange error when running mv_getviyafileextparams in Studio 2026-04-29 12:50:03 +01:00
Allan Bowe a2a8004b06 Merge pull request #420 from sasjs/castabload
feat: new & updated SAS Viya / CAS macros
2026-04-28 19:12:06 +01:00
github-actions 955854919f chore: updating all.sas 2026-04-28 18:07:10 +00:00
4gl f1ac0bd821 fix: removing usecache option (wasn't used in the end) 2026-04-28 19:06:41 +01:00
github-actions f642e35f6b chore: updating all.sas 2026-04-28 17:48:38 +00:00
4gl 36452a2a02 fix: simplifying mv_castabload and improving tests 2026-04-28 18:39:09 +01:00
github-actions 1c005586dc chore: updating all.sas 2026-04-28 16:36:58 +00:00
4gl 7ef58a0f54 feat: mv_castabsave macro and tests 2026-04-28 17:36:25 +01:00
github-actions 8a22280627 chore: updating all.sas 2026-04-28 11:40:09 +00:00
4gl b6fad4a469 chore: adding quit in mp_assertscope and excluding .claude in .gitignore 2026-04-28 12:39:40 +01:00
github-actions 7aa788e547 chore: updating all.sas 2026-04-28 11:39:16 +00:00
4gl 73fd85d254 feat: new mfv_getcaslib macro
Fetches a caslib from a regular SAS libref
2026-04-28 12:38:41 +01:00
github-actions 7acaafae99 chore: updating all.sas 2026-04-27 16:29:53 +00:00
4gl d0a5780cd1 feat: adding tests, adding param to mfv_existsashdat, updating README 2026-04-27 17:29:07 +01:00
4gl 08f2d0d53f feat: castabload macro 2026-04-27 14:11:09 +01:00
Allan Bowe 3a54b9c796 Merge pull request #419 from sasjs/islib_validation
Islib validation
2026-04-02 22:50:36 +00:00
github-actions e66ef31e26 chore: updating all.sas 2026-04-02 22:45:31 +00:00
allan 186ea1cfba feat: adding ISLIB validation to mp_validatecol macro 2026-04-02 23:45:09 +01:00
21 changed files with 1496 additions and 87 deletions
+9
View File
@@ -54,6 +54,7 @@ jobs:
echo "REFRESH_TOKEN=${{secrets.SAS9_4GL_IO_REFRESH_TOKEN}}" >> .env.server echo "REFRESH_TOKEN=${{secrets.SAS9_4GL_IO_REFRESH_TOKEN}}" >> .env.server
- name: Semantic Release - name: Semantic Release
id: makerelease
uses: cycjimmy/semantic-release-action@v6 uses: cycjimmy/semantic-release-action@v6
env: env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
@@ -64,3 +65,11 @@ jobs:
npx @sasjs/cli compile job -s sasjs/utils/create_sas_package.sas -o sasjsbuild -t server npx @sasjs/cli compile job -s sasjs/utils/create_sas_package.sas -o sasjsbuild -t server
# need long duration token per https://github.com/sasjs/server/issues/307 # need long duration token per https://github.com/sasjs/server/issues/307
# npx @sasjs/cli run sasjsbuild/jobs/utils/create_sas_package.sas -t server # npx @sasjs/cli run sasjsbuild/jobs/utils/create_sas_package.sas -t server
- name: Update Docs Site
if: steps.makerelease.outputs.new_release_published == 'true'
run: |
sudo apt-get update
sudo apt-get install -y doxygen
export GITHUB_TOKEN=${{ secrets.GITHUB_TOKEN }}
npm run docs
+1
View File
@@ -14,3 +14,4 @@ mc_*
~ ~
.claude
+1 -1
View File
@@ -201,7 +201,7 @@ When contributing to this library, it is therefore important to ensure that all
- All dataset references must be 2 level (eg `work.blah`, not `blah`). This is to avoid contention when options [DATASTMTCHK](https://support.sas.com/documentation/cdl/en/lrdict/64316/HTML/default/viewer.htm#a000279064.htm)=ALLKEYWORDS is in effect, or the [USER](https://documentation.sas.com/doc/en/pgmsascdc/9.4_3.5/lrcon/n18m1vkqmeo4esn1moikt23zhp8s.htm) library is active. - All dataset references must be 2 level (eg `work.blah`, not `blah`). This is to avoid contention when options [DATASTMTCHK](https://support.sas.com/documentation/cdl/en/lrdict/64316/HTML/default/viewer.htm#a000279064.htm)=ALLKEYWORDS is in effect, or the [USER](https://documentation.sas.com/doc/en/pgmsascdc/9.4_3.5/lrcon/n18m1vkqmeo4esn1moikt23zhp8s.htm) library is active.
- Avoid naming collisions! All macro variables should be local scope. Use system generated work tables where possible - eg `data ; set sashelp.class; run; data &output; set &syslast; run;` - Avoid naming collisions! All macro variables should be local scope. Use system generated work tables where possible - eg `data ; set sashelp.class; run; data &output; set &syslast; run;`
- Where global macro variables are absolutely necessary, they should make use of `&sasjs_prefix` - see mp_init.sas - Where global macro variables are absolutely necessary, they should make use of `&sasjs_prefix` - see mp_init.sas
- The use of `quit;` for `proc sql` is optional unless you are looking to benefit from the timing statistics. - The use of `quit;` for `proc sql` is essential, to avoid `WARNING: You cannot disconnect or terminate session XXXX until the procedure completes.` when terminating CAS sessions in Viya.
- Use [sasjs lint](https://github.com/sasjs/lint)! - Use [sasjs lint](https://github.com/sasjs/lint)!
## General Notes ## General Notes
+502 -42
View File
@@ -3512,6 +3512,7 @@ run;
run; run;
proc sql; proc sql;
drop table &ds; drop table &ds;
quit;
%mend mp_assert;/** %mend mp_assert;/**
@file @file
@@ -4042,6 +4043,7 @@ run;
from dictionary.macros from dictionary.macros
where scope="&scope" and upcase(name) not in (%mf_getquotedstr(&ilist)) where scope="&scope" and upcase(name) not in (%mf_getquotedstr(&ilist))
order by name,offset; order by name,offset;
quit;
%end; %end;
%else %if &action=COMPARE %then %do; %else %if &action=COMPARE %then %do;
@@ -4079,7 +4081,6 @@ run;
%let test_comments=%str(Mod:(&mod) Add:(&add) Del:(&del)); %let test_comments=%str(Mod:(&mod) Add:(&add) Del:(&del));
%end; %end;
data ; data ;
length test_description $256 test_result $4 test_comments $256; length test_description $256 test_result $4 test_comments $256;
test_description=symget('desc'); test_description=symget('desc');
@@ -4092,6 +4093,7 @@ run;
run; run;
proc sql; proc sql;
drop table &ds; drop table &ds;
quit;
%end; %end;
%mend mp_assertscope; %mend mp_assertscope;
@@ -10103,8 +10105,9 @@ filename &tempref clear;
&prefix._INIT_NUM /* initialisation time as numeric */ &prefix._INIT_NUM /* initialisation time as numeric */
&prefix._INIT_DTTM /* initialisation time in E8601DT26.6 format */ &prefix._INIT_DTTM /* initialisation time in E8601DT26.6 format */
&prefix.WORK /* avoid typing %sysfunc(pathname(work)) every time */ &prefix.WORK /* avoid typing %sysfunc(pathname(work)) every time */
&prefix.PROCESSMODE
&prefix._STPSRV_HEADER_LOC
; ;
%let sasjs_prefix=&prefix; %let sasjs_prefix=&prefix;
data _null_; data _null_;
@@ -14541,7 +14544,7 @@ alter table &libds modify &var char(&len);
%mend mp_updatevarlength; %mend mp_updatevarlength;
/** /**
@file @file
@brief Used to validate variables in a dataset @brief Used to validate values in a data step
@details Useful when sanitising inputs, to ensure that they arrive with a @details Useful when sanitising inputs, to ensure that they arrive with a
certain pattern. certain pattern.
Usage: Usage:
@@ -14568,6 +14571,7 @@ alter table &libds modify &var char(&len);
@param [in] incol The column to be validated @param [in] incol The column to be validated
@param [in] rule The rule to apply. Current rules: @param [in] rule The rule to apply. Current rules:
@li ISINT - checks if the variable is an integer @li ISINT - checks if the variable is an integer
@li ISLIB - checks if the value is a valid libref (NOT whether it exists)
@li ISNUM - checks if the variable is numeric @li ISNUM - checks if the variable is numeric
@li LIBDS - matches LIBREF.DATASET format @li LIBDS - matches LIBREF.DATASET format
@li FORMAT - checks if the provided format is syntactically valid @li FORMAT - checks if the provided format is syntactically valid
@@ -14606,6 +14610,19 @@ alter table &libds modify &var char(&len);
else &outcol=1; else &outcol=1;
drop &tempcol; drop &tempcol;
%end; %end;
%else %if &rule=ISLIB %then %do;
if _n_=1 then do;
retain &tempcol;
&tempcol=prxparse('/^[_a-z]\w{0,7}$/i');
if missing(&tempcol) then do;
putlog 'ERR' +(-1) "OR: Invalid expression for ISLIB";
stop;
end;
drop &tempcol;
end;
if prxmatch(&tempcol, trim(&incol)) then &outcol=1;
else &outcol=0;
%end;
%else %if &rule=LIBDS %then %do; %else %if &rule=LIBDS %then %do;
/* match libref.dataset */ /* match libref.dataset */
if _n_=1 then do; if _n_=1 then do;
@@ -24197,12 +24214,12 @@ run;
%if %mfv_existsashdat(libds=casuser.sometable) %then %put yes it does!; %if %mfv_existsashdat(libds=casuser.sometable) %then %put yes it does!;
The function uses `dosubl()` to run the `table.fileinfo` action, for the The function uses `dosubl()` to run the `table.fileinfo` action, for the
specified library, filtering for `*.sashdat` tables. The results are stored specified library, filtering for `*.sashdat` tables.
in a WORK table (&outprefix._&lib). If that table already exists, it is
queried instead, to avoid the dosubl() performance hit.
To force a rescan, just use a new `&outprefix` value, or delete the table(s) Results are cached in a WORK table (&outprefix._&lib). If that table
before running the function. already exists it is queried directly to avoid the dosubl() overhead.
To force a rescan, use a new `&outprefix` value or delete the cache
table before calling.
@param [in] libds library.dataset @param [in] libds library.dataset
@param [out] outprefix= (work.mfv_existsashdat) @param [out] outprefix= (work.mfv_existsashdat)
@@ -24215,13 +24232,12 @@ run;
@author Mathieu Blauw @author Mathieu Blauw
**/ **/
%macro mfv_existsashdat(libds,outprefix=work.mfv_existsashdat %macro mfv_existsashdat(libds,outprefix=work.mfv_existsashdat);
);
%local rc dsid name lib ds; %local rc dsid name lib ds;
%let lib=%upcase(%scan(&libds,1,'.')); %let lib=%upcase(%scan(&libds,1,'.'));
%let ds=%upcase(%scan(&libds,-1,'.')); %let ds=%upcase(%scan(&libds,-1,'.'));
/* if table does not exist, create it */ /* if cache table does not exist, build it */
%if %sysfunc(exist(&outprefix._&lib)) ne 1 %then %do; %if %sysfunc(exist(&outprefix._&lib)) ne 1 %then %do;
%let rc=%sysfunc(dosubl(%nrstr( %let rc=%sysfunc(dosubl(%nrstr(
/* Read in table list (once per &lib per session) */ /* Read in table list (once per &lib per session) */
@@ -24232,7 +24248,7 @@ run;
quit; quit;
/* Only keep name, without file extension */ /* Only keep name, without file extension */
data &outprefix._&lib; data &outprefix._&lib;
set &outprefix._&lib(where=(Name like '%.sashdat') keep=Name); set &outprefix._&lib(where=(upcase(Name) like '%.SASHDAT') keep=Name);
Name=upcase(scan(Name,1,'.')); Name=upcase(scan(Name,1,'.'));
run; run;
))); )));
@@ -24249,6 +24265,43 @@ run;
%else 0; %else 0;
%mend mfv_existsashdat; %mend mfv_existsashdat;
/**
@file mfv_getcaslib.sas
@brief Returns the CAS caslib name for a given SAS libref
@details Pure macro function. Reads sashelp.vlibnam and returns
the sysvalue where sysname='Caslib' for the given libref. This
is useful when the caslib name and libref name may differ.
Usage:
%put %mfv_getcaslib(lib=PUBLIC);
@param [in] lib SAS libref for which to return the CAS caslib name
@return Returns the CAS caslib name, or empty string if not found
**/
%macro mfv_getcaslib(lib);
%local dsid rc result;
%let dsid=%sysfunc(open(sashelp.vlibnam(
where=(libname="%upcase(&lib)" and sysname="Caslib")
)));
%if &dsid %then %do;
%let rc=%sysfunc(fetch(&dsid));
%if &rc=0 %then
%let result=%sysfunc(
getvarc(&dsid,%sysfunc(varnum(&dsid,SYSVALUE)))
);
%let rc=%sysfunc(close(&dsid));
%end;
&result
%mend mfv_getcaslib;
/** /**
@file @file
@brief Returns the path of a folder from the URI @brief Returns the path of a folder from the URI
@@ -24357,6 +24410,405 @@ run;
msg=Cannot leave &sysmacroname with syscc=&syscc msg=Cannot leave &sysmacroname with syscc=&syscc
) )
%mend mfv_getpathuri;/** %mend mfv_getpathuri;/**
@file mv_castabload.sas
@brief Checks if a CAS table is loaded; if not, loads and promotes it
@details Runs in SPRE against an active CAS session. Accepts a
SAS libref, derives the CAS caslib and session UUID from
sashelp.vlibnam, then checks whether the table is already
in-memory. If not, locates the owning CAS server via the
casManagement REST API, queries the table endpoint to discover
the source file and caslib, then loads and promotes the table.
A CAS session must already be established by the caller, eg:
cas mysess;
libname mylib cas caslib=Public;
%mv_castabload(lib=mylib, table=BASEBALL)
@param [in] lib= SAS libref for the CAS caslib
@param [in] table= Name of the CAS table to load
@param [in] mdebug= (0) Set to 1 to enable verbose logging:
- echoes resolved parameters
- prints tableExists result
- enables mprint/notes during PROC calls
<h4> SAS Macros </h4>
@li mf_getplatform.sas
@li mf_getuniquefileref.sas
@li mf_getuniquelibref.sas
@li mp_abort.sas
**/
%macro mv_castabload(
lib=
,table=
,mdebug=0
);
%local _sysopts base_uri caslib uuid server
srcfile srccaslib fname1 libref1 ftmp i _svcount _exists;
%let _sysopts=%sysfunc(getoption(mprint)) %sysfunc(getoption(notes));
/* ---- input validation -------------------------------------------------- */
%mp_abort(
iftrue=("&lib"="" or "&table"=""),
msg=%str(lib= and table= are required)
)
%if &mdebug=1 %then %do;
%put &=lib;
%put &=table;
options mprint notes;
%end;
/* ---- derive caslib and session UUID from sashelp.vlibnam --------------- */
data _null_;
set sashelp.vlibnam(
where=(libname="%upcase(&lib)"
and sysname in ("Caslib","Session UUID"))
);
if sysname="Caslib" then call symputx('caslib',sysvalue,'L');
else call symputx('uuid',sysvalue,'L');
%if &mdebug=1 %then %do;
putlog sysname sysvalue;
%end;
run;
%mp_abort(
iftrue=("&caslib"=""),
msg=%str(&lib is not an assigned CAS libref)
)
%mp_abort(
iftrue=("&uuid"=""),
msg=%str(No session UUID found for libref &lib)
)
/* ---- existence check --------------------------------------------------- */
proc cas;
table.tableExists result=r /
caslib="&caslib"
name="&table";
%if &mdebug=1 %then %do;
print r;
%end;
if r.exists > 0 then call symputx('_exists', '1', 'L');
else call symputx('_exists', '0', 'L');
quit;
/* ---- already loaded: skip ---------------------------------------------- */
%if &_exists=1 %then %do;
%put NOTE: Table &caslib..&table already loaded - skipping;
%return;
%end;
/* ---- get list of CAS servers ----------------------------------------- */
%let base_uri=%mf_getplatform(VIYARESTAPI);
%let fname1=%mf_getuniquefileref();
%let libref1=%mf_getuniquelibref();
proc http method='GET' out=&fname1 oauth_bearer=sas_services
url="&base_uri/casManagement/servers";
run;
%mp_abort(
iftrue=(&SYS_PROCHTTP_STATUS_CODE ne 200),
msg=%str(&SYS_PROCHTTP_STATUS_CODE &SYS_PROCHTTP_STATUS_PHRASE)
)
libname &libref1 JSON fileref=&fname1;
data _null_;
set &libref1..items;
call symputx(cats('_sv_', _n_), name, 'L');
call symputx('_svcount', _n_, 'L');
run;
libname &libref1 clear;
filename &fname1 clear;
/* ---- find which server owns this session ------------------------------ */
%do i=1 %to &_svcount;
%if "&server"="" %then %do;
%if &mdebug=1 %then %put checking server: &&_sv_&i;
%let ftmp=%mf_getuniquefileref();
proc http method='GET' out=&ftmp oauth_bearer=sas_services
url="&base_uri/casManagement/servers/&&_sv_&i/sessions/&uuid";
run;
%if &SYS_PROCHTTP_STATUS_CODE=200
%then %let server=&&_sv_&i;
filename &ftmp clear;
%end;
%end;
%mp_abort(
iftrue=("&server"=""),
msg=%str(Could not find owning server for CAS session &uuid)
)
%if &mdebug=1 %then %put &=server;
/* ---- discover source file from REST endpoint -------------------------- */
%let fname1=%mf_getuniquefileref();
%let libref1=%mf_getuniquelibref();
proc http method='GET' out=&fname1 oauth_bearer=sas_services
url="&base_uri/casManagement/servers/&server/caslibs/&caslib/tables/&table";
run;
%if &mdebug=1 %then %do;
%put &=SYS_PROCHTTP_STATUS_CODE &=SYS_PROCHTTP_STATUS_PHRASE;
data _null_;
infile &fname1;
input;
putlog _infile_;
run;
%end;
%mp_abort(
iftrue=(&SYS_PROCHTTP_STATUS_CODE=404),
msg=%str(&caslib..&table not found - is a source file registered?)
)
%mp_abort(
iftrue=(&SYS_PROCHTTP_STATUS_CODE ne 200),
msg=%str(&SYS_PROCHTTP_STATUS_CODE &SYS_PROCHTTP_STATUS_PHRASE)
)
libname &libref1 JSON fileref=&fname1;
data _null_;
set &libref1..tablereference;
call symputx('srcfile', sourceTableName, 'L');
call symputx('srccaslib', sourceCaslibName, 'L');
stop;
run;
libname &libref1 clear;
filename &fname1 clear;
%mp_abort(
iftrue=("&srcfile"="" or "&srccaslib"=""),
msg=%str(No sourceTableName/sourceCaslibName for &caslib..&table)
)
%if &mdebug=1 %then %put &=srcfile &=srccaslib;
/* ---- load from discovered source -------------------------------------- */
proc casutil;
load casdata="&srcfile"
incaslib="&srccaslib"
casout="&table"
outcaslib="&caslib"
promote;
quit;
%mp_abort(
iftrue=(&syscc ne 0),
msg=%str(Load failed for &caslib..&table)
)
%put NOTE: Table &caslib..&table loaded and promoted from &srcfile;
/* ---- restore options --------------------------------------------------- */
%if &mdebug=1 %then %do;
options &_sysopts;
%end;
%mend mv_castabload;
/**
@file mv_castabsave.sas
@brief Saves an in-memory CAS table back to persistent storage
@details Runs in SPRE against an active CAS session. Accepts a
SAS libref, derives the CAS caslib and session UUID from
sashelp.vlibnam, locates the owning CAS server via the
casManagement REST API, then queries the table endpoint to
discover the original source file and saves back to that path.
CASUTIL infers the file type from the output file extension.
A CAS session must already be established by the caller, eg:
cas mysess;
libname mylib cas caslib=Public;
%mv_castabsave(lib=mylib, table=BASEBALL)
@param [in] lib= SAS libref for the CAS caslib
@param [in] table= Name of the in-memory CAS table to save
@param [in] mdebug= (0) Set to 1 to enable verbose logging:
- echoes resolved parameters
- prints HTTP response body
- enables mprint/notes during PROC calls
<h4> SAS Macros </h4>
@li mf_getplatform.sas
@li mf_getuniquefileref.sas
@li mf_getuniquelibref.sas
@li mp_abort.sas
**/
%macro mv_castabsave(
lib=
,table=
,mdebug=0
);
%local _sysopts base_uri caslib uuid server
srcfile srccaslib fname1 libref1 ftmp i _svcount;
%let _sysopts=%sysfunc(getoption(mprint)) %sysfunc(getoption(notes));
/* ---- input validation -------------------------------------------------- */
%mp_abort(
iftrue=("&lib"="" or "&table"=""),
msg=%str(lib= and table= are required)
)
%if &mdebug=1 %then %do;
%put &=lib;
%put &=table;
options mprint notes;
%end;
/* ---- derive caslib and session UUID from sashelp.vlibnam --------------- */
data _null_;
set sashelp.vlibnam(
where=(libname="%upcase(&lib)"
and sysname in ("Caslib","Session UUID"))
);
if sysname="Caslib" then call symputx('caslib',sysvalue,'L');
else call symputx('uuid',sysvalue,'L');
run;
%mp_abort(
iftrue=("&caslib"=""),
msg=%str(&lib is not an assigned CAS libref)
)
%mp_abort(
iftrue=("&uuid"=""),
msg=%str(No session UUID found for libref &lib)
)
%if &mdebug=1 %then %do;
%put &=caslib;
%put &=uuid;
%end;
%let base_uri=%mf_getplatform(VIYARESTAPI);
/* ---- get list of CAS servers ------------------------------------------- */
%let fname1=%mf_getuniquefileref();
%let libref1=%mf_getuniquelibref();
proc http method='GET' out=&fname1 oauth_bearer=sas_services
url="&base_uri/casManagement/servers";
run;
%mp_abort(
iftrue=(&SYS_PROCHTTP_STATUS_CODE ne 200),
msg=%str(&SYS_PROCHTTP_STATUS_CODE &SYS_PROCHTTP_STATUS_PHRASE)
)
libname &libref1 JSON fileref=&fname1;
data _null_;
set &libref1..items;
call symputx(cats('_sv_', _n_), name, 'L');
call symputx('_svcount', _n_, 'L');
run;
libname &libref1 clear;
filename &fname1 clear;
/* ---- find which server owns this session ------------------------------- */
%do i=1 %to &_svcount;
%if "&server"="" %then %do;
%if &mdebug=1 %then %put checking server: &&_sv_&i;
%let ftmp=%mf_getuniquefileref();
proc http method='GET' out=&ftmp oauth_bearer=sas_services
url="&base_uri/casManagement/servers/&&_sv_&i/sessions/&uuid";
run;
%if &SYS_PROCHTTP_STATUS_CODE=200
%then %let server=&&_sv_&i;
filename &ftmp clear;
%end;
%end;
%mp_abort(
iftrue=("&server"=""),
msg=%str(Could not find owning server for CAS session &uuid)
)
%if &mdebug=1 %then %put &=server;
/* ---- discover srcfile from REST endpoint ------------------------------- */
%let fname1=%mf_getuniquefileref();
%let libref1=%mf_getuniquelibref();
proc http method='GET' out=&fname1 oauth_bearer=sas_services
url="&base_uri/casManagement/servers/&server/caslibs/&caslib/tables/&table";
run;
%if &mdebug=1 %then %do;
%put &=SYS_PROCHTTP_STATUS_CODE &=SYS_PROCHTTP_STATUS_PHRASE;
data _null_;
infile &fname1;
input;
putlog _infile_;
run;
%end;
%mp_abort(
iftrue=(&SYS_PROCHTTP_STATUS_CODE=404),
msg=%str(&caslib..&table not found - is it loaded in memory?)
)
%mp_abort(
iftrue=(&SYS_PROCHTTP_STATUS_CODE ne 200),
msg=%str(&SYS_PROCHTTP_STATUS_CODE &SYS_PROCHTTP_STATUS_PHRASE)
)
libname &libref1 JSON fileref=&fname1;
data _null_;
set &libref1..tablereference;
call symputx('srcfile', sourceTableName, 'L');
call symputx('srccaslib', sourceCaslibName, 'L');
stop;
run;
libname &libref1 clear;
filename &fname1 clear;
%mp_abort(
iftrue=("&srcfile"="" or "&srccaslib"=""),
msg=%str(No sourceTableName/sourceCaslibName for &caslib..&table)
)
%if &mdebug=1 %then %put &=srcfile;
/* ---- save to disk ------------------------------------------------------- */
proc casutil;
save casdata="&table"
incaslib="&caslib"
casout="&srcfile"
outcaslib="&srccaslib"
replace;
quit;
%mp_abort(
iftrue=(&syscc ne 0),
msg=%str(Save failed for &caslib..&table)
)
%put NOTE: Table &caslib..&table saved to &srcfile;
/* ---- restore options --------------------------------------------------- */
%if &mdebug=1 %then %do;
options &_sysopts;
%end;
%mend mv_castabsave;
/**
@file @file
@brief Creates a file in SAS Drive using the API method @brief Creates a file in SAS Drive using the API method
@details Creates a file in SAS Drive using the API interface. @details Creates a file in SAS Drive using the API interface.
@@ -24562,9 +25014,10 @@ run;
/* Get Viya file-extension details into some macro variables */ /* Get Viya file-extension details into some macro variables */
%mv_getViyaFileExtParms(&ext %mv_getViyaFileExtParms(&ext
,propertiesVar=viyaProperties ,propertiesVar=viyaProperties
,typeDefNameVar=viyaTypeDefName ,typeDefNameVar=viyaTypeDefName
,mdebug=&mdebug); ,mdebug=&mdebug
)
/* fetch job info */ /* fetch job info */
%local fname1; %local fname1;
@@ -24632,9 +25085,9 @@ run;
/* If properties were found then patch the file to include them */ /* If properties were found then patch the file to include them */
%if not %mf_isBlank(%superq(viyaProperties)) %then %do; %if not %mf_isBlank(%superq(viyaProperties)) %then %do;
/* Wrap the properties object in a root object also containing the file name */ /* Wrap the properties object in a root object also containing the filename */
%local viyapatch; %local viyapatch;
%let viyapatch = %sysfunc(pathname(work))/%mf_getuniquename(prefix=patch_json_); %let viyapatch=%sysfunc(pathname(work))/%mf_getuniquename(prefix=patch_json_);
data _null_; data _null_;
length line $32767; length line $32767;
file "&viyapatch" lrecl=32767; file "&viyapatch" lrecl=32767;
@@ -24654,7 +25107,7 @@ run;
run; run;
%end; %end;
/* And apply the properties to the newly created file, using the PATCH method */ /* Apply the properties to the newly created file, using the PATCH method */
%let fref=%mf_getuniquefileref(); %let fref=%mf_getuniquefileref();
filename &fref "&viyapatch"; filename &fref "&viyapatch";
%let url=&base_uri&fileuri; %let url=&base_uri&fileuri;
@@ -28275,6 +28728,7 @@ libname &libref1 clear;
@li mf_existds.sas @li mf_existds.sas
@li mf_getplatform.sas @li mf_getplatform.sas
@li mf_getuniquefileref.sas @li mf_getuniquefileref.sas
@li mf_getuniquelibref.sas
@li mf_getuniquename.sas @li mf_getuniquename.sas
@li mf_getvalue.sas @li mf_getvalue.sas
@li mf_getvarlist.sas @li mf_getvarlist.sas
@@ -28300,7 +28754,7 @@ libname &libref1 clear;
iftrue=(%mf_isBlank(&ext)) iftrue=(%mf_isBlank(&ext))
,msg=%str(No file extension provided.) ,msg=%str(No file extension provided.)
,mac=MV_GETVIYAFILEEXTPARMS ,mac=MV_GETVIYAFILEEXTPARMS
); )
%mp_abort( %mp_abort(
iftrue=(%mf_isBlank(&typeDefNameVar) and iftrue=(%mf_isBlank(&typeDefNameVar) and
@@ -28308,13 +28762,13 @@ libname &libref1 clear;
%mf_isBlank(&mediaTypeVar)) %mf_isBlank(&mediaTypeVar))
,msg=%str(MV_GETVIYAFILEEXTPARMS - No parameter was requested.) ,msg=%str(MV_GETVIYAFILEEXTPARMS - No parameter was requested.)
,mac=MV_GETVIYAFILEEXTPARMS ,mac=MV_GETVIYAFILEEXTPARMS
); )
%mp_abort( %mp_abort(
iftrue=(%mf_isBlank(&viyaFileExtRespLibDs)) iftrue=(%mf_isBlank(&viyaFileExtRespLibDs))
,msg=%str(No <libname.>dataset name provided to cache inital response.) ,msg=%str(No <libname.>dataset name provided to cache inital response.)
,mac=MV_GETVIYAFILEEXTPARMS ,mac=MV_GETVIYAFILEEXTPARMS
); )
/* Declare requested parameters as global macro vars and initialize blank */ /* Declare requested parameters as global macro vars and initialize blank */
%if not %mf_isBlank(&typeDefNameVar) %then %do; %if not %mf_isBlank(&typeDefNameVar) %then %do;
@@ -28331,9 +28785,7 @@ libname &libref1 clear;
%end; %end;
%let base_uri=%mf_getplatform(VIYARESTAPI); %let base_uri=%mf_getplatform(VIYARESTAPI);
%if &mdebug=1 %then %do; %if &mdebug=1 %then %put DEBUG: &=base_uri;
%put DEBUG: &=base_uri;
%end;
%let ext=%lowcase(&ext); %let ext=%lowcase(&ext);
@@ -28357,7 +28809,7 @@ libname &libref1 clear;
%if (&SYS_PROCHTTP_STATUS_CODE ne 200) %then %do; %if (&SYS_PROCHTTP_STATUS_CODE ne 200) %then %do;
/* To avoid a breaking change, exit early if the request failed. /* To avoid a breaking change, exit early if the request failed.
The calling process will proceed with empty requested macro variables. */ The calling process will proceed with empty macro variables. */
%put INFO: &sysmacroname File extension details were not retrieved.; %put INFO: &sysmacroname File extension details were not retrieved.;
filename &viyatypedefs clear; filename &viyatypedefs clear;
%return; %return;
@@ -28378,11 +28830,12 @@ libname &libref1 clear;
/* Convert the content of that JSON into SAS datasets */ /* Convert the content of that JSON into SAS datasets */
/* First prepare a new WORK-based folder to receive the datasets */ /* First prepare a new WORK-based folder to receive the datasets */
%local jsonworkfolder jsonlib opt_dlcreatedir; %local jsonworkfolder jsonlib opt_dlcreatedir;
%let jsonworkfolder=%sysfunc(pathname(work))/%mf_getuniquename(prefix=json_); %let jsonworkfolder=%sysfunc(pathname(work))/%mf_getuniquename(prefix=jsn_);
%let jsonlib=%mf_getuniquelibref(prefix=json); %let jsonlib=%mf_getuniquelibref(prefix=json);
/* And point a libname at it */ /* And point a libname at it */
%let opt_dlcreatedir = %sysfunc(getoption(dlcreatedir)); %let opt_dlcreatedir = %sysfunc(getoption(dlcreatedir));
options dlcreatedir; libname &jsonlib "&jsonworkfolder"; options &opt_dlcreatedir; options dlcreatedir; libname &jsonlib "&jsonworkfolder";
options &opt_dlcreatedir;
/* Read the json output once and copy datasets to its work folder */ /* Read the json output once and copy datasets to its work folder */
%local libref1; %local libref1;
@@ -28411,20 +28864,22 @@ libname &libref1 clear;
%end; /* If initial filetype query response didn't exist */ %end; /* If initial filetype query response didn't exist */
/* Find the row-group for the current file extension */ %if &mdebug %then %put DEBUG: Find the row-group for extension &ext;
%local itemRowGroup; %local itemRowGroup;
%let itemRowGroup = data _null_;
%mf_getValue( set &viyaFileExtRespLibDs;
&viyaFileExtRespLibDs where p1='items' and p2='extensions' and value="&ext";
,_viyaItemIdx call symputx('itemRowGroup',_viyaItemIdx,'l');
,filter=%quote(p1='items' and p2='extensions' and value="&ext") %if &mdebug %then %do;
); putlog (_all_)(=);
%end;
run;
%if &mdebug %then %put DEBUG: &=itemRowGroup; %if &mdebug %then %put DEBUG: &=itemRowGroup;
%if %mf_isBlank(&itemRowGroup) %then %do; %if %mf_isBlank(&itemRowGroup) %then %do;
/* extension was not found */ /* extension was not found */
%if(&mdebug=1) %then %put DEBUG: No type details found for extension "&ext".; %if &mdebug %then %put DEBUG: No type details found for extension "&ext";
%return; %return;
%end; %end;
@@ -28438,14 +28893,17 @@ libname &libref1 clear;
/* Populate typeDefName, if requested */ /* Populate typeDefName, if requested */
%if (not %mf_isBlank(&typeDefNameVar)) %then %do; %if (not %mf_isBlank(&typeDefNameVar)) %then %do;
%let &typeDefNameVar = %mf_getvalue(&dsItems,value,filter=%quote(p1="items" and p2="name")); %let &typeDefNameVar = %mf_getvalue(
%if &mdebug=1 %then %put DEBUG: &=typeDefNameVar &typeDefNameVar=&&&typeDefNameVar; &dsItems,value,filter=%quote(p1="items" and p2="name"));
%if &mdebug %then
%put DEBUG: &=typeDefNameVar &typeDefNameVar=&&&typeDefNameVar;
%end; %end;
/* Populate mediaType, if requested */ /* Populate mediaType, if requested */
%if (not %mf_isBlank(&mediaTypeVar)) %then %do; %if (not %mf_isBlank(&mediaTypeVar)) %then %do;
%let &mediaTypeVar = %mf_getvalue(&dsItems,value,filter=%quote(p1="items" and p2="mediaType")); %let &mediaTypeVar = %mf_getvalue(
%if &mdebug=1 %then %put DEBUG: &=mediaTypeVar &mediaTypeVar=&&&mediaTypeVar; &dsItems,value,filter=%quote(p1="items" and p2="mediaType"));
%if &mdebug %then %put DEBUG: &=mediaTypeVar &mediaTypeVar=&&&mediaTypeVar;
%end; %end;
/* Populate properties macro variable, if requested */ /* Populate properties macro variable, if requested */
@@ -28462,7 +28920,8 @@ libname &libref1 clear;
/* Check for 1+ properties */ /* Check for 1+ properties */
%if ( %mf_nobs(&dsProperties) = 0 ) %then %do; %if ( %mf_nobs(&dsProperties) = 0 ) %then %do;
%let &propertiesVar = %str(); %let &propertiesVar = %str();
%if &mdebug=1 %then %put DEBUG: &SYSMACRONAME - No Viya properties found for file suffix %str(%')&ext%str(%'); %if &mdebug %then %put DEBUG: &SYSMACRONAME - No Viya properties %trim(
)found for file suffix %str(%')&ext%str(%');
%end; %end;
%else %do; %else %do;
/* Properties potentially span multiple rows in the input table */ /* Properties potentially span multiple rows in the input table */
@@ -28492,7 +28951,8 @@ libname &libref1 clear;
end; end;
run; run;
%if &mdebug=1 %then %put DEBUG: &=propertiesVar &propertiesVar=&&&propertiesVar; %if &mdebug %then
%put DEBUG: &=propertiesVar &propertiesVar=&&&propertiesVar;
%end; %end;
%end; %end;
+1
View File
@@ -52,5 +52,6 @@
run; run;
proc sql; proc sql;
drop table &ds; drop table &ds;
quit;
%mend mp_assert; %mend mp_assert;
+2 -1
View File
@@ -92,6 +92,7 @@
from dictionary.macros from dictionary.macros
where scope="&scope" and upcase(name) not in (%mf_getquotedstr(&ilist)) where scope="&scope" and upcase(name) not in (%mf_getquotedstr(&ilist))
order by name,offset; order by name,offset;
quit;
%end; %end;
%else %if &action=COMPARE %then %do; %else %if &action=COMPARE %then %do;
@@ -129,7 +130,6 @@
%let test_comments=%str(Mod:(&mod) Add:(&add) Del:(&del)); %let test_comments=%str(Mod:(&mod) Add:(&add) Del:(&del));
%end; %end;
data ; data ;
length test_description $256 test_result $4 test_comments $256; length test_description $256 test_result $4 test_comments $256;
test_description=symget('desc'); test_description=symget('desc');
@@ -142,6 +142,7 @@
run; run;
proc sql; proc sql;
drop table &ds; drop table &ds;
quit;
%end; %end;
%mend mp_assertscope; %mend mp_assertscope;
+2 -1
View File
@@ -41,8 +41,9 @@
&prefix._INIT_NUM /* initialisation time as numeric */ &prefix._INIT_NUM /* initialisation time as numeric */
&prefix._INIT_DTTM /* initialisation time in E8601DT26.6 format */ &prefix._INIT_DTTM /* initialisation time in E8601DT26.6 format */
&prefix.WORK /* avoid typing %sysfunc(pathname(work)) every time */ &prefix.WORK /* avoid typing %sysfunc(pathname(work)) every time */
&prefix.PROCESSMODE
&prefix._STPSRV_HEADER_LOC
; ;
%let sasjs_prefix=&prefix; %let sasjs_prefix=&prefix;
data _null_; data _null_;
+15 -1
View File
@@ -1,6 +1,6 @@
/** /**
@file @file
@brief Used to validate variables in a dataset @brief Used to validate values in a data step
@details Useful when sanitising inputs, to ensure that they arrive with a @details Useful when sanitising inputs, to ensure that they arrive with a
certain pattern. certain pattern.
Usage: Usage:
@@ -27,6 +27,7 @@
@param [in] incol The column to be validated @param [in] incol The column to be validated
@param [in] rule The rule to apply. Current rules: @param [in] rule The rule to apply. Current rules:
@li ISINT - checks if the variable is an integer @li ISINT - checks if the variable is an integer
@li ISLIB - checks if the value is a valid libref (NOT whether it exists)
@li ISNUM - checks if the variable is numeric @li ISNUM - checks if the variable is numeric
@li LIBDS - matches LIBREF.DATASET format @li LIBDS - matches LIBREF.DATASET format
@li FORMAT - checks if the provided format is syntactically valid @li FORMAT - checks if the provided format is syntactically valid
@@ -65,6 +66,19 @@
else &outcol=1; else &outcol=1;
drop &tempcol; drop &tempcol;
%end; %end;
%else %if &rule=ISLIB %then %do;
if _n_=1 then do;
retain &tempcol;
&tempcol=prxparse('/^[_a-z]\w{0,7}$/i');
if missing(&tempcol) then do;
putlog 'ERR' +(-1) "OR: Invalid expression for ISLIB";
stop;
end;
drop &tempcol;
end;
if prxmatch(&tempcol, trim(&incol)) then &outcol=1;
else &outcol=0;
%end;
%else %if &rule=LIBDS %then %do; %else %if &rule=LIBDS %then %do;
/* match libref.dataset */ /* match libref.dataset */
if _n_=1 then do; if _n_=1 then do;
+1 -1
View File
@@ -8,7 +8,7 @@
# refresh github pages site # refresh github pages site
rm -rf sasjsbuild/docsite rm -rf sasjsbuild/docsite
git clone git@github.com:sasjs/core.github.io.git sasjsbuild/docsite git clone https://x-access-token:$GITHUB_TOKEN@github.com/sasjs/core.github.io.git sasjsbuild/docsite
rm -rf sasjsbuild/docsite/*.html rm -rf sasjsbuild/docsite/*.html
rm -rf sasjsbuild/docsite/*.js rm -rf sasjsbuild/docsite/*.js
rm -rf sasjsbuild/docsite/*.png rm -rf sasjsbuild/docsite/*.png
+26
View File
@@ -130,3 +130,29 @@ run;
test=EQUALS 6, test=EQUALS 6,
outds=work.test_results outds=work.test_results
) )
/**
* Test 5 - ISLIB
*/
data test5;
infile datalines4 dsd;
input;
inf=_infile_;
%mp_validatecol(inf,ISLIB,islib)
if islib=1;
datalines4;
some
!lib
%abort
definite
2fail
nineletrs
.failalso
_valid
;;;;
run;
%mp_assertdsobs(work.test5,
desc=Testing ISLIB,
test=EQUALS 3,
outds=work.test_results
)
+74
View File
@@ -0,0 +1,74 @@
/**
@file
@brief Testing mfv_existsashdat macro function
<h4> SAS Macros </h4>
@li mf_uid.sas
@li mfv_existsashdat.sas
@li mp_assert.sas
@li mp_assertscope.sas
**/
options mprint;
/* ------------------------------------------------------------------------ */
/* Setup: start a CAS session and stage a sashdat file in the Public caslib */
/* ------------------------------------------------------------------------ */
cas mysess;
caslib _all_ assign;
%let testcaslib = Public; /* change this if Public isn't available */
proc cas;
table.caslibInfo result=r / ;
found = 0;
do row over r.CASLibInfo;
if upcase(row.Name) = upcase("&testcaslib") then found = 1;
end;
if found = 0 then do;
print "ERROR: caslib &testcaslib not available";
exit;
end;
quit;
%put NOTE: Using testcaslib=&testcaslib;
%let tab1=T%mf_uid();
proc casutil;
load data=sashelp.baseball outcaslib="&testcaslib" casout="&tab1" replace;
save casdata="&tab1" incaslib="&testcaslib"
casout="&tab1..sashdat" outcaslib="&testcaslib" replace;
droptable casdata="&tab1" incaslib="&testcaslib" quiet;
quit;
/* ------------------------------------------------------------------------ */
%put TEST 1 - returns 1 when the sashdat file exists in the caslib;
/* ------------------------------------------------------------------------ */
%mp_assert(
iftrue=(%mfv_existsashdat(&testcaslib..&tab1)=1),
desc=Test 1 - Check returns 1 for a sashdat that exists
)
/* ------------------------------------------------------------------------ */
%put TEST 2 - returns 0 when the file does not exist in the caslib;
/* ------------------------------------------------------------------------ */
%mp_assertscope(SNAPSHOT)
%mp_assert(
iftrue=(%mfv_existsashdat(&testcaslib..DOESNOTEXIST_%mf_uid())=0),
desc=Check returns 0 for a sashdat that does not exist
)
%mp_assertscope(COMPARE,
desc=Check mfv_existsashdat does not leak macro variables into GLOBAL scope
)
/* ------------------------------------------------------------------------ */
/* Teardown */
/* ------------------------------------------------------------------------ */
proc casutil;
deletesource casdata="&tab1..sashdat" incaslib="&testcaslib" quiet;
quit;
cas mysess terminate;
%let syscc=0;
+69
View File
@@ -0,0 +1,69 @@
/**
@file
@brief Testing mfv_getcaslib macro function
<h4> SAS Macros </h4>
@li mfv_getcaslib.sas
@li mp_assert.sas
@li mp_assertscope.sas
**/
options mprint;
/* ------------------------------------------------------------------------ */
/* Setup: start a CAS session and assign caslibs */
/* ------------------------------------------------------------------------ */
cas mysess;
caslib _all_ assign;
%let testcaslib=Public;
libname castest cas caslib=&testcaslib;
/* ------------------------------------------------------------------------ */
%put TEST 1 - returns the caslib name for a valid CAS libref;
/* ------------------------------------------------------------------------ */
%mp_assert(
iftrue=(%mfv_getcaslib(castest)=%upcase(&testcaslib)),
desc=Check correct caslib name returned for a valid CAS libref
)
/* ------------------------------------------------------------------------ */
%put TEST 2 - returns empty for a non-CAS libref (WORK);
/* ------------------------------------------------------------------------ */
%mp_assert(
iftrue=(%mfv_getcaslib(WORK)=),
desc=Check empty string returned for a non-CAS libref
)
/* ------------------------------------------------------------------------ */
%put TEST 3 - returns empty for a libref that does not exist;
/* ------------------------------------------------------------------------ */
%mp_assert(
iftrue=(%mfv_getcaslib(DOESNOTEXIST)=),
desc=Check empty string returned for a non-existent libref
)
/* ------------------------------------------------------------------------ */
%put TEST 5 - no scope leakage into global macro variables;
/* ------------------------------------------------------------------------ */
%mp_assertscope(SNAPSHOT)
%let _rc=%mfv_getcaslib(castest);
%mp_assertscope(COMPARE,
desc=Check mfv_getcaslib does not leak macro variables into GLOBAL scope,
ignorelist=_RC
)
/* ------------------------------------------------------------------------ */
/* Teardown */
/* ------------------------------------------------------------------------ */
cas mysess terminate;
+152
View File
@@ -0,0 +1,152 @@
/**
@file
@brief Testing mv_castabload macro
<h4> SAS Macros </h4>
@li mf_uid.sas
@li mp_assert.sas
@li mp_assertscope.sas
@li mv_castabload.sas
**/
options mprint;
/* -------------------------------------------------------------------- */
/* Setup: start a CAS session and stage a source file in the caslib */
/* -------------------------------------------------------------------- */
cas mysess;
caslib _all_ assign;
%let testcaslib=Public;
proc cas;
table.caslibInfo result=r / ;
found=0;
do row over r.CASLibInfo;
if upcase(row.Name)=upcase("&testcaslib") then found=1;
end;
if found=0 then do;
print "ERROR: caslib &testcaslib not available";
exit;
end;
quit;
%put NOTE: Using testcaslib=&testcaslib;
%let tab1=T%mf_uid();
/* Save a sashdat source file then drop the in-memory copy so the first
mv_castabload call has something to load */
proc casutil;
load data=sashelp.baseball
outcaslib="&testcaslib" casout="&tab1" replace;
save casdata="&tab1" incaslib="&testcaslib"
casout="&tab1..sashdat" outcaslib="&testcaslib" replace;
droptable casdata="&tab1" incaslib="&testcaslib" quiet;
quit;
libname mylib cas caslib="&testcaslib";
/* -------------------------------------------------------------------- */
%put TEST 1 - load a table that is not in memory;
/* -------------------------------------------------------------------- */
/* Confirm table is absent before the call */
%let _tabexists=0;
proc cas;
table.tableExists result=r /
caslib="&testcaslib" name="&tab1";
if r.exists > 0 then call symputx('_tabexists','1');
quit;
%mp_assert(
iftrue=(&_tabexists=0),
desc=Check table is not in memory before mv_castabload
)
%mv_castabload(lib=mylib, table=&tab1, mdebug=1)
%let _tabexists=0;
proc cas;
table.tableExists result=r /
caslib="&testcaslib" name="&tab1";
if r.exists > 0 then call symputx('_tabexists','1');
quit;
%mp_assert(
iftrue=(&_tabexists=1),
desc=Check table is in memory after mv_castabload
)
/* -------------------------------------------------------------------- */
%put TEST 2 - reload fetches a fresh copy and discards in-memory changes;
/* -------------------------------------------------------------------- */
/* Append a sentinel row to the in-memory table */
data work.extra;
set mylib.&tab1;
name='TESTROW';
output;
stop;
run;
proc casutil;
load data=work.extra casout="&tab1"
outcaslib="&testcaslib" append;
quit;
%let _modified=0;
proc sql noprint;
select count(*) into :_modified
from mylib.&tab1
where name='TESTROW';
quit;
%mp_assert(
iftrue=(&_modified=1),
desc=Check sentinel row is present in memory before reload
)
/* Drop the table and reload - source file does not have the sentinel */
proc casutil;
droptable casdata="&tab1" incaslib="&testcaslib" quiet;
droptable casdata="&tab1" incaslib="&testcaslib" quiet;
quit;
%mp_assertscope(SNAPSHOT)
%mv_castabload(lib=mylib, table=&tab1, mdebug=1)
%mp_assertscope(COMPARE,
desc=Check mv_castabload does not leak macro variables into GLOBAL scope
)
%let _after=0;
proc sql noprint;
select count(*) into :_after
from mylib.&tab1
where name='TESTROW';
quit;
%mp_assert(
iftrue=(&_after=0),
desc=Check sentinel row is absent after reload from source
)
/* -------------------------------------------------------------------- */
/* Teardown */
/* -------------------------------------------------------------------- */
libname mylib clear;
proc casutil;
droptable casdata="&tab1" incaslib="&testcaslib" quiet;
droptable casdata="&tab1" incaslib="&testcaslib" quiet;
deletesource casdata="&tab1..sashdat"
incaslib="&testcaslib" quiet;
quit;
cas mysess terminate;
%let syscc=0;
+158
View File
@@ -0,0 +1,158 @@
/**
@file
@brief Testing mv_castabsave macro
<h4> SAS Macros </h4>
@li mf_uid.sas
@li mp_assert.sas
@li mp_assertscope.sas
@li mv_castabsave.sas
**/
options mprint;
/* -------------------------------------------------------------------- */
/* Setup: start a CAS session and load a table that has a tracked */
/* source file so mv_castabsave can discover it via the REST API */
/* -------------------------------------------------------------------- */
cas mysess;
caslib _all_ assign;
%let testcaslib=Public;
proc cas;
table.caslibInfo result=r / ;
found=0;
do row over r.CASLibInfo;
if upcase(row.Name)=upcase("&testcaslib") then found=1;
end;
if found=0 then do;
print "ERROR: caslib &testcaslib not available";
exit;
end;
quit;
%put NOTE: Using testcaslib=&testcaslib;
%let tab1=T%mf_uid();
/* Load sashelp.class into CAS, save as sashdat, reload from that file
so the table has a tracked source path (needed for REST discovery) */
proc casutil;
load data=sashelp.class
outcaslib="&testcaslib" casout="&tab1" replace;
save casdata="&tab1" incaslib="&testcaslib"
casout="&tab1..sashdat" outcaslib="&testcaslib" replace;
/* Drop any existing global-scope version before promoting */
/* runs twice (with quiet) as first would drop local scope if exists */
droptable casdata="&tab1" incaslib="&testcaslib" quiet;
droptable casdata="&tab1" incaslib="&testcaslib" quiet;
load casdata="&tab1..sashdat" incaslib="&testcaslib"
casout="&tab1" outcaslib="&testcaslib" promote;
quit;
libname mylib cas caslib="&testcaslib";
/* -------------------------------------------------------------------- */
%put TEST 1 - save in-memory table back to disk + no scope leakage;
/* -------------------------------------------------------------------- */
/* Source file is removed so that the reload proves mv_castabsave
created the file from scratch, not that a prior version existed */
proc casutil;
deletesource casdata="&tab1..sashdat"
incaslib="&testcaslib" quiet;
quit;
/* Insert a sentinel row - it must survive the full save/drop/reload */
data work.appendme;
set mylib.&tab1;
name='TESTROW';
output;
stop;
proc casutil;
load data=work.appendme casout="&tab1" outcaslib="&testcaslib" append;
quit;
%mp_assertscope(SNAPSHOT)
%mv_castabsave(lib=mylib, table=&tab1, mdebug=1)
%mp_assertscope(COMPARE,
desc=Check mv_castabsave does not leak macro variables into GLOBAL scope,
ignorelist=MC0_JADP1LEN MC0_JADP2LEN MC0_JADP3LEN MC0_JADPNUM MC0_JADVLEN
)
proc casutil;
droptable casdata="&tab1" incaslib="&testcaslib" quiet;
droptable casdata="&tab1" incaslib="&testcaslib" quiet;
load casdata="&tab1..sashdat" incaslib="&testcaslib"
casout="&tab1" outcaslib="&testcaslib" promote;
quit;
%let _rowcount=0;
proc sql noprint;
select count(*) into :_rowcount
from mylib.&tab1
where name='TESTROW';
quit;
%mp_assert(
iftrue=(&_rowcount=1),
desc=Check inserted row survives mv_castabsave round-trip to disk
)
/* -------------------------------------------------------------------- */
%put TEST 2 - save overwrites an existing source file;
/* -------------------------------------------------------------------- */
/* Source file already exists from the TEST 1 save - append a new row */
data work.appendme;
set mylib.&tab1;
name='TESTROW2';
output;
stop;
proc casutil;
load data=work.appendme casout="&tab1"
outcaslib="&testcaslib" append;
quit;
%mv_castabsave(lib=mylib, table=&tab1, mdebug=1)
proc casutil;
droptable casdata="&tab1" incaslib="&testcaslib" quiet;
droptable casdata="&tab1" incaslib="&testcaslib" quiet;
load casdata="&tab1..sashdat" incaslib="&testcaslib"
casout="&tab1" outcaslib="&testcaslib" promote;
quit;
%let _rowcount=0;
proc sql noprint;
select count(*) into :_rowcount
from mylib.&tab1
where name='TESTROW2';
quit;
%mp_assert(
iftrue=(&_rowcount=1),
desc=Check inserted row survives save over an existing source file
)
/* -------------------------------------------------------------------- */
/* Teardown */
/* -------------------------------------------------------------------- */
libname mylib clear;
proc casutil;
droptable casdata="&tab1" incaslib="&testcaslib" quiet;
deletesource casdata="&tab1..sashdat"
incaslib="&testcaslib" quiet;
quit;
cas mysess terminate;
%let syscc=0;
@@ -80,7 +80,7 @@ options mprint;
typeDefNameVar=invalidTypeDefName, typeDefNameVar=invalidTypeDefName,
propertiesVar=invalidProperties, propertiesVar=invalidProperties,
mediaTypeVar=invalidMediaType mediaTypeVar=invalidMediaType
) )
%mp_assertscope(COMPARE %mp_assertscope(COMPARE
,ignorelist= ,ignorelist=
&mvarIgnoreList invalidTypeDefName invalidProperties invalidMediaType &mvarIgnoreList invalidTypeDefName invalidProperties invalidMediaType
+8 -9
View File
@@ -6,12 +6,12 @@
%if %mfv_existsashdat(libds=casuser.sometable) %then %put yes it does!; %if %mfv_existsashdat(libds=casuser.sometable) %then %put yes it does!;
The function uses `dosubl()` to run the `table.fileinfo` action, for the The function uses `dosubl()` to run the `table.fileinfo` action, for the
specified library, filtering for `*.sashdat` tables. The results are stored specified library, filtering for `*.sashdat` tables.
in a WORK table (&outprefix._&lib). If that table already exists, it is
queried instead, to avoid the dosubl() performance hit.
To force a rescan, just use a new `&outprefix` value, or delete the table(s) Results are cached in a WORK table (&outprefix._&lib). If that table
before running the function. already exists it is queried directly to avoid the dosubl() overhead.
To force a rescan, use a new `&outprefix` value or delete the cache
table before calling.
@param [in] libds library.dataset @param [in] libds library.dataset
@param [out] outprefix= (work.mfv_existsashdat) @param [out] outprefix= (work.mfv_existsashdat)
@@ -24,13 +24,12 @@
@author Mathieu Blauw @author Mathieu Blauw
**/ **/
%macro mfv_existsashdat(libds,outprefix=work.mfv_existsashdat %macro mfv_existsashdat(libds,outprefix=work.mfv_existsashdat);
);
%local rc dsid name lib ds; %local rc dsid name lib ds;
%let lib=%upcase(%scan(&libds,1,'.')); %let lib=%upcase(%scan(&libds,1,'.'));
%let ds=%upcase(%scan(&libds,-1,'.')); %let ds=%upcase(%scan(&libds,-1,'.'));
/* if table does not exist, create it */ /* if cache table does not exist, build it */
%if %sysfunc(exist(&outprefix._&lib)) ne 1 %then %do; %if %sysfunc(exist(&outprefix._&lib)) ne 1 %then %do;
%let rc=%sysfunc(dosubl(%nrstr( %let rc=%sysfunc(dosubl(%nrstr(
/* Read in table list (once per &lib per session) */ /* Read in table list (once per &lib per session) */
@@ -41,7 +40,7 @@
quit; quit;
/* Only keep name, without file extension */ /* Only keep name, without file extension */
data &outprefix._&lib; data &outprefix._&lib;
set &outprefix._&lib(where=(Name like '%.sashdat') keep=Name); set &outprefix._&lib(where=(upcase(Name) like '%.SASHDAT') keep=Name);
Name=upcase(scan(Name,1,'.')); Name=upcase(scan(Name,1,'.'));
run; run;
))); )));
+37
View File
@@ -0,0 +1,37 @@
/**
@file mfv_getcaslib.sas
@brief Returns the CAS caslib name for a given SAS libref
@details Pure macro function. Reads sashelp.vlibnam and returns
the sysvalue where sysname='Caslib' for the given libref. This
is useful when the caslib name and libref name may differ.
Usage:
%put %mfv_getcaslib(lib=PUBLIC);
@param [in] lib SAS libref for which to return the CAS caslib name
@return Returns the CAS caslib name, or empty string if not found
**/
%macro mfv_getcaslib(lib);
%local dsid rc result;
%let dsid=%sysfunc(open(sashelp.vlibnam(
where=(libname="%upcase(&lib)" and sysname="Caslib")
)));
%if &dsid %then %do;
%let rc=%sysfunc(fetch(&dsid));
%if &rc=0 %then
%let result=%sysfunc(
getvarc(&dsid,%sysfunc(varnum(&dsid,SYSVALUE)))
);
%let rc=%sysfunc(close(&dsid));
%end;
&result
%mend mfv_getcaslib;
+207
View File
@@ -0,0 +1,207 @@
/**
@file mv_castabload.sas
@brief Checks if a CAS table is loaded; if not, loads and promotes it
@details Runs in SPRE against an active CAS session. Accepts a
SAS libref, derives the CAS caslib and session UUID from
sashelp.vlibnam, then checks whether the table is already
in-memory. If not, locates the owning CAS server via the
casManagement REST API, queries the table endpoint to discover
the source file and caslib, then loads and promotes the table.
A CAS session must already be established by the caller, eg:
cas mysess;
libname mylib cas caslib=Public;
%mv_castabload(lib=mylib, table=BASEBALL)
@param [in] lib= SAS libref for the CAS caslib
@param [in] table= Name of the CAS table to load
@param [in] mdebug= (0) Set to 1 to enable verbose logging:
- echoes resolved parameters
- prints tableExists result
- enables mprint/notes during PROC calls
<h4> SAS Macros </h4>
@li mf_getplatform.sas
@li mf_getuniquefileref.sas
@li mf_getuniquelibref.sas
@li mp_abort.sas
**/
%macro mv_castabload(
lib=
,table=
,mdebug=0
);
%local _sysopts base_uri caslib uuid server
srcfile srccaslib fname1 libref1 ftmp i _svcount _exists;
%let _sysopts=%sysfunc(getoption(mprint)) %sysfunc(getoption(notes));
/* ---- input validation -------------------------------------------------- */
%mp_abort(
iftrue=("&lib"="" or "&table"=""),
msg=%str(lib= and table= are required)
)
%if &mdebug=1 %then %do;
%put &=lib;
%put &=table;
options mprint notes;
%end;
/* ---- derive caslib and session UUID from sashelp.vlibnam --------------- */
data _null_;
set sashelp.vlibnam(
where=(libname="%upcase(&lib)"
and sysname in ("Caslib","Session UUID"))
);
if sysname="Caslib" then call symputx('caslib',sysvalue,'L');
else call symputx('uuid',sysvalue,'L');
%if &mdebug=1 %then %do;
putlog sysname sysvalue;
%end;
run;
%mp_abort(
iftrue=("&caslib"=""),
msg=%str(&lib is not an assigned CAS libref)
)
%mp_abort(
iftrue=("&uuid"=""),
msg=%str(No session UUID found for libref &lib)
)
/* ---- existence check --------------------------------------------------- */
proc cas;
table.tableExists result=r /
caslib="&caslib"
name="&table";
%if &mdebug=1 %then %do;
print r;
%end;
if r.exists > 0 then call symputx('_exists', '1', 'L');
else call symputx('_exists', '0', 'L');
quit;
/* ---- already loaded: skip ---------------------------------------------- */
%if &_exists=1 %then %do;
%put NOTE: Table &caslib..&table already loaded - skipping;
%return;
%end;
/* ---- get list of CAS servers ----------------------------------------- */
%let base_uri=%mf_getplatform(VIYARESTAPI);
%let fname1=%mf_getuniquefileref();
%let libref1=%mf_getuniquelibref();
proc http method='GET' out=&fname1 oauth_bearer=sas_services
url="&base_uri/casManagement/servers";
run;
%mp_abort(
iftrue=(&SYS_PROCHTTP_STATUS_CODE ne 200),
msg=%str(&SYS_PROCHTTP_STATUS_CODE &SYS_PROCHTTP_STATUS_PHRASE)
)
libname &libref1 JSON fileref=&fname1;
data _null_;
set &libref1..items;
call symputx(cats('_sv_', _n_), name, 'L');
call symputx('_svcount', _n_, 'L');
run;
libname &libref1 clear;
filename &fname1 clear;
/* ---- find which server owns this session ------------------------------ */
%do i=1 %to &_svcount;
%if "&server"="" %then %do;
%if &mdebug=1 %then %put checking server: &&_sv_&i;
%let ftmp=%mf_getuniquefileref();
proc http method='GET' out=&ftmp oauth_bearer=sas_services
url="&base_uri/casManagement/servers/&&_sv_&i/sessions/&uuid";
run;
%if &SYS_PROCHTTP_STATUS_CODE=200
%then %let server=&&_sv_&i;
filename &ftmp clear;
%end;
%end;
%mp_abort(
iftrue=("&server"=""),
msg=%str(Could not find owning server for CAS session &uuid)
)
%if &mdebug=1 %then %put &=server;
/* ---- discover source file from REST endpoint -------------------------- */
%let fname1=%mf_getuniquefileref();
%let libref1=%mf_getuniquelibref();
proc http method='GET' out=&fname1 oauth_bearer=sas_services
url="&base_uri/casManagement/servers/&server/caslibs/&caslib/tables/&table";
run;
%if &mdebug=1 %then %do;
%put &=SYS_PROCHTTP_STATUS_CODE &=SYS_PROCHTTP_STATUS_PHRASE;
data _null_;
infile &fname1;
input;
putlog _infile_;
run;
%end;
%mp_abort(
iftrue=(&SYS_PROCHTTP_STATUS_CODE=404),
msg=%str(&caslib..&table not found - is a source file registered?)
)
%mp_abort(
iftrue=(&SYS_PROCHTTP_STATUS_CODE ne 200),
msg=%str(&SYS_PROCHTTP_STATUS_CODE &SYS_PROCHTTP_STATUS_PHRASE)
)
libname &libref1 JSON fileref=&fname1;
data _null_;
set &libref1..tablereference;
call symputx('srcfile', sourceTableName, 'L');
call symputx('srccaslib', sourceCaslibName, 'L');
stop;
run;
libname &libref1 clear;
filename &fname1 clear;
%mp_abort(
iftrue=("&srcfile"="" or "&srccaslib"=""),
msg=%str(No sourceTableName/sourceCaslibName for &caslib..&table)
)
%if &mdebug=1 %then %put &=srcfile &=srccaslib;
/* ---- load from discovered source -------------------------------------- */
proc casutil;
load casdata="&srcfile"
incaslib="&srccaslib"
casout="&table"
outcaslib="&caslib"
promote;
quit;
%mp_abort(
iftrue=(&syscc ne 0),
msg=%str(Load failed for &caslib..&table)
)
%put NOTE: Table &caslib..&table loaded and promoted from &srcfile;
/* ---- restore options --------------------------------------------------- */
%if &mdebug=1 %then %do;
options &_sysopts;
%end;
%mend mv_castabload;
+192
View File
@@ -0,0 +1,192 @@
/**
@file mv_castabsave.sas
@brief Saves an in-memory CAS table back to persistent storage
@details Runs in SPRE against an active CAS session. Accepts a
SAS libref, derives the CAS caslib and session UUID from
sashelp.vlibnam, locates the owning CAS server via the
casManagement REST API, then queries the table endpoint to
discover the original source file and saves back to that path.
CASUTIL infers the file type from the output file extension.
A CAS session must already be established by the caller, eg:
cas mysess;
libname mylib cas caslib=Public;
%mv_castabsave(lib=mylib, table=BASEBALL)
@param [in] lib= SAS libref for the CAS caslib
@param [in] table= Name of the in-memory CAS table to save
@param [in] mdebug= (0) Set to 1 to enable verbose logging:
- echoes resolved parameters
- prints HTTP response body
- enables mprint/notes during PROC calls
<h4> SAS Macros </h4>
@li mf_getplatform.sas
@li mf_getuniquefileref.sas
@li mf_getuniquelibref.sas
@li mp_abort.sas
**/
%macro mv_castabsave(
lib=
,table=
,mdebug=0
);
%local _sysopts base_uri caslib uuid server
srcfile srccaslib fname1 libref1 ftmp i _svcount;
%let _sysopts=%sysfunc(getoption(mprint)) %sysfunc(getoption(notes));
/* ---- input validation -------------------------------------------------- */
%mp_abort(
iftrue=("&lib"="" or "&table"=""),
msg=%str(lib= and table= are required)
)
%if &mdebug=1 %then %do;
%put &=lib;
%put &=table;
options mprint notes;
%end;
/* ---- derive caslib and session UUID from sashelp.vlibnam --------------- */
data _null_;
set sashelp.vlibnam(
where=(libname="%upcase(&lib)"
and sysname in ("Caslib","Session UUID"))
);
if sysname="Caslib" then call symputx('caslib',sysvalue,'L');
else call symputx('uuid',sysvalue,'L');
run;
%mp_abort(
iftrue=("&caslib"=""),
msg=%str(&lib is not an assigned CAS libref)
)
%mp_abort(
iftrue=("&uuid"=""),
msg=%str(No session UUID found for libref &lib)
)
%if &mdebug=1 %then %do;
%put &=caslib;
%put &=uuid;
%end;
%let base_uri=%mf_getplatform(VIYARESTAPI);
/* ---- get list of CAS servers ------------------------------------------- */
%let fname1=%mf_getuniquefileref();
%let libref1=%mf_getuniquelibref();
proc http method='GET' out=&fname1 oauth_bearer=sas_services
url="&base_uri/casManagement/servers";
run;
%mp_abort(
iftrue=(&SYS_PROCHTTP_STATUS_CODE ne 200),
msg=%str(&SYS_PROCHTTP_STATUS_CODE &SYS_PROCHTTP_STATUS_PHRASE)
)
libname &libref1 JSON fileref=&fname1;
data _null_;
set &libref1..items;
call symputx(cats('_sv_', _n_), name, 'L');
call symputx('_svcount', _n_, 'L');
run;
libname &libref1 clear;
filename &fname1 clear;
/* ---- find which server owns this session ------------------------------- */
%do i=1 %to &_svcount;
%if "&server"="" %then %do;
%if &mdebug=1 %then %put checking server: &&_sv_&i;
%let ftmp=%mf_getuniquefileref();
proc http method='GET' out=&ftmp oauth_bearer=sas_services
url="&base_uri/casManagement/servers/&&_sv_&i/sessions/&uuid";
run;
%if &SYS_PROCHTTP_STATUS_CODE=200
%then %let server=&&_sv_&i;
filename &ftmp clear;
%end;
%end;
%mp_abort(
iftrue=("&server"=""),
msg=%str(Could not find owning server for CAS session &uuid)
)
%if &mdebug=1 %then %put &=server;
/* ---- discover srcfile from REST endpoint ------------------------------- */
%let fname1=%mf_getuniquefileref();
%let libref1=%mf_getuniquelibref();
proc http method='GET' out=&fname1 oauth_bearer=sas_services
url="&base_uri/casManagement/servers/&server/caslibs/&caslib/tables/&table";
run;
%if &mdebug=1 %then %do;
%put &=SYS_PROCHTTP_STATUS_CODE &=SYS_PROCHTTP_STATUS_PHRASE;
data _null_;
infile &fname1;
input;
putlog _infile_;
run;
%end;
%mp_abort(
iftrue=(&SYS_PROCHTTP_STATUS_CODE=404),
msg=%str(&caslib..&table not found - is it loaded in memory?)
)
%mp_abort(
iftrue=(&SYS_PROCHTTP_STATUS_CODE ne 200),
msg=%str(&SYS_PROCHTTP_STATUS_CODE &SYS_PROCHTTP_STATUS_PHRASE)
)
libname &libref1 JSON fileref=&fname1;
data _null_;
set &libref1..tablereference;
call symputx('srcfile', sourceTableName, 'L');
call symputx('srccaslib', sourceCaslibName, 'L');
stop;
run;
libname &libref1 clear;
filename &fname1 clear;
%mp_abort(
iftrue=("&srcfile"="" or "&srccaslib"=""),
msg=%str(No sourceTableName/sourceCaslibName for &caslib..&table)
)
%if &mdebug=1 %then %put &=srcfile;
/* ---- save to disk ------------------------------------------------------- */
proc casutil;
save casdata="&table"
incaslib="&caslib"
casout="&srcfile"
outcaslib="&srccaslib"
replace;
quit;
%mp_abort(
iftrue=(&syscc ne 0),
msg=%str(Save failed for &caslib..&table)
)
%put NOTE: Table &caslib..&table saved to &srcfile;
/* ---- restore options --------------------------------------------------- */
%if &mdebug=1 %then %do;
options &_sysopts;
%end;
%mend mv_castabsave;
+7 -6
View File
@@ -204,9 +204,10 @@ run;
/* Get Viya file-extension details into some macro variables */ /* Get Viya file-extension details into some macro variables */
%mv_getViyaFileExtParms(&ext %mv_getViyaFileExtParms(&ext
,propertiesVar=viyaProperties ,propertiesVar=viyaProperties
,typeDefNameVar=viyaTypeDefName ,typeDefNameVar=viyaTypeDefName
,mdebug=&mdebug); ,mdebug=&mdebug
)
/* fetch job info */ /* fetch job info */
%local fname1; %local fname1;
@@ -274,9 +275,9 @@ run;
/* If properties were found then patch the file to include them */ /* If properties were found then patch the file to include them */
%if not %mf_isBlank(%superq(viyaProperties)) %then %do; %if not %mf_isBlank(%superq(viyaProperties)) %then %do;
/* Wrap the properties object in a root object also containing the file name */ /* Wrap the properties object in a root object also containing the filename */
%local viyapatch; %local viyapatch;
%let viyapatch = %sysfunc(pathname(work))/%mf_getuniquename(prefix=patch_json_); %let viyapatch=%sysfunc(pathname(work))/%mf_getuniquename(prefix=patch_json_);
data _null_; data _null_;
length line $32767; length line $32767;
file "&viyapatch" lrecl=32767; file "&viyapatch" lrecl=32767;
@@ -296,7 +297,7 @@ run;
run; run;
%end; %end;
/* And apply the properties to the newly created file, using the PATCH method */ /* Apply the properties to the newly created file, using the PATCH method */
%let fref=%mf_getuniquefileref(); %let fref=%mf_getuniquefileref();
filename &fref "&viyapatch"; filename &fref "&viyapatch";
%let url=&base_uri&fileuri; %let url=&base_uri&fileuri;
+31 -24
View File
@@ -23,6 +23,7 @@
@li mf_existds.sas @li mf_existds.sas
@li mf_getplatform.sas @li mf_getplatform.sas
@li mf_getuniquefileref.sas @li mf_getuniquefileref.sas
@li mf_getuniquelibref.sas
@li mf_getuniquename.sas @li mf_getuniquename.sas
@li mf_getvalue.sas @li mf_getvalue.sas
@li mf_getvarlist.sas @li mf_getvarlist.sas
@@ -48,7 +49,7 @@
iftrue=(%mf_isBlank(&ext)) iftrue=(%mf_isBlank(&ext))
,msg=%str(No file extension provided.) ,msg=%str(No file extension provided.)
,mac=MV_GETVIYAFILEEXTPARMS ,mac=MV_GETVIYAFILEEXTPARMS
); )
%mp_abort( %mp_abort(
iftrue=(%mf_isBlank(&typeDefNameVar) and iftrue=(%mf_isBlank(&typeDefNameVar) and
@@ -56,13 +57,13 @@
%mf_isBlank(&mediaTypeVar)) %mf_isBlank(&mediaTypeVar))
,msg=%str(MV_GETVIYAFILEEXTPARMS - No parameter was requested.) ,msg=%str(MV_GETVIYAFILEEXTPARMS - No parameter was requested.)
,mac=MV_GETVIYAFILEEXTPARMS ,mac=MV_GETVIYAFILEEXTPARMS
); )
%mp_abort( %mp_abort(
iftrue=(%mf_isBlank(&viyaFileExtRespLibDs)) iftrue=(%mf_isBlank(&viyaFileExtRespLibDs))
,msg=%str(No <libname.>dataset name provided to cache inital response.) ,msg=%str(No <libname.>dataset name provided to cache inital response.)
,mac=MV_GETVIYAFILEEXTPARMS ,mac=MV_GETVIYAFILEEXTPARMS
); )
/* Declare requested parameters as global macro vars and initialize blank */ /* Declare requested parameters as global macro vars and initialize blank */
%if not %mf_isBlank(&typeDefNameVar) %then %do; %if not %mf_isBlank(&typeDefNameVar) %then %do;
@@ -79,9 +80,7 @@
%end; %end;
%let base_uri=%mf_getplatform(VIYARESTAPI); %let base_uri=%mf_getplatform(VIYARESTAPI);
%if &mdebug=1 %then %do; %if &mdebug=1 %then %put DEBUG: &=base_uri;
%put DEBUG: &=base_uri;
%end;
%let ext=%lowcase(&ext); %let ext=%lowcase(&ext);
@@ -105,7 +104,7 @@
%if (&SYS_PROCHTTP_STATUS_CODE ne 200) %then %do; %if (&SYS_PROCHTTP_STATUS_CODE ne 200) %then %do;
/* To avoid a breaking change, exit early if the request failed. /* To avoid a breaking change, exit early if the request failed.
The calling process will proceed with empty requested macro variables. */ The calling process will proceed with empty macro variables. */
%put INFO: &sysmacroname File extension details were not retrieved.; %put INFO: &sysmacroname File extension details were not retrieved.;
filename &viyatypedefs clear; filename &viyatypedefs clear;
%return; %return;
@@ -126,11 +125,12 @@
/* Convert the content of that JSON into SAS datasets */ /* Convert the content of that JSON into SAS datasets */
/* First prepare a new WORK-based folder to receive the datasets */ /* First prepare a new WORK-based folder to receive the datasets */
%local jsonworkfolder jsonlib opt_dlcreatedir; %local jsonworkfolder jsonlib opt_dlcreatedir;
%let jsonworkfolder=%sysfunc(pathname(work))/%mf_getuniquename(prefix=json_); %let jsonworkfolder=%sysfunc(pathname(work))/%mf_getuniquename(prefix=jsn_);
%let jsonlib=%mf_getuniquelibref(prefix=json); %let jsonlib=%mf_getuniquelibref(prefix=json);
/* And point a libname at it */ /* And point a libname at it */
%let opt_dlcreatedir = %sysfunc(getoption(dlcreatedir)); %let opt_dlcreatedir = %sysfunc(getoption(dlcreatedir));
options dlcreatedir; libname &jsonlib "&jsonworkfolder"; options &opt_dlcreatedir; options dlcreatedir; libname &jsonlib "&jsonworkfolder";
options &opt_dlcreatedir;
/* Read the json output once and copy datasets to its work folder */ /* Read the json output once and copy datasets to its work folder */
%local libref1; %local libref1;
@@ -159,20 +159,22 @@
%end; /* If initial filetype query response didn't exist */ %end; /* If initial filetype query response didn't exist */
/* Find the row-group for the current file extension */ %if &mdebug %then %put DEBUG: Find the row-group for extension &ext;
%local itemRowGroup; %local itemRowGroup;
%let itemRowGroup = data _null_;
%mf_getValue( set &viyaFileExtRespLibDs;
&viyaFileExtRespLibDs where p1='items' and p2='extensions' and value="&ext";
,_viyaItemIdx call symputx('itemRowGroup',_viyaItemIdx,'l');
,filter=%quote(p1='items' and p2='extensions' and value="&ext") %if &mdebug %then %do;
); putlog (_all_)(=);
%end;
run;
%if &mdebug %then %put DEBUG: &=itemRowGroup; %if &mdebug %then %put DEBUG: &=itemRowGroup;
%if %mf_isBlank(&itemRowGroup) %then %do; %if %mf_isBlank(&itemRowGroup) %then %do;
/* extension was not found */ /* extension was not found */
%if(&mdebug=1) %then %put DEBUG: No type details found for extension "&ext".; %if &mdebug %then %put DEBUG: No type details found for extension "&ext";
%return; %return;
%end; %end;
@@ -186,14 +188,17 @@
/* Populate typeDefName, if requested */ /* Populate typeDefName, if requested */
%if (not %mf_isBlank(&typeDefNameVar)) %then %do; %if (not %mf_isBlank(&typeDefNameVar)) %then %do;
%let &typeDefNameVar = %mf_getvalue(&dsItems,value,filter=%quote(p1="items" and p2="name")); %let &typeDefNameVar = %mf_getvalue(
%if &mdebug=1 %then %put DEBUG: &=typeDefNameVar &typeDefNameVar=&&&typeDefNameVar; &dsItems,value,filter=%quote(p1="items" and p2="name"));
%if &mdebug %then
%put DEBUG: &=typeDefNameVar &typeDefNameVar=&&&typeDefNameVar;
%end; %end;
/* Populate mediaType, if requested */ /* Populate mediaType, if requested */
%if (not %mf_isBlank(&mediaTypeVar)) %then %do; %if (not %mf_isBlank(&mediaTypeVar)) %then %do;
%let &mediaTypeVar = %mf_getvalue(&dsItems,value,filter=%quote(p1="items" and p2="mediaType")); %let &mediaTypeVar = %mf_getvalue(
%if &mdebug=1 %then %put DEBUG: &=mediaTypeVar &mediaTypeVar=&&&mediaTypeVar; &dsItems,value,filter=%quote(p1="items" and p2="mediaType"));
%if &mdebug %then %put DEBUG: &=mediaTypeVar &mediaTypeVar=&&&mediaTypeVar;
%end; %end;
/* Populate properties macro variable, if requested */ /* Populate properties macro variable, if requested */
@@ -210,7 +215,8 @@
/* Check for 1+ properties */ /* Check for 1+ properties */
%if ( %mf_nobs(&dsProperties) = 0 ) %then %do; %if ( %mf_nobs(&dsProperties) = 0 ) %then %do;
%let &propertiesVar = %str(); %let &propertiesVar = %str();
%if &mdebug=1 %then %put DEBUG: &SYSMACRONAME - No Viya properties found for file suffix %str(%')&ext%str(%'); %if &mdebug %then %put DEBUG: &SYSMACRONAME - No Viya properties %trim(
)found for file suffix %str(%')&ext%str(%');
%end; %end;
%else %do; %else %do;
/* Properties potentially span multiple rows in the input table */ /* Properties potentially span multiple rows in the input table */
@@ -240,7 +246,8 @@
end; end;
run; run;
%if &mdebug=1 %then %put DEBUG: &=propertiesVar &propertiesVar=&&&propertiesVar; %if &mdebug %then
%put DEBUG: &=propertiesVar &propertiesVar=&&&propertiesVar;
%end; %end;
%end; %end;