mirror of
https://github.com/sasjs/core.git
synced 2026-04-16 21:23:14 +00:00
feat(*): recreate library as scoped package
This commit is contained in:
97
meta/mm_adduser2group.sas
Normal file
97
meta/mm_adduser2group.sas
Normal file
@@ -0,0 +1,97 @@
|
||||
/**
|
||||
@file
|
||||
@brief Adds a user to a group
|
||||
@details Adds a user to a metadata group. The macro first checks whether the
|
||||
user is in that group, and if not, the user is added.
|
||||
|
||||
Usage:
|
||||
|
||||
%mm_adduser2group(user=sasdemo
|
||||
,group=someGroup)
|
||||
|
||||
|
||||
@param user= the user name (not displayname)
|
||||
@param group= the group to which to add the user
|
||||
|
||||
@warning the macro does not check inherited group memberships - it looks at
|
||||
direct members only
|
||||
|
||||
@version 9.3
|
||||
@author Allan Bowe
|
||||
|
||||
**/
|
||||
|
||||
%macro mm_adduser2group(user=
|
||||
,group=
|
||||
,mdebug=0
|
||||
);
|
||||
/* first, check if user is in group already exists */
|
||||
%local check uuri guri;
|
||||
%let check=ok;
|
||||
|
||||
data _null_;
|
||||
length uri type msg $256;
|
||||
call missing(of _all_);
|
||||
rc=metadata_getnobj("omsobj:Person?@Name='&user'",1,uri);
|
||||
if rc<=0 then do;
|
||||
msg="%str(WARN)ING: rc="!!cats(rc)!!" &user not found "!!
|
||||
", or there was an err reading the repository.";
|
||||
call symputx('check',msg);
|
||||
putlog msg;
|
||||
stop;
|
||||
end;
|
||||
call symputx('uuri',scan(uri,2,'\'));
|
||||
|
||||
rc=metadata_getnobj("omsobj:IdentityGroup?@Name='&group'",1,uri);
|
||||
if rc<=0 then do;
|
||||
msg="%str(WARN)ING: rc="!!cats(rc)!!" &group not found "!!
|
||||
", or there was an err reading the repository.";
|
||||
call symputx('check',msg);
|
||||
putlog msg;
|
||||
stop;
|
||||
end;
|
||||
call symputx('guri',scan(uri,2,'\'));
|
||||
|
||||
rc=metadata_getnobj("omsobj:Person?Person[@Name='&user'][IdentityGroups/*[@Name='&group']]",1,uri);
|
||||
if rc=0 then do;
|
||||
msg="%str(WARN)ING: rc="!!cats(rc)!!" &user already in &group";
|
||||
call symputx('check',msg);
|
||||
stop;
|
||||
end;
|
||||
|
||||
if &mdebug ne 0 then put (_all_)(=);
|
||||
run;
|
||||
|
||||
/* stop if issues */
|
||||
%if %quote(&check) ne %quote(ok) %then %do;
|
||||
%put ✓
|
||||
%return;
|
||||
%end;
|
||||
|
||||
%if %length(&syscc) ge 4 %then %do;
|
||||
%put WARNING: SYSCC=&syscc, exiting &sysmacroname;
|
||||
%return;
|
||||
%end;
|
||||
|
||||
|
||||
filename __us2grp temp;
|
||||
|
||||
proc metadata in= "<UpdateMetadata><Reposid>$METAREPOSITORY</Reposid><Metadata>
|
||||
<Person Id='&uuri'><IdentityGroups><IdentityGroup ObjRef='&guri' />
|
||||
</IdentityGroups></Person></Metadata>
|
||||
<NS>SAS</NS><Flags>268435456</Flags></UpdateMetadata>"
|
||||
out=__us2grp verbose;
|
||||
run;
|
||||
|
||||
%if &mdebug ne 0 %then %do;
|
||||
/* write the response to the log for debugging */
|
||||
data _null_;
|
||||
infile __us2grp lrecl=32767;
|
||||
input;
|
||||
put _infile_;
|
||||
run;
|
||||
%end;
|
||||
|
||||
filename __us2grp clear;
|
||||
|
||||
%mend;
|
||||
452
meta/mm_assigndirectlib.sas
Executable file
452
meta/mm_assigndirectlib.sas
Executable file
@@ -0,0 +1,452 @@
|
||||
/**
|
||||
@file
|
||||
@brief Assigns library directly using details from metadata
|
||||
@details Queries metadata to get the libname definition then allocates the
|
||||
library directly (ie, not using the META engine).
|
||||
usage:
|
||||
|
||||
%mm_assignDirectLib(MyLib);
|
||||
data x; set mylib.sometable; run;
|
||||
|
||||
%mm_assignDirectLib(MyDB,open_passthrough=MyAlias);
|
||||
create table MyTable as
|
||||
select * from connection to MyAlias( select * from DBTable);
|
||||
disconnect from MyAlias;
|
||||
quit;
|
||||
|
||||
<h4> Dependencies </h4>
|
||||
@li mf_getengine.sas
|
||||
@li mp_abort.sas
|
||||
|
||||
@param libref the libref (not name) of the metadata library
|
||||
@param open_passthrough= provide an alias to produce the CONNECT TO statement
|
||||
for the relevant external database
|
||||
@param sql_options= an override default output fileref to avoid naming clash
|
||||
@param mDebug= set to 1 to show debug messages in the log
|
||||
@param mAbort= set to 1 to call %mp_abort().
|
||||
|
||||
@returns libname statement
|
||||
|
||||
@version 9.2
|
||||
@author Allan Bowe
|
||||
|
||||
**/
|
||||
|
||||
%macro mm_assigndirectlib(
|
||||
libref /* libref to assign from metadata */
|
||||
,open_passthrough= /* provide an alias to produce the
|
||||
CONNECT TO statement for the
|
||||
relevant external database */
|
||||
,sql_options= /* add any options to add to proc sql statement eg outobs=
|
||||
(only valid for pass through) */
|
||||
,mDebug=0
|
||||
,mAbort=0
|
||||
)/*/STORE SOURCE*/;
|
||||
|
||||
%local mD;
|
||||
%if &mDebug=1 %then %let mD=;
|
||||
%else %let mD=%str(*);
|
||||
%&mD.put Executing mm_assigndirectlib.sas;
|
||||
%&mD.put _local_;
|
||||
|
||||
%if &mAbort=1 %then %let mAbort=;
|
||||
%else %let mAbort=%str(*);
|
||||
|
||||
%&mD.put NOTE: Creating direct (non META) connection to &libref library;
|
||||
|
||||
%local cur_engine;
|
||||
%let cur_engine=%mf_getengine(&libref);
|
||||
%if &cur_engine ne META and &cur_engine ne %then %do;
|
||||
%put NOTE: &libref already has a direct (&cur_engine) libname connection;
|
||||
%return;
|
||||
%end;
|
||||
%else %if %upcase(&libref)=WORK %then %do;
|
||||
%put NOTE: We already have a direct connection to WORK :-) ;
|
||||
%return;
|
||||
%end;
|
||||
|
||||
/* need to determine the library ENGINE first */
|
||||
%local engine;
|
||||
data _null_;
|
||||
length lib_uri engine $256;
|
||||
call missing (of _all_);
|
||||
/* get URI for the particular library */
|
||||
rc1=metadata_getnobj("omsobj:SASLibrary?@Libref ='&libref'",1,lib_uri);
|
||||
/* get the Engine attribute of the previous object */
|
||||
rc2=metadata_getattr(lib_uri,'Engine',engine);
|
||||
putlog "mm_assigndirectlib for &libref:" rc1= lib_uri= rc2= engine=;
|
||||
call symputx("liburi",lib_uri,'l');
|
||||
call symputx("engine",engine,'l');
|
||||
run;
|
||||
|
||||
/* now obtain engine specific connection details */
|
||||
%if &engine=BASE %then %do;
|
||||
%&mD.put NOTE: Retrieving BASE library path;
|
||||
data _null_;
|
||||
length up_uri $256 path cat_path $1024;
|
||||
retain cat_path;
|
||||
call missing (of _all_);
|
||||
/* get all the filepaths of the UsingPackages association */
|
||||
i=1;
|
||||
rc3=metadata_getnasn("&liburi",'UsingPackages',i,up_uri);
|
||||
do while (rc3>0);
|
||||
/* get the DirectoryName attribute of the previous object */
|
||||
rc4=metadata_getattr(up_uri,'DirectoryName',path);
|
||||
if i=1 then path = '("'!!trim(path)!!'" ';
|
||||
else path =' "'!!trim(path)!!'" ';
|
||||
cat_path = trim(cat_path) !! " " !! trim(path) ;
|
||||
i+1;
|
||||
rc3=metadata_getnasn("&liburi",'UsingPackages',i,up_uri);
|
||||
end;
|
||||
cat_path = trim(cat_path) !! ")";
|
||||
&mD.putlog "NOTE: Getting physical path for &libref library";
|
||||
&mD.putlog rc3= up_uri= rc4= cat_path= path=;
|
||||
&mD.putlog "NOTE: Libname cmd will be:";
|
||||
&mD.putlog "libname &libref" cat_path;
|
||||
call symputx("filepath",cat_path,'l');
|
||||
run;
|
||||
|
||||
%if %sysevalf(&sysver<9.4) %then %do;
|
||||
libname &libref &filepath;
|
||||
%end;
|
||||
%else %do;
|
||||
/* apply the new filelocks option to cater for temporary locks */
|
||||
libname &libref &filepath filelockwait=5;
|
||||
%end;
|
||||
|
||||
%end;
|
||||
%else %if &engine=REMOTE %then %do;
|
||||
data x;
|
||||
length rcCon rcProp rc k 3 uriCon uriProp PropertyValue PropertyName Delimiter $256 properties $2048;
|
||||
retain properties;
|
||||
rcCon = metadata_getnasn("&liburi", "LibraryConnection", 1, uriCon);
|
||||
|
||||
rcProp = metadata_getnasn(uriCon, "Properties", 1, uriProp);
|
||||
|
||||
k = 1;
|
||||
rcProp = metadata_getnasn(uriCon, "Properties", k, uriProp);
|
||||
do while (rcProp > 0);
|
||||
rc = metadata_getattr(uriProp , "DefaultValue",PropertyValue);
|
||||
rc = metadata_getattr(uriProp , "PropertyName",PropertyName);
|
||||
rc = metadata_getattr(uriProp , "Delimiter",Delimiter);
|
||||
properties = trim(properties) !! " " !! trim(PropertyName) !! trim(Delimiter) !! trim(PropertyValue);
|
||||
output;
|
||||
k+1;
|
||||
rcProp = metadata_getnasn(uriCon, "Properties", k, uriProp);
|
||||
end;
|
||||
%&mD.put NOTE: Getting properties for REMOTE SHARE &libref library;
|
||||
&mD.put _all_;
|
||||
%&mD.put NOTE: Libname cmd will be:;
|
||||
%&mD.put libname &libref &engine &properties slibref=&libref;
|
||||
call symputx ("properties",trim(properties),'l');
|
||||
run;
|
||||
|
||||
libname &libref &engine &properties slibref=&libref;
|
||||
|
||||
%end;
|
||||
|
||||
%else %if &engine=OLEDB %then %do;
|
||||
%&mD.put NOTE: Retrieving OLEDB connection details;
|
||||
data _null_;
|
||||
length domain datasource provider properties schema
|
||||
connx_uri domain_uri conprop_uri lib_uri schema_uri value $256.;
|
||||
call missing (of _all_);
|
||||
/* get source connection ID */
|
||||
rc=metadata_getnasn("&liburi",'LibraryConnection',1,connx_uri);
|
||||
/* get connection domain */
|
||||
rc1=metadata_getnasn(connx_uri,'Domain',1,domain_uri);
|
||||
rc2=metadata_getattr(domain_uri,'Name',domain);
|
||||
&mD.putlog / 'NOTE: ' // 'NOTE- connection id: ' connx_uri ;
|
||||
&mD.putlog 'NOTE- domain: ' domain;
|
||||
/* get DSN and PROVIDER from connection properties */
|
||||
i=0;
|
||||
do until (rc<0);
|
||||
i+1;
|
||||
rc=metadata_getnasn(connx_uri,'Properties',i,conprop_uri);
|
||||
rc2=metadata_getattr(conprop_uri,'Name',value);
|
||||
if value='Connection.OLE.Property.DATASOURCE.Name.xmlKey.txt' then do;
|
||||
rc3=metadata_getattr(conprop_uri,'DefaultValue',datasource);
|
||||
end;
|
||||
else if value='Connection.OLE.Property.PROVIDER.Name.xmlKey.txt' then do;
|
||||
rc4=metadata_getattr(conprop_uri,'DefaultValue',provider);
|
||||
end;
|
||||
else if value='Connection.OLE.Property.PROPERTIES.Name.xmlKey.txt' then do;
|
||||
rc5=metadata_getattr(conprop_uri,'DefaultValue',properties);
|
||||
end;
|
||||
end;
|
||||
&mD.putlog 'NOTE- dsn/provider/properties: ' /
|
||||
datasource provider properties;
|
||||
&mD.putlog 'NOTE- schema: ' schema // 'NOTE-';
|
||||
|
||||
/* get SCHEMA */
|
||||
rc6=metadata_getnasn("&liburi",'UsingPackages',1,lib_uri);
|
||||
rc7=metadata_getattr(lib_uri,'SchemaName',schema);
|
||||
call symputx('SQL_domain',domain,'l');
|
||||
call symputx('SQL_dsn',datasource,'l');
|
||||
call symputx('SQL_provider',provider,'l');
|
||||
call symputx('SQL_properties',properties,'l');
|
||||
call symputx('SQL_schema',schema,'l');
|
||||
run;
|
||||
|
||||
%if %length(&open_passthrough)>0 %then %do;
|
||||
proc sql &sql_options;
|
||||
connect to OLEDB as &open_passthrough(INSERT_SQL=YES
|
||||
/* need additional properties to make this work */
|
||||
properties=('Integrated Security'=SSPI
|
||||
'Persist Security Info'=True
|
||||
%sysfunc(compress(%str(&SQL_properties),%str(())))
|
||||
)
|
||||
DATASOURCE=&sql_dsn PROMPT=NO
|
||||
PROVIDER=&sql_provider SCHEMA=&sql_schema CONNECTION = GLOBAL);
|
||||
%end;
|
||||
%else %do;
|
||||
LIBNAME &libref OLEDB PROPERTIES=&sql_properties
|
||||
DATASOURCE=&sql_dsn PROVIDER=&sql_provider SCHEMA=&sql_schema
|
||||
%if %length(&sql_domain)>0 %then %do;
|
||||
authdomain="&sql_domain"
|
||||
%end;
|
||||
connection=shared;
|
||||
%end;
|
||||
%end;
|
||||
%else %if &engine=ODBC %then %do;
|
||||
&mD.%put NOTE: Retrieving ODBC connection details;
|
||||
data _null_;
|
||||
length connx_uri conprop_uri value datasource up_uri schema $256.;
|
||||
call missing (of _all_);
|
||||
/* get source connection ID */
|
||||
rc=metadata_getnasn("&liburi",'LibraryConnection',1,connx_uri);
|
||||
/* get connection properties */
|
||||
i=0;
|
||||
do until (rc2<0);
|
||||
i+1;
|
||||
rc2=metadata_getnasn(connx_uri,'Properties',i,conprop_uri);
|
||||
rc3=metadata_getattr(conprop_uri,'Name',value);
|
||||
if value='Connection.ODBC.Property.DATASRC.Name.xmlKey.txt' then do;
|
||||
rc4=metadata_getattr(conprop_uri,'DefaultValue',datasource);
|
||||
rc2=-1;
|
||||
end;
|
||||
end;
|
||||
/* get SCHEMA */
|
||||
rc6=metadata_getnasn("&liburi",'UsingPackages',1,up_uri);
|
||||
rc7=metadata_getattr(up_uri,'SchemaName',schema);
|
||||
&mD.put rc= connx_uri= rc2= conprop_uri= rc3= value= rc4= datasource=
|
||||
rc6= up_uri= rc7= schema=;
|
||||
|
||||
call symputx('SQL_schema',schema,'l');
|
||||
call symputx('SQL_dsn',datasource,'l');
|
||||
run;
|
||||
|
||||
%if %length(&open_passthrough)>0 %then %do;
|
||||
proc sql &sql_options;
|
||||
connect to ODBC as &open_passthrough
|
||||
(INSERT_SQL=YES DATASRC=&sql_dsn. CONNECTION=global);
|
||||
%end;
|
||||
%else %do;
|
||||
libname &libref ODBC DATASRC=&sql_dsn SCHEMA=&sql_schema;
|
||||
%end;
|
||||
%end;
|
||||
%else %if &engine=POSTGRES %then %do;
|
||||
%put NOTE: Obtaining POSTGRES library details;
|
||||
data _null_;
|
||||
length database ignore_read_only_columns direct_exe preserve_col_names
|
||||
preserve_tab_names server schema authdomain user password
|
||||
prop name value uri urisrc $256.;
|
||||
call missing (of _all_);
|
||||
/* get database value */
|
||||
prop='Connection.DBMS.Property.DB.Name.xmlKey.txt';
|
||||
rc=metadata_getprop("&liburi",prop,database,"");
|
||||
if database^='' then database='database='!!quote(trim(database));
|
||||
call symputx('database',database,'l');
|
||||
|
||||
/* get IGNORE_READ_ONLY_COLUMNS value */
|
||||
prop='Library.DBMS.Property.DBIROC.Name.xmlKey.txt';
|
||||
rc=metadata_getprop("&liburi",prop,ignore_read_only_columns,"");
|
||||
if ignore_read_only_columns^='' then ignore_read_only_columns=
|
||||
'ignore_read_only_columns='!!ignore_read_only_columns;
|
||||
call symputx('ignore_read_only_columns',ignore_read_only_columns,'l');
|
||||
|
||||
/* get DIRECT_EXE value */
|
||||
prop='Library.DBMS.Property.DirectExe.Name.xmlKey.txt';
|
||||
rc=metadata_getprop("&liburi",prop,direct_exe,"");
|
||||
if direct_exe^='' then direct_exe='direct_exe='!!direct_exe;
|
||||
call symputx('direct_exe',direct_exe,'l');
|
||||
|
||||
/* get PRESERVE_COL_NAMES value */
|
||||
prop='Library.DBMS.Property.PreserveColNames.Name.xmlKey.txt';
|
||||
rc=metadata_getprop("&liburi",prop,preserve_col_names,"");
|
||||
if preserve_col_names^='' then preserve_col_names=
|
||||
'preserve_col_names='!!preserve_col_names;
|
||||
call symputx('preserve_col_names',preserve_col_names,'l');
|
||||
|
||||
/* get PRESERVE_TAB_NAMES value */
|
||||
/* be careful with PRESERVE_TAB_NAMES=YES - it will mean your table will
|
||||
become case sensitive!! */
|
||||
prop='Library.DBMS.Property.PreserveTabNames.Name.xmlKey.txt';
|
||||
rc=metadata_getprop("&liburi",prop,preserve_tab_names,"");
|
||||
if preserve_tab_names^='' then preserve_tab_names=
|
||||
'preserve_tab_names='!!preserve_tab_names;
|
||||
call symputx('preserve_tab_names',preserve_tab_names,'l');
|
||||
|
||||
/* get SERVER value */
|
||||
if metadata_getnasn("&liburi","LibraryConnection",1,uri)>0 then do;
|
||||
prop='Connection.DBMS.Property.SERVER.Name.xmlKey.txt';
|
||||
rc=metadata_getprop(uri,prop,server,"");
|
||||
end;
|
||||
if server^='' then server='server='!!server;
|
||||
call symputx('server',server,'l');
|
||||
|
||||
/* get SCHEMA value */
|
||||
if metadata_getnasn("&liburi","UsingPackages",1,uri)>0 then do;
|
||||
rc=metadata_getattr(uri,"SchemaName",schema);
|
||||
end;
|
||||
if schema^='' then schema='schema='!!schema;
|
||||
call symputx('schema',schema,'l');
|
||||
|
||||
/* get AUTHDOMAIN value */
|
||||
/* this is only useful if the user account contains that auth domain
|
||||
if metadata_getnasn("&liburi","DefaultLogin",1,uri)>0 then do;
|
||||
rc=metadata_getnasn(uri,"Domain",1,urisrc);
|
||||
rc=metadata_getattr(urisrc,"Name",authdomain);
|
||||
end;
|
||||
if authdomain^='' then authdomain='authdomain='!!quote(trim(authdomain));
|
||||
*/
|
||||
call symputx('authdomain',authdomain,'l');
|
||||
|
||||
/* get user & pass */
|
||||
if authdomain='' & metadata_getnasn("&liburi","DefaultLogin",1,uri)>0 then
|
||||
do;
|
||||
rc=metadata_getattr(uri,"UserID",user);
|
||||
rc=metadata_getattr(uri,"Password",password);
|
||||
end;
|
||||
if user^='' then do;
|
||||
user='user='!!quote(trim(user));
|
||||
password='password='!!quote(trim(password));
|
||||
end;
|
||||
call symputx('user',user,'l');
|
||||
call symputx('password',password,'l');
|
||||
|
||||
&md.put _all_;
|
||||
run;
|
||||
|
||||
%if %length(&open_passthrough)>0 %then %do;
|
||||
%put WARNING: Passthrough option for postgres not yet supported;
|
||||
%return;
|
||||
%end;
|
||||
%else %do;
|
||||
%if &mdebug=1 %then %do;
|
||||
%put NOTE: Executing the following:/;
|
||||
%put NOTE- libname &libref POSTGRES &database &ignore_read_only_columns;
|
||||
%put NOTE- &direct_exe &preserve_col_names &preserve_tab_names;
|
||||
%put NOTE- &server &schema &authdomain &user &password //;
|
||||
%end;
|
||||
libname &libref POSTGRES &database &ignore_read_only_columns &direct_exe
|
||||
&preserve_col_names &preserve_tab_names &server &schema &authdomain
|
||||
&user &password;
|
||||
%end;
|
||||
%end;
|
||||
%else %if &engine=ORACLE %then %do;
|
||||
%put NOTE: Obtaining &engine library details;
|
||||
data _null_;
|
||||
length assocuri1 assocuri2 assocuri3 authdomain path schema $256;
|
||||
call missing (of _all_);
|
||||
|
||||
/* get auth domain */
|
||||
rc=metadata_getnasn("&liburi",'LibraryConnection',1,assocuri1);
|
||||
rc=metadata_getnasn(assocuri1,'Domain',1,assocuri2);
|
||||
rc=metadata_getattr(assocuri2,"Name",authdomain);
|
||||
call symputx('authdomain',authdomain,'l');
|
||||
|
||||
/* path */
|
||||
rc=metadata_getprop(assocuri1,'Connection.Oracle.Property.PATH.Name.xmlKey.txt',path);
|
||||
call symputx('path',path,'l');
|
||||
|
||||
/* schema */
|
||||
rc=metadata_getnasn("&liburi",'UsingPackages',1,assocuri3);
|
||||
rc=metadata_getattr(assocuri3,'SchemaName',schema);
|
||||
call symputx('schema',schema,'l');
|
||||
run;
|
||||
%put NOTE: Executing the following:/; %put NOTE-;
|
||||
%put NOTE- libname &libref ORACLE path=&path schema=&schema authdomain=&authdomain;
|
||||
%put NOTE-;
|
||||
libname &libref ORACLE path=&path schema=&schema authdomain=&authdomain;
|
||||
%end;
|
||||
%else %if &engine=SQLSVR %then %do;
|
||||
%put NOTE: Obtaining &engine library details;
|
||||
data _null;
|
||||
length assocuri1 assocuri2 assocuri3 authdomain path schema userid passwd $256;
|
||||
call missing (of _all_);
|
||||
|
||||
rc=metadata_getnasn("&liburi",'DefaultLogin',1,assocuri1);
|
||||
rc=metadata_getattr(assocuri1,"UserID",userid);
|
||||
rc=metadata_getattr(assocuri1,"Password",passwd);
|
||||
call symputx('user',userid,'l');
|
||||
call symputx('pass',passwd,'l');
|
||||
|
||||
/* path */
|
||||
rc=metadata_getnasn("&liburi",'LibraryConnection',1,assocuri2);
|
||||
rc=metadata_getprop(assocuri2,'Connection.SQL.Property.Datasrc.Name.xmlKey.txt',path);
|
||||
call symputx('path',path,'l');
|
||||
|
||||
/* schema */
|
||||
rc=metadata_getnasn("&liburi",'UsingPackages',1,assocuri3);
|
||||
rc=metadata_getattr(assocuri3,'SchemaName',schema);
|
||||
call symputx('schema',schema,'l');
|
||||
run;
|
||||
|
||||
%put NOTE: Executing the following:/; %put NOTE-;
|
||||
%put NOTE- libname &libref SQLSVR datasrc=&path schema=&schema user="&user" pass="XXX";
|
||||
%put NOTE-;
|
||||
|
||||
libname &libref SQLSVR datasrc=&path schema=&schema user="&user" pass="&pass" ;
|
||||
%end;
|
||||
%else %if &engine=TERADATA %then %do;
|
||||
%put NOTE: Obtaining &engine library details;
|
||||
data _null;
|
||||
length assocuri1 assocuri2 assocuri3 authdomain path schema userid passwd $256;
|
||||
call missing (of _all_);
|
||||
|
||||
/* get auth domain */
|
||||
rc=metadata_getnasn("&liburi",'LibraryConnection',1,assocuri1);
|
||||
rc=metadata_getnasn(assocuri1,'Domain',1,assocuri2);
|
||||
rc=metadata_getattr(assocuri2,"Name",authdomain);
|
||||
call symputx('authdomain',authdomain,'l');
|
||||
|
||||
/*
|
||||
rc=metadata_getnasn("&liburi",'DefaultLogin',1,assocuri1);
|
||||
rc=metadata_getattr(assocuri1,"UserID",userid);
|
||||
rc=metadata_getattr(assocuri1,"Password",passwd);
|
||||
call symputx('user',userid,'l');
|
||||
call symputx('pass',passwd,'l');
|
||||
*/
|
||||
|
||||
/* path */
|
||||
rc=metadata_getnasn("&liburi",'LibraryConnection',1,assocuri2);
|
||||
rc=metadata_getprop(assocuri2,'Connection.Teradata.Property.SERVER.Name.xmlKey.txt',path);
|
||||
call symputx('path',path,'l');
|
||||
|
||||
/* schema */
|
||||
rc=metadata_getnasn("&liburi",'UsingPackages',1,assocuri3);
|
||||
rc=metadata_getattr(assocuri3,'SchemaName',schema);
|
||||
call symputx('schema',schema,'l');
|
||||
run;
|
||||
|
||||
%put NOTE: Executing the following:/; %put NOTE-;
|
||||
%put NOTE- libname &libref TERADATA server=&path schema=&schema authdomain=&authdomain;
|
||||
%put NOTE-;
|
||||
|
||||
libname &libref TERADATA server=&path schema=&schema authdomain=&authdomain;
|
||||
%end;
|
||||
%else %if &engine= %then %do;
|
||||
%put NOTE: Libref &libref is not registered in metadata;
|
||||
%&mAbort.mp_abort(
|
||||
msg=%str(ERR)OR: Libref &libref is not registered in metadata
|
||||
,mac=mm_assigndirectlib.sas);
|
||||
%return;
|
||||
%end;
|
||||
%else %do;
|
||||
%put WARNING: Engine &engine is currently unsupported;
|
||||
%put WARNING- Please contact your support team.;
|
||||
%return;
|
||||
%end;
|
||||
|
||||
%mend;
|
||||
76
meta/mm_assignlib.sas
Executable file
76
meta/mm_assignlib.sas
Executable file
@@ -0,0 +1,76 @@
|
||||
/**
|
||||
@file
|
||||
@brief Assigns a meta engine library using LIBREF
|
||||
@details Queries metadata to get the library NAME which can then be used in
|
||||
a libname statement with the meta engine.
|
||||
|
||||
usage:
|
||||
|
||||
%macro mp_abort(iftrue,mac,msg);%put &=msg;%mend;
|
||||
|
||||
%mm_assignlib(SOMEREF)
|
||||
|
||||
<h4> Dependencies </h4>
|
||||
@li mp_abort.sas
|
||||
|
||||
@param libref the libref (not name) of the metadata library
|
||||
@param mAbort= If not assigned, HARD will call %mp_abort(), SOFT will silently return
|
||||
|
||||
@returns libname statement
|
||||
|
||||
@version 9.2
|
||||
@author Allan Bowe
|
||||
|
||||
**/
|
||||
|
||||
%macro mm_assignlib(
|
||||
libref
|
||||
,mAbort=HARD
|
||||
)/*/STORE SOURCE*/;
|
||||
|
||||
%if %sysfunc(libref(&libref)) %then %do;
|
||||
%local mp_abort msg; %let mp_abort=0;
|
||||
data _null_;
|
||||
length liburi LibName $200;
|
||||
call missing(of _all_);
|
||||
nobj=metadata_getnobj("omsobj:SASLibrary?@Libref='&libref'",1,liburi);
|
||||
if nobj=1 then do;
|
||||
rc=metadata_getattr(liburi,"Name",LibName);
|
||||
/* now try and assign it */
|
||||
if libname("&libref",,'meta',cats('liburi="',liburi,'";')) ne 0 then do;
|
||||
call symputx('msg',sysmsg(),'l');
|
||||
if "&mabort"='HARD' then call symputx('mp_abort',1,'l');
|
||||
end;
|
||||
else do;
|
||||
put (_all_)(=);
|
||||
call symputx('libname',libname,'L');
|
||||
call symputx('liburi',liburi,'L');
|
||||
end;
|
||||
end;
|
||||
else if nobj>1 then do;
|
||||
if "&mabort"='HARD' then call symputx('mp_abort',1);
|
||||
call symputx('msg',"More than one library with libref=&libref");
|
||||
end;
|
||||
else do;
|
||||
if "&mabort"='HARD' then call symputx('mp_abort',1);
|
||||
call symputx('msg',"Library &libref not found in metadata");
|
||||
end;
|
||||
run;
|
||||
|
||||
%if &mp_abort=1 %then %do;
|
||||
%mp_abort(iftrue= (&mp_abort=1)
|
||||
,mac=&sysmacroname
|
||||
,msg=&msg
|
||||
)
|
||||
%return;
|
||||
%end;
|
||||
%else %if %length(&msg)>2 %then %do;
|
||||
%put NOTE: &msg;
|
||||
%return;
|
||||
%end;
|
||||
|
||||
%end;
|
||||
%else %do;
|
||||
%put NOTE: Library &libref is already assigned;
|
||||
%end;
|
||||
%mend;
|
||||
153
meta/mm_createapplication.sas
Normal file
153
meta/mm_createapplication.sas
Normal file
@@ -0,0 +1,153 @@
|
||||
/**
|
||||
@file
|
||||
@brief Create an Application object in a metadata folder
|
||||
@details Application objects are useful for storing properties in metadata.
|
||||
This macro is idempotent - it will not create an object with the same name
|
||||
in the same location, twice.
|
||||
|
||||
usage:
|
||||
|
||||
%mm_createapplication(tree=/User Folders/sasdemo
|
||||
,name=MyApp
|
||||
,classidentifier=myAppSeries
|
||||
,params= name1=value1
name2=value2
emptyvalue=
|
||||
)
|
||||
|
||||
@warning application components do not get deleted when removing the container folder! be sure you have the administrative priviliges to remove this kind of metadata from the SMC plugin (or be ready to do to so programmatically).
|
||||
|
||||
<h4> Dependencies </h4>
|
||||
@li mp_abort.sas
|
||||
@li mf_verifymacvars.sas
|
||||
|
||||
@param tree= The metadata folder uri, or the metadata path, in which to
|
||||
create the object. This must exist.
|
||||
@param name= Application object name. Avoid spaces.
|
||||
@param ClassIdentifier= the class of applications to which this app belongs
|
||||
@param params= name=value pairs which will become public properties of the
|
||||
application object. These are delimited using 
 (newline character)
|
||||
|
||||
@param desc= Application description (optional). Avoid ampersands as these
|
||||
are illegal characters (unless they are escapted- eg &)
|
||||
@param version= version number of application
|
||||
@param frefin= fileref to use (enables change if there is a conflict). The
|
||||
filerefs are left open, to enable inspection after running the
|
||||
macro (or importing into an xmlmap if needed).
|
||||
@param frefout= fileref to use (enables change if there is a conflict)
|
||||
@param mDebug= set to 1 to show debug messages in the log
|
||||
|
||||
@author Allan Bowe
|
||||
|
||||
**/
|
||||
|
||||
%macro mm_createapplication(
|
||||
tree=/User Folders/sasdemo
|
||||
,name=myApp
|
||||
,ClassIdentifier=mcore
|
||||
,desc=Created by mm_createapplication
|
||||
,params= param1=1
param2=blah
|
||||
,version=
|
||||
,frefin=mm_in
|
||||
,frefout=mm_out
|
||||
,mDebug=1
|
||||
);
|
||||
|
||||
%local mD;
|
||||
%if &mDebug=1 %then %let mD=;
|
||||
%else %let mD=%str(*);
|
||||
%&mD.put Executing &sysmacroname..sas;
|
||||
%&mD.put _local_;
|
||||
|
||||
%mf_verifymacvars(tree name)
|
||||
|
||||
/**
|
||||
* check tree exists
|
||||
*/
|
||||
|
||||
data _null_;
|
||||
length type uri $256;
|
||||
rc=metadata_pathobj("","&tree","Folder",type,uri);
|
||||
call symputx('type',type,'l');
|
||||
call symputx('treeuri',uri,'l');
|
||||
run;
|
||||
|
||||
%mp_abort(
|
||||
iftrue= (&type ne Tree)
|
||||
,mac=mm_createapplication.sas
|
||||
,msg=Tree &tree does not exist!
|
||||
)
|
||||
|
||||
/**
|
||||
* Check object does not exist already
|
||||
*/
|
||||
data _null_;
|
||||
length type uri $256;
|
||||
rc=metadata_pathobj("","&tree/&name","Application",type,uri);
|
||||
call symputx('type',type,'l');
|
||||
putlog (_all_)(=);
|
||||
run;
|
||||
|
||||
%mp_abort(
|
||||
iftrue= (&type = SoftwareComponent)
|
||||
,mac=mm_createapplication.sas
|
||||
,msg=Application &name already exists in &tree!
|
||||
)
|
||||
|
||||
|
||||
/**
|
||||
* Now we can create the application
|
||||
*/
|
||||
filename &frefin temp;
|
||||
|
||||
/* write header XML */
|
||||
data _null_;
|
||||
file &frefin;
|
||||
name=quote(symget('name'));
|
||||
desc=quote(symget('desc'));
|
||||
ClassIdentifier=quote(symget('ClassIdentifier'));
|
||||
version=quote(symget('version'));
|
||||
params=quote(symget('params'));
|
||||
treeuri=quote(symget('treeuri'));
|
||||
|
||||
put "<AddMetadata><Reposid>$METAREPOSITORY</Reposid><Metadata> "/
|
||||
'<SoftwareComponent IsHidden="0" Name=' name ' ProductName=' name /
|
||||
' ClassIdentifier=' ClassIdentifier ' Desc=' desc /
|
||||
' SoftwareVersion=' version ' SpecVersion=' version /
|
||||
' Major="1" Minor="1" UsageVersion="1000000" PublicType="Application" >' /
|
||||
' <Notes>' /
|
||||
' <TextStore Name="Public Configuration Properties" IsHidden="0" ' /
|
||||
' UsageVersion="0" StoredText=' params '/>' /
|
||||
' </Notes>' /
|
||||
"<Trees><Tree ObjRef=" treeuri "/></Trees>"/
|
||||
"</SoftwareComponent></Metadata><NS>SAS</NS>"/
|
||||
"<Flags>268435456</Flags></AddMetadata>";
|
||||
run;
|
||||
|
||||
filename &frefout temp;
|
||||
|
||||
proc metadata in= &frefin out=&frefout verbose;
|
||||
run;
|
||||
|
||||
%if &mdebug=1 %then %do;
|
||||
/* write the response to the log for debugging */
|
||||
data _null_;
|
||||
infile &frefout lrecl=1048576;
|
||||
input;
|
||||
put _infile_;
|
||||
run;
|
||||
%end;
|
||||
|
||||
%put NOTE: Checking to ensure application (&name) was created;
|
||||
data _null_;
|
||||
length type uri $256;
|
||||
rc=metadata_pathobj("","&tree/&name","Application",type,uri);
|
||||
call symputx('apptype',type,'l');
|
||||
%if &mdebug=1 %then putlog (_all_)(=);;
|
||||
run;
|
||||
%if &apptype ne SoftwareComponent %then %do;
|
||||
%put %str(ERR)OR: Could not find (&name) at (&tree)!!;
|
||||
%return;
|
||||
%end;
|
||||
%else %put NOTE: Application (&name) successfully created in (&tree)!;
|
||||
|
||||
|
||||
%mend;
|
||||
83
meta/mm_createdataset.sas
Normal file
83
meta/mm_createdataset.sas
Normal file
@@ -0,0 +1,83 @@
|
||||
/**
|
||||
@file mm_createdataset.sas
|
||||
@brief Create a dataset from a metadata definition
|
||||
@details This macro was built to support viewing empty tables in
|
||||
https://datacontroller.io - a free evaluation copy is available by
|
||||
contacting the author (Allan Bowe).
|
||||
|
||||
The table can be retrieved using LIBRARY.DATASET reference, or directly
|
||||
using the metadata URI.
|
||||
|
||||
The dataset is written to the WORK library.
|
||||
|
||||
usage:
|
||||
|
||||
%mm_createdataset(libds=metlib.some_dataset)
|
||||
|
||||
or
|
||||
|
||||
%mm_createdataset(tableuri=G5X8AFW1.BE00015Y)
|
||||
|
||||
<h4> Dependencies </h4>
|
||||
@li mm_getlibs.sas
|
||||
@li mm_gettables.sas
|
||||
@li mm_getcols.sas
|
||||
|
||||
@param libds= library.dataset metadata source. Note - table names in metadata
|
||||
can be longer than 32 chars (just fyi, not an issue here)
|
||||
@param tableuri= Metadata URI of the table to be created
|
||||
@param outds= The dataset to create, default is `work.mm_createdataset`.
|
||||
The table name needs to be 32 chars or less as per SAS naming rules.
|
||||
@param mdebug= set DBG to 1 to disable DEBUG messages
|
||||
|
||||
@version 9.4
|
||||
@author Allan Bowe
|
||||
|
||||
**/
|
||||
|
||||
%macro mm_createdataset(libds=,tableuri=,outds=work.mm_createdataset,mDebug=0);
|
||||
%local dbg errorcheck tempds1 tempds2 tempds3;
|
||||
%if &mDebug=0 %then %let dbg=*;
|
||||
%let errorcheck=1;
|
||||
|
||||
%if %index(&libds,.)>0 %then %do;
|
||||
/* get lib uri */
|
||||
data;run;%let tempds1=&syslast;
|
||||
%mm_getlibs(outds=&tempds1)
|
||||
data _null_;
|
||||
set &tempds1;
|
||||
if upcase(libraryref)="%upcase(%scan(&libds,1,.))";
|
||||
call symputx('liburi',LibraryId,'l');
|
||||
run;
|
||||
/* get ds uri */
|
||||
data;run;%let tempds2=&syslast;
|
||||
%mm_gettables(uri=&liburi,outds=&tempds2)
|
||||
data _null_;
|
||||
set &tempds2;
|
||||
if upcase(tablename)="%upcase(%scan(&libds,2,.))";
|
||||
call symputx('tableuri',tableuri);
|
||||
run;
|
||||
%end;
|
||||
|
||||
data;run;%let tempds3=&syslast;
|
||||
%mm_getcols(tableuri=&tableuri,outds=&tempds3)
|
||||
|
||||
data _null_;
|
||||
set &tempds3 end=last;
|
||||
if _n_=1 then call execute('data &outds;');
|
||||
length attrib $32767;
|
||||
|
||||
if SAScolumntype='C' then type='$';
|
||||
attrib='attrib '!!cats(colname)!!' length='!!cats(type,SASColumnLength,'.');
|
||||
|
||||
if not missing(sasformat) then fmt=' format='!!cats(sasformat);
|
||||
if not missing(sasinformat) then infmt=' informat='!!cats(sasinformat);
|
||||
if not missing(coldesc) then desc=' label='!!quote(cats(coldesc));
|
||||
|
||||
attrib=trim(attrib)!!fmt!!infmt!!desc!!';';
|
||||
|
||||
call execute(attrib);
|
||||
if last then call execute('call missing(of _all_);stop;run;');
|
||||
run;
|
||||
|
||||
%mend;
|
||||
125
meta/mm_createdocument.sas
Normal file
125
meta/mm_createdocument.sas
Normal file
@@ -0,0 +1,125 @@
|
||||
/**
|
||||
@file
|
||||
@brief Create a Document object in a metadata folder
|
||||
@details Document objects are useful for storing properties in metadata.
|
||||
This macro is idempotent - it will not create an object with the same name
|
||||
in the same location, twice.
|
||||
Note - the filerefs are left open, to enable inspection after running the
|
||||
macro (or importing into an xmlmap if needed).
|
||||
|
||||
usage:
|
||||
|
||||
%mm_createdocument(tree=/User Folders/sasdemo
|
||||
,name=MyNote)
|
||||
|
||||
<h4> Dependencies </h4>
|
||||
@li mp_abort.sas
|
||||
@li mf_verifymacvars.sas
|
||||
|
||||
|
||||
@param tree= The metadata folder uri, or the metadata path, in which to
|
||||
create the document. This must exist.
|
||||
@param name= Document object name. Avoid spaces.
|
||||
|
||||
@param desc= Document description (optional)
|
||||
@param textrole= TextRole property (optional)
|
||||
@param frefin= fileref to use (enables change if there is a conflict)
|
||||
@param frefout= fileref to use (enables change if there is a conflict)
|
||||
@param mDebug= set to 1 to show debug messages in the log
|
||||
|
||||
@author Allan Bowe
|
||||
|
||||
**/
|
||||
|
||||
%macro mm_createdocument(
|
||||
tree=/User Folders/sasdemo
|
||||
,name=myNote
|
||||
,desc=Created by &sysmacroname
|
||||
,textrole=
|
||||
,frefin=mm_in
|
||||
,frefout=mm_out
|
||||
,mDebug=1
|
||||
);
|
||||
|
||||
%local mD;
|
||||
%if &mDebug=1 %then %let mD=;
|
||||
%else %let mD=%str(*);
|
||||
%&mD.put Executing &sysmacroname..sas;
|
||||
%&mD.put _local_;
|
||||
|
||||
%mf_verifymacvars(tree name)
|
||||
|
||||
/**
|
||||
* check tree exists
|
||||
*/
|
||||
|
||||
data _null_;
|
||||
length type uri $256;
|
||||
rc=metadata_pathobj("","&tree","Folder",type,uri);
|
||||
call symputx('type',type,'l');
|
||||
call symputx('treeuri',uri,'l');
|
||||
run;
|
||||
|
||||
%mp_abort(
|
||||
iftrue= (&type ne Tree)
|
||||
,mac=mm_createdocument.sas
|
||||
,msg=Tree &tree does not exist!
|
||||
)
|
||||
|
||||
/**
|
||||
* Check object does not exist already
|
||||
*/
|
||||
data _null_;
|
||||
length type uri $256;
|
||||
rc=metadata_pathobj("","&tree/&name","Note",type,uri);
|
||||
call symputx('type',type,'l');
|
||||
call symputx('docuri',uri,'l');
|
||||
putlog (_all_)(=);
|
||||
run;
|
||||
|
||||
%if &type = Document %then %do;
|
||||
%put Document &name already exists in &tree!;
|
||||
%return;
|
||||
%end;
|
||||
|
||||
/**
|
||||
* Now we can create the document
|
||||
*/
|
||||
filename &frefin temp;
|
||||
|
||||
/* write header XML */
|
||||
data _null_;
|
||||
file &frefin;
|
||||
name=quote("&name");
|
||||
desc=quote("&desc");
|
||||
textrole=quote("&textrole");
|
||||
treeuri=quote("&treeuri");
|
||||
|
||||
put "<AddMetadata><Reposid>$METAREPOSITORY</Reposid>"/
|
||||
'<Metadata><Document IsHidden="0" PublicType="Note" UsageVersion="1000000"'/
|
||||
" Name=" name " desc=" desc " TextRole=" textrole ">"/
|
||||
"<Notes> "/
|
||||
' <TextStore IsHidden="0" Name=' name ' UsageVersion="0" '/
|
||||
' TextRole="SourceCode" StoredText="hello world" />' /
|
||||
'</Notes>'/
|
||||
/*URI="Document for public note" */
|
||||
"<Trees><Tree ObjRef=" treeuri "/></Trees>"/
|
||||
"</Document></Metadata><NS>SAS</NS>"/
|
||||
"<Flags>268435456</Flags></AddMetadata>";
|
||||
run;
|
||||
|
||||
filename &frefout temp;
|
||||
|
||||
proc metadata in= &frefin out=&frefout verbose;
|
||||
run;
|
||||
|
||||
%if &mdebug=1 %then %do;
|
||||
/* write the response to the log for debugging */
|
||||
data _null_;
|
||||
infile &frefout lrecl=1048576;
|
||||
input;
|
||||
put _infile_;
|
||||
run;
|
||||
%end;
|
||||
|
||||
%mend;
|
||||
159
meta/mm_createfolder.sas
Normal file
159
meta/mm_createfolder.sas
Normal file
@@ -0,0 +1,159 @@
|
||||
/**
|
||||
@file
|
||||
@brief Recursively create a metadata folder
|
||||
@details This macro was inspired by Paul Homes who wrote an early
|
||||
version (mkdirmd.sas) in 2010. The original is described here:
|
||||
https://platformadmin.com/blogs/paul/2010/07/mkdirmd/
|
||||
|
||||
The macro will NOT create a new ROOT folder - not
|
||||
because it can't, but more because that is generally not something
|
||||
your administrator would like you to do!
|
||||
|
||||
The macro is idempotent - if you run it twice, it will only create a folder
|
||||
once.
|
||||
|
||||
usage:
|
||||
|
||||
%mm_createfolder(path=/some/meta/folder)
|
||||
|
||||
@param path= Name of the folder to create.
|
||||
@param mdebug= set DBG to 1 to disable DEBUG messages
|
||||
|
||||
@version 9.4
|
||||
@author Allan Bowe
|
||||
|
||||
**/
|
||||
|
||||
%macro mm_createfolder(path=,mDebug=0);
|
||||
%put &sysmacroname: execution started for &path;
|
||||
%local dbg errorcheck;
|
||||
%if &mDebug=0 %then %let dbg=*;
|
||||
|
||||
%local parentFolderObjId child errorcheck paths;
|
||||
%let paths=0;
|
||||
%let errorcheck=1;
|
||||
|
||||
%if &syscc ge 4 %then %do;
|
||||
%put SYSCC=&syscc - this macro requires a clean session;
|
||||
%return;
|
||||
%end;
|
||||
|
||||
data _null_;
|
||||
length objId parentId objType parent child $200
|
||||
folderPath $1000;
|
||||
call missing (of _all_);
|
||||
folderPath = "%trim(&path)";
|
||||
|
||||
* remove any trailing slash ;
|
||||
if ( substr(folderPath,length(folderPath),1) = '/' ) then
|
||||
folderPath=substr(folderPath,1,length(folderPath)-1);
|
||||
|
||||
* name must not be blank;
|
||||
if ( folderPath = '' ) then do;
|
||||
put "%str(ERR)OR: &sysmacroname PATH parameter value must be non-blank";
|
||||
end;
|
||||
|
||||
* must have a starting slash ;
|
||||
if ( substr(folderPath,1,1) ne '/' ) then do;
|
||||
put "%str(ERR)OR: &sysmacroname PATH parameter value must have starting slash";
|
||||
stop;
|
||||
end;
|
||||
|
||||
* check if folder already exists ;
|
||||
rc=metadata_pathobj('',cats(folderPath,"(Folder)"),"",objType,objId);
|
||||
if rc ge 1 then do;
|
||||
put "NOTE: Folder " folderPath " already exists!";
|
||||
stop;
|
||||
end;
|
||||
|
||||
* do not create a root (one level) folder ;
|
||||
if countc(folderPath,'/')=1 then do;
|
||||
put "%str(ERR)OR: &sysmacroname will not create a new ROOT folder";
|
||||
stop;
|
||||
end;
|
||||
|
||||
* check that root folder exists ;
|
||||
root=cats('/',scan(folderpath,1,'/'),"(Folder)");
|
||||
if metadata_pathobj('',root,"",objType,parentId)<1 then do;
|
||||
put "%str(ERR)OR: " root " does not exist!";
|
||||
stop;
|
||||
end;
|
||||
|
||||
* check that parent folder exists ;
|
||||
child=scan(folderPath,-1,'/');
|
||||
parent=substr(folderpath,1,length(folderpath)-length(child)-1);
|
||||
rc=metadata_pathobj('',cats(parent,"(Folder)"),"",objType,parentId);
|
||||
if rc<1 then do;
|
||||
putlog 'The following folders will be created:';
|
||||
/* folder does not exist - so start from top and work down */
|
||||
length newpath $1000;
|
||||
paths=0;
|
||||
do x=2 to countw(folderpath,'/');
|
||||
newpath='';
|
||||
do i=1 to x;
|
||||
newpath=cats(newpath,'/',scan(folderpath,i,'/'));
|
||||
end;
|
||||
rc=metadata_pathobj('',cats(newpath,"(Folder)"),"",objType,parentId);
|
||||
if rc<1 then do;
|
||||
paths+1;
|
||||
call symputx(cats('path',paths),newpath);
|
||||
putlog newpath;
|
||||
end;
|
||||
call symputx('paths',paths);
|
||||
end;
|
||||
end;
|
||||
else putlog "parent " parent " exists";
|
||||
|
||||
call symputx('parentFolderObjId',parentId,'l');
|
||||
call symputx('child',child,'l');
|
||||
call symputx('errorcheck',0,'l');
|
||||
|
||||
&dbg put (_all_)(=);
|
||||
run;
|
||||
|
||||
%if &errorcheck=1 or &syscc ge 4 %then %return;
|
||||
|
||||
%if &paths>0 %then %do x=1 %to &paths;
|
||||
%put executing recursive call for &&path&x;
|
||||
%mm_createfolder(path=&&path&x)
|
||||
%end;
|
||||
%else %do;
|
||||
filename __newdir temp;
|
||||
options noquotelenmax;
|
||||
%local inmeta;
|
||||
%put creating: &path;
|
||||
%let inmeta=<AddMetadata><Reposid>$METAREPOSITORY</Reposid><Metadata>
|
||||
<Tree Name='&child' PublicType='Folder' TreeType='BIP Folder' UsageVersion='1000000'>
|
||||
<ParentTree><Tree ObjRef='&parentFolderObjId'/></ParentTree></Tree></Metadata>
|
||||
<NS>SAS</NS><Flags>268435456</Flags></AddMetadata>;
|
||||
|
||||
proc metadata in="&inmeta" out=__newdir verbose;
|
||||
run ;
|
||||
|
||||
/* check it was successful */
|
||||
data _null_;
|
||||
length objId parentId objType parent child $200 ;
|
||||
call missing (of _all_);
|
||||
rc=metadata_pathobj('',cats("&path","(Folder)"),"",objType,objId);
|
||||
if rc ge 1 then do;
|
||||
putlog "SUCCCESS! &path created.";
|
||||
end;
|
||||
else do;
|
||||
putlog "%str(ERR)OR: unsuccessful attempt to create &path";
|
||||
call symputx('syscc',8);
|
||||
end;
|
||||
run;
|
||||
|
||||
/* write the response to the log for debugging */
|
||||
%if &mDebug ne 0 %then %do;
|
||||
data _null_;
|
||||
infile __newdir lrecl=32767;
|
||||
input;
|
||||
put _infile_;
|
||||
run;
|
||||
%end;
|
||||
filename __newdir clear;
|
||||
%end;
|
||||
|
||||
%put &sysmacroname: execution finished for &path;
|
||||
%mend;
|
||||
321
meta/mm_createlibrary.sas
Normal file
321
meta/mm_createlibrary.sas
Normal file
@@ -0,0 +1,321 @@
|
||||
/**
|
||||
@file
|
||||
@brief Create a SAS Library
|
||||
@details Currently only supports BASE engine
|
||||
|
||||
This macro is idempotent - if you run it twice (for the same libref or
|
||||
libname), it will only create one library. There is a dependency on other
|
||||
macros in this library - they should be installed as a suite (see README).
|
||||
|
||||
Usage:
|
||||
|
||||
%mm_createlibrary(
|
||||
libname=My New Library
|
||||
,libref=mynewlib
|
||||
,libdesc=Super & <fine>
|
||||
,engine=BASE
|
||||
,tree=/User Folders/sasdemo
|
||||
,servercontext=SASApp
|
||||
,directory=/tmp/tests
|
||||
,mDebug=1)
|
||||
|
||||
<h4> Dependencies </h4>
|
||||
@li mf_verifymacvars.sas
|
||||
@li mm_createfolder.sas
|
||||
|
||||
|
||||
@param libname= Library name (as displayed to user, 256 chars). Duplicates
|
||||
are not created (case sensitive).
|
||||
@param libref= Library libref (8 chars). Duplicate librefs are not created,
|
||||
HOWEVER- the check is not case sensitive - if *libref* exists, *LIBREF*
|
||||
will still be created. Librefs created will always be uppercased.
|
||||
@param engine= Library engine (currently only BASE supported)
|
||||
@param tree= The metadata folder uri, or the metadata path, in which to
|
||||
create the library.
|
||||
@param servercontext= The SAS server against which the library is registered.
|
||||
@param IsPreassigned= set to 1 if the library should be pre-assigned.
|
||||
|
||||
@param libdesc= Library description (optional)
|
||||
@param directory= Required for the BASE engine. The metadata directory objects
|
||||
are searched to find an existing one with a matching physical path.
|
||||
If more than one uri found with that path, then the first one will be used.
|
||||
If no URI is found, a new directory object will be created. The physical
|
||||
path will also be created, if it doesn't exist.
|
||||
|
||||
|
||||
@param mDebug= set to 1 to show debug messages in the log
|
||||
@param frefin= fileref to use (enables change if there is a conflict). The
|
||||
filerefs are left open, to enable inspection after running the
|
||||
macro (or importing into an xmlmap if needed).
|
||||
@param frefout= fileref to use (enables change if there is a conflict)
|
||||
|
||||
|
||||
@version 9.3
|
||||
@author Allan Bowe
|
||||
|
||||
**/
|
||||
|
||||
%macro mm_createlibrary(
|
||||
libname=My New Library
|
||||
,libref=mynewlib
|
||||
,libdesc=Created automatically using the mm_createlibrary macro
|
||||
,engine=BASE
|
||||
,tree=/User Folders/sasdemo
|
||||
,servercontext=SASApp
|
||||
,directory=/tmp/somelib
|
||||
,IsPreassigned=0
|
||||
,mDebug=0
|
||||
,frefin=mm_in
|
||||
,frefout=mm_out
|
||||
)/*/STORE SOURCE*/;
|
||||
|
||||
%local mD;
|
||||
%if &mDebug=1 %then %let mD=;
|
||||
%else %let mD=%str(*);
|
||||
%&mD.put Executing &sysmacroname..sas;
|
||||
%&mD.put _local_;
|
||||
|
||||
%let libref=%upcase(&libref);
|
||||
|
||||
/**
|
||||
* Check Library does not exist already with this libname
|
||||
*/
|
||||
data _null_;
|
||||
length type uri $256;
|
||||
rc=metadata_resolve("omsobj:SASLibrary?@Name='&libname'",type,uri);
|
||||
call symputx('checktype',type,'l');
|
||||
call symputx('liburi',uri,'l');
|
||||
putlog (_all_)(=);
|
||||
run;
|
||||
%if &checktype = SASLibrary %then %do;
|
||||
%put WARNING: Library (&liburi) already exists with libname (&libname) ;
|
||||
%return;
|
||||
%end;
|
||||
|
||||
/**
|
||||
* Check Library does not exist already with this libref
|
||||
*/
|
||||
data _null_;
|
||||
length type uri $256;
|
||||
rc=metadata_resolve("omsobj:SASLibrary?@Libref='&libref'",type,uri);
|
||||
call symputx('checktype',type,'l');
|
||||
call symputx('liburi',uri,'l');
|
||||
putlog (_all_)(=);
|
||||
run;
|
||||
%if &checktype = SASLibrary %then %do;
|
||||
%put WARNING: Library (&liburi) already exists with libref (&libref) ;
|
||||
%return;
|
||||
%end;
|
||||
|
||||
|
||||
/**
|
||||
* Attempt to create tree
|
||||
*/
|
||||
%mm_createfolder(path=&tree)
|
||||
|
||||
/**
|
||||
* check tree exists
|
||||
*/
|
||||
data _null_;
|
||||
length type uri $256;
|
||||
rc=metadata_pathobj("","&tree","Folder",type,uri);
|
||||
call symputx('foldertype',type,'l');
|
||||
call symputx('treeuri',uri,'l');
|
||||
run;
|
||||
%if &foldertype ne Tree %then %do;
|
||||
%put WARNING: Tree &tree does not exist!;
|
||||
%return;
|
||||
%end;
|
||||
|
||||
/**
|
||||
* Create filerefs for proc metadata call
|
||||
*/
|
||||
filename &frefin temp;
|
||||
filename &frefout temp;
|
||||
|
||||
%if &engine=BASE %then %do;
|
||||
|
||||
%mf_verifymacvars(libname libref engine servercontext tree)
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Check that the ServerContext exists
|
||||
*/
|
||||
data _null_;
|
||||
length type uri $256;
|
||||
rc=metadata_resolve("omsobj:ServerContext?@Name='&ServerContext'",type,uri);
|
||||
call symputx('checktype',type,'l');
|
||||
call symputx('serveruri',uri,'l');
|
||||
putlog (_all_)(=);
|
||||
run;
|
||||
%if &checktype ne ServerContext %then %do;
|
||||
%put %str(ERR)OR: ServerContext (&ServerContext) does not exist!;
|
||||
%return;
|
||||
%end;
|
||||
|
||||
/**
|
||||
* Get prototype info
|
||||
*/
|
||||
data _null_;
|
||||
length type uri str $256;
|
||||
str="omsobj:Prototype?@Name='Library.SAS.Prototype.Name.xmlKey.txt'";
|
||||
rc=metadata_resolve(str,type,uri);
|
||||
call symputx('checktype',type,'l');
|
||||
call symputx('prototypeuri',uri,'l');
|
||||
putlog (_all_)(=);
|
||||
run;
|
||||
%if &checktype ne Prototype %then %do;
|
||||
%put %str(ERR)OR: Prototype (Library.SAS.Prototype.Name.xmlKey.txt) not found!;
|
||||
%return;
|
||||
%end;
|
||||
|
||||
/**
|
||||
* Check that Physical location exists
|
||||
*/
|
||||
%if %sysfunc(fileexist(&directory))=0 %then %do;
|
||||
%put %str(ERR)OR: Physical directory (&directory) does not appear to exist!;
|
||||
%return;
|
||||
%end;
|
||||
|
||||
/**
|
||||
* Check that Directory Object exists in metadata
|
||||
*/
|
||||
data _null_;
|
||||
length type uri $256;
|
||||
rc=metadata_resolve("omsobj:Directory?@DirectoryRole='LibraryPath'"
|
||||
!!" and @DirectoryName='&directory'",type,uri);
|
||||
call symputx('checktype',type,'l');
|
||||
call symputx('directoryuri',uri,'l');
|
||||
putlog (_all_)(=);
|
||||
run;
|
||||
%if &checktype ne Directory %then %do;
|
||||
%put NOTE: Directory object does not exist for (&directory) location;
|
||||
%put NOTE: It will now be created;
|
||||
|
||||
data _null_;
|
||||
file &frefin;
|
||||
directory=quote(symget('directory'));
|
||||
put "<AddMetadata><Reposid>$METAREPOSITORY</Reposid><Metadata> "/
|
||||
'<Directory UsageVersion="1000000" IsHidden="0" IsRelative="0"'/
|
||||
' DirectoryRole="LibraryPath" Name="Path" DirectoryName=' directory '/>'/
|
||||
"</Metadata><NS>SAS</NS>"/
|
||||
"<Flags>268435456</Flags></AddMetadata>";
|
||||
run;
|
||||
|
||||
proc metadata in= &frefin out=&frefout %if &mdebug=1 %then verbose;;
|
||||
run;
|
||||
%if &mdebug=1 %then %do;
|
||||
data _null_;
|
||||
infile &frefout lrecl=1048576;
|
||||
input; put _infile_;
|
||||
run;
|
||||
%end;
|
||||
%put NOTE: Checking to ensure directory (&directory) object was created;
|
||||
data _null_;
|
||||
length type uri $256;
|
||||
rc=metadata_resolve("omsobj:Directory?@DirectoryRole='LibraryPath'"
|
||||
!!" and @DirectoryName='&directory'",type,uri);
|
||||
call symputx('checktype2',type,'l');
|
||||
call symputx('directoryuri',uri,'l');
|
||||
%if &mdebug=1 %then putlog (_all_)(=);;
|
||||
run;
|
||||
%if &checktype2 ne Directory %then %do;
|
||||
%put %str(ERR)OR: Directory (&directory) object was NOT created!;
|
||||
%return;
|
||||
%end;
|
||||
%else %put NOTE: Directory (&directoryuri) successfully created!;
|
||||
%end;
|
||||
|
||||
/**
|
||||
* check SAS version
|
||||
*/
|
||||
%if %sysevalf(&sysver lt 9.3) %then %do;
|
||||
%put WARNING: Version 9.3 or later required;
|
||||
%return;
|
||||
%end;
|
||||
|
||||
/**
|
||||
* Prepare the XML and create the library
|
||||
*/
|
||||
data _null_;
|
||||
file &frefin;
|
||||
treeuri=quote(symget('treeuri'));
|
||||
serveruri=quote(symget('serveruri'));
|
||||
directoryuri=quote(symget('directoryuri'));
|
||||
libname=quote(symget('libname'));
|
||||
libref=quote(symget('libref'));
|
||||
IsPreassigned=quote(symget('IsPreassigned'));
|
||||
prototypeuri=quote(symget('prototypeuri'));
|
||||
|
||||
/* escape description so it can be stored as XML */
|
||||
libdesc=tranwrd(symget('libdesc'),'&','&');
|
||||
libdesc=tranwrd(libdesc,'<','<');
|
||||
libdesc=tranwrd(libdesc,'>','>');
|
||||
libdesc=tranwrd(libdesc,"'",''');
|
||||
libdesc=tranwrd(libdesc,'"','"');
|
||||
libdesc=tranwrd(libdesc,'0A'x,' ');
|
||||
libdesc=tranwrd(libdesc,'0D'x,' ');
|
||||
libdesc=tranwrd(libdesc,'$','$');
|
||||
libdesc=quote(trim(libdesc));
|
||||
|
||||
put "<AddMetadata><Reposid>$METAREPOSITORY</Reposid><Metadata> "/
|
||||
'<SASLibrary Desc=' libdesc ' Engine="BASE" IsDBMSLibname="0" '/
|
||||
' IsHidden="0" IsPreassigned=' IsPreassigned ' Libref=' libref /
|
||||
' UsageVersion="1000000" PublicType="Library" name=' libname '>'/
|
||||
' <DeployedComponents>'/
|
||||
' <ServerContext ObjRef=' serveruri "/>"/
|
||||
' </DeployedComponents>'/
|
||||
' <PropertySets>'/
|
||||
' <PropertySet Name="ModifiedByProductPropertySet" '/
|
||||
' SetRole="ModifiedByProductPropertySet" UsageVersion="0" />'/
|
||||
' </PropertySets>'/
|
||||
" <Trees><Tree ObjRef=" treeuri "/></Trees>"/
|
||||
' <UsingPackages> '/
|
||||
' <Directory ObjRef=' directoryuri ' />'/
|
||||
' </UsingPackages>'/
|
||||
' <UsingPrototype>'/
|
||||
' <Prototype ObjRef=' prototypeuri '/>'/
|
||||
' </UsingPrototype>'/
|
||||
'</SASLibrary></Metadata><NS>SAS</NS>'/
|
||||
'<Flags>268435456</Flags></AddMetadata>';
|
||||
run;
|
||||
|
||||
|
||||
proc metadata in= &frefin out=&frefout %if &mdebug=1 %then verbose ;;
|
||||
run;
|
||||
|
||||
%if &mdebug=1 %then %do;
|
||||
data _null_;
|
||||
infile &frefout lrecl=1048576;
|
||||
input;put _infile_;
|
||||
run;
|
||||
%end;
|
||||
%put NOTE: Checking to ensure library (&libname) was created;
|
||||
data _null_;
|
||||
length type uri $256;
|
||||
rc=metadata_pathobj("","&tree/&libname","Library",type,uri);
|
||||
call symputx('libtype',type,'l');
|
||||
call symputx('liburi',uri,'l');
|
||||
%if &mdebug=1 %then putlog (_all_)(=);;
|
||||
run;
|
||||
%if &libtype ne SASLibrary %then %do;
|
||||
%put %str(ERR)OR: Could not find (&libname) at (&tree)!!;
|
||||
%return;
|
||||
%end;
|
||||
%else %put NOTE: Library (&libname) successfully created in (&tree)!;
|
||||
%end;
|
||||
%else %do;
|
||||
%put %str(ERR)OR: Other library engine types are not yet supported!!;
|
||||
%end;
|
||||
|
||||
|
||||
/**
|
||||
* Wrap up
|
||||
*/
|
||||
%if &mdebug ne 1 %then %do;
|
||||
filename &frefin clear;
|
||||
filename &frefout clear;
|
||||
%end;
|
||||
|
||||
%mend;
|
||||
389
meta/mm_createstp.sas
Executable file
389
meta/mm_createstp.sas
Executable file
@@ -0,0 +1,389 @@
|
||||
/**
|
||||
@file
|
||||
@brief Create a type 1 Stored Process (9.2 compatible)
|
||||
@details This macro creates a Type 1 stored process, and also the necessary
|
||||
PromptGroup / File / TextStore objects. It requires the location (or uri)
|
||||
for the App Server / Directory / Folder (Tree) objects.
|
||||
To upgrade this macro to work with type 2 (which can embed SAS code
|
||||
and is compabitible with SAS from 9.3 onwards) then the UsageVersion should
|
||||
change to 2000000 and the TextStore object updated. The ComputeServer
|
||||
reference will also be to ServerContext rather than LogicalServer.
|
||||
|
||||
This macro is idempotent - if you run it twice, it will only create an STP
|
||||
once.
|
||||
|
||||
usage (type 1 STP):
|
||||
|
||||
%mm_createstp(stpname=MyNewSTP
|
||||
,filename=mySpecialProgram.sas
|
||||
,directory=SASEnvironment/SASCode/STPs
|
||||
,tree=/User Folders/sasdemo
|
||||
,outds=work.uris)
|
||||
|
||||
If you wish to remove the new STP you can do so by running:
|
||||
|
||||
data _null_;
|
||||
set work.uris;
|
||||
rc1 = METADATA_DELOBJ(texturi);
|
||||
rc2 = METADATA_DELOBJ(prompturi);
|
||||
rc3 = METADATA_DELOBJ(fileuri);
|
||||
rc4 = METADATA_DELOBJ(stpuri);
|
||||
putlog (_all_)(=);
|
||||
run;
|
||||
|
||||
usage (type 2 STP):
|
||||
%mm_createstp(stpname=MyNewType2STP
|
||||
,filename=mySpecialProgram.sas
|
||||
,directory=SASEnvironment/SASCode/STPs
|
||||
,tree=/User Folders/sasdemo
|
||||
,Server=SASApp
|
||||
,stptype=2)
|
||||
|
||||
<h4> Dependencies </h4>
|
||||
@li mf_nobs.sas
|
||||
@li mf_verifymacvars.sas
|
||||
@li mm_getdirectories.sas
|
||||
@li mm_updatestpsourcecode.sas
|
||||
@li mp_dropmembers.sas
|
||||
@li mm_getservercontexts.sas
|
||||
|
||||
@param stpname= Stored Process name. Avoid spaces - testing has shown that
|
||||
the check to avoid creating multiple STPs in the same folder with the same
|
||||
name does not work when the name contains spaces.
|
||||
@param stpdesc= Stored Process description (optional)
|
||||
@param filename= the name of the .sas program to run
|
||||
@param directory= The directory uri, or the actual path to the sas program
|
||||
(no trailing slash). If more than uri is found with that path, then the
|
||||
first one will be used.
|
||||
@param tree= The metadata folder uri, or the metadata path, in which to
|
||||
create the STP.
|
||||
@param server= The server which will run the STP. Server name or uri is fine.
|
||||
@param outds= The two level name of the output dataset. Will contain all the
|
||||
meta uris. Defaults to work.mm_createstp.
|
||||
@param mDebug= set to 1 to show debug messages in the log
|
||||
@param stptype= Default is 1 (STP code saved on filesystem). Set to 2 if
|
||||
source code is to be saved in metadata (9.3 and above feature).
|
||||
@param minify= set to YES to strip comments / blank lines etc
|
||||
@param frefin= fileref to use (enables change if there is a conflict). The
|
||||
filerefs are left open, to enable inspection after running the
|
||||
macro (or importing into an xmlmap if needed).
|
||||
@param frefout= fileref to use (enables change if there is a conflict)
|
||||
@param repo= ServerContext is tied to a repo, if you are not using the
|
||||
foundation repo then select a different one here
|
||||
|
||||
@returns outds dataset containing the following columns:
|
||||
- stpuri
|
||||
- prompturi
|
||||
- fileuri
|
||||
- texturi
|
||||
|
||||
@version 9.2
|
||||
@author Allan Bowe
|
||||
|
||||
**/
|
||||
|
||||
%macro mm_createstp(
|
||||
stpname=Macro People STP
|
||||
,stpdesc=This stp was created automatically by the mm_createstp macro
|
||||
,filename=mm_createstp.sas
|
||||
,directory=SASEnvironment/SASCode
|
||||
,tree=/User Folders/sasdemo
|
||||
,package=false
|
||||
,streaming=true
|
||||
,outds=work.mm_createstp
|
||||
,mDebug=0
|
||||
,server=SASApp
|
||||
,stptype=1
|
||||
,minify=NO
|
||||
,frefin=mm_in
|
||||
,frefout=mm_out
|
||||
)/*/STORE SOURCE*/;
|
||||
|
||||
%local mD;
|
||||
%if &mDebug=1 %then %let mD=;
|
||||
%else %let mD=%str(*);
|
||||
%&mD.put Executing mm_CreateSTP.sas;
|
||||
%&mD.put _local_;
|
||||
|
||||
%mf_verifymacvars(stpname filename directory tree)
|
||||
%mp_dropmembers(%scan(&outds,2,.))
|
||||
|
||||
/**
|
||||
* check tree exists
|
||||
*/
|
||||
data _null_;
|
||||
length type uri $256;
|
||||
rc=metadata_pathobj("","&tree","Folder",type,uri);
|
||||
call symputx('foldertype',type,'l');
|
||||
call symputx('treeuri',uri,'l');
|
||||
run;
|
||||
%if &foldertype ne Tree %then %do;
|
||||
%put WARNING: Tree &tree does not exist!;
|
||||
%return;
|
||||
%end;
|
||||
|
||||
/**
|
||||
* Check STP does not exist already
|
||||
*/
|
||||
%local cmtype;
|
||||
data _null_;
|
||||
length type uri $256;
|
||||
rc=metadata_pathobj("","&tree/&stpname",'StoredProcess',type,uri);
|
||||
call symputx('cmtype',type,'l');
|
||||
call symputx('stpuri',uri,'l');
|
||||
run;
|
||||
%if &cmtype = ClassifierMap %then %do;
|
||||
%put WARNING: Stored Process &stpname already exists in &tree!;
|
||||
%return;
|
||||
%end;
|
||||
|
||||
/**
|
||||
* Check that the physical file exists
|
||||
*/
|
||||
%if %sysfunc(fileexist(&directory/&filename)) ne 1 %then %do;
|
||||
%put WARNING: FILE *&directory/&filename* NOT FOUND!;
|
||||
%return;
|
||||
%end;
|
||||
|
||||
%if &stptype=1 %then %do;
|
||||
/* type 1 STP - where code is stored on filesystem */
|
||||
%if %sysevalf(&sysver lt 9.2) %then %do;
|
||||
%put WARNING: Version 9.2 or later required;
|
||||
%return;
|
||||
%end;
|
||||
|
||||
/* check directory object (where 9.2 source code reference is stored) */
|
||||
data _null_;
|
||||
length id $20 dirtype $256;
|
||||
rc=metadata_resolve("&directory",dirtype,id);
|
||||
call symputx('checkdirtype',dirtype,'l');
|
||||
run;
|
||||
|
||||
%if &checkdirtype ne Directory %then %do;
|
||||
%mm_getdirectories(path=&directory,outds=&outds ,mDebug=&mDebug)
|
||||
%if %mf_nobs(&outds)=0 or %sysfunc(exist(&outds))=0 %then %do;
|
||||
%put WARNING: The directory object does not exist for &directory;
|
||||
%return;
|
||||
%end;
|
||||
%end;
|
||||
%else %do;
|
||||
data &outds;
|
||||
directoryuri="&directory";
|
||||
run;
|
||||
%end;
|
||||
|
||||
data &outds (keep=stpuri prompturi fileuri texturi);
|
||||
length stpuri prompturi fileuri texturi serveruri $256 ;
|
||||
set &outds;
|
||||
|
||||
/* final checks on uris */
|
||||
length id $20 type $256;
|
||||
__rc=metadata_resolve("&treeuri",type,id);
|
||||
if type ne 'Tree' then do;
|
||||
putlog "WARNING: Invalid tree URI: &treeuri";
|
||||
stopme=1;
|
||||
end;
|
||||
__rc=metadata_resolve(directoryuri,type,id);
|
||||
if type ne 'Directory' then do;
|
||||
putlog 'WARNING: Invalid directory URI: ' directoryuri;
|
||||
stopme=1;
|
||||
end;
|
||||
|
||||
/* get server info */
|
||||
__rc=metadata_resolve("&server",type,serveruri);
|
||||
if type ne 'LogicalServer' then do;
|
||||
__rc=metadata_getnobj("omsobj:LogicalServer?@Name='&server'",1,serveruri);
|
||||
if serveruri='' then do;
|
||||
putlog "WARNING: Invalid server: &server";
|
||||
stopme=1;
|
||||
end;
|
||||
end;
|
||||
|
||||
if stopme=1 then do;
|
||||
putlog (_all_)(=);
|
||||
stop;
|
||||
end;
|
||||
|
||||
/* create empty prompt */
|
||||
rc1=METADATA_NEWOBJ('PromptGroup',prompturi,'Parameters');
|
||||
rc2=METADATA_SETATTR(prompturi, 'UsageVersion', '1000000');
|
||||
rc3=METADATA_SETATTR(prompturi, 'GroupType','2');
|
||||
rc4=METADATA_SETATTR(prompturi, 'Name','Parameters');
|
||||
rc5=METADATA_SETATTR(prompturi, 'PublicType','Embedded:PromptGroup');
|
||||
GroupInfo="<PromptGroup promptId='PromptGroup_%sysfunc(datetime())_&sysprocessid'"
|
||||
!!" version='1.0'><Label><Text xml:lang='en-GB'>Parameters</Text>"
|
||||
!!"</Label></PromptGroup>";
|
||||
rc6 = METADATA_SETATTR(prompturi, 'GroupInfo',groupinfo);
|
||||
|
||||
if sum(of rc1-rc6) ne 0 then do;
|
||||
putlog 'WARNING: Issue creating prompt.';
|
||||
if prompturi ne . then do;
|
||||
putlog ' Removing orphan: ' prompturi;
|
||||
rc = METADATA_DELOBJ(prompturi);
|
||||
put rc=;
|
||||
end;
|
||||
stop;
|
||||
end;
|
||||
|
||||
/* create a file uri */
|
||||
rc7=METADATA_NEWOBJ('File',fileuri,'SP Source File');
|
||||
rc8=METADATA_SETATTR(fileuri, 'FileName',"&filename");
|
||||
rc9=METADATA_SETATTR(fileuri, 'IsARelativeName','1');
|
||||
rc10=METADATA_SETASSN(fileuri, 'Directories','MODIFY',directoryuri);
|
||||
if sum(of rc7-rc10) ne 0 then do;
|
||||
putlog 'WARNING: Issue creating file.';
|
||||
if fileuri ne . then do;
|
||||
putlog ' Removing orphans:' prompturi fileuri;
|
||||
rc = METADATA_DELOBJ(prompturi);
|
||||
rc = METADATA_DELOBJ(fileuri);
|
||||
put (_all_)(=);
|
||||
end;
|
||||
stop;
|
||||
end;
|
||||
|
||||
/* create a TextStore object */
|
||||
rc11= METADATA_NEWOBJ('TextStore',texturi,'Stored Process');
|
||||
rc12= METADATA_SETATTR(texturi, 'TextRole','StoredProcessConfiguration');
|
||||
rc13= METADATA_SETATTR(texturi, 'TextType','XML');
|
||||
storedtext='<?xml version="1.0" encoding="UTF-8"?><StoredProcess>'
|
||||
!!"<ResultCapabilities Package='&package' Streaming='&streaming'/>"
|
||||
!!"<OutputParameters/></StoredProcess>";
|
||||
rc14= METADATA_SETATTR(texturi, 'StoredText',storedtext);
|
||||
if sum(of rc11-rc14) ne 0 then do;
|
||||
putlog 'WARNING: Issue creating TextStore.';
|
||||
if texturi ne . then do;
|
||||
putlog ' Removing orphans: ' prompturi fileuri texturi;
|
||||
rc = METADATA_DELOBJ(prompturi);
|
||||
rc = METADATA_DELOBJ(fileuri);
|
||||
rc = METADATA_DELOBJ(texturi);
|
||||
put (_all_)(=);
|
||||
end;
|
||||
stop;
|
||||
end;
|
||||
|
||||
/* create meta obj */
|
||||
rc15= METADATA_NEWOBJ('ClassifierMap',stpuri,"&stpname");
|
||||
rc16= METADATA_SETASSN(stpuri, 'Trees','MODIFY',treeuri);
|
||||
rc17= METADATA_SETASSN(stpuri, 'ComputeLocations','MODIFY',serveruri);
|
||||
rc18= METADATA_SETASSN(stpuri, 'SourceCode','MODIFY',fileuri);
|
||||
rc19= METADATA_SETASSN(stpuri, 'Prompts','MODIFY',prompturi);
|
||||
rc20= METADATA_SETASSN(stpuri, 'Notes','MODIFY',texturi);
|
||||
rc21= METADATA_SETATTR(stpuri, 'PublicType', 'StoredProcess');
|
||||
rc22= METADATA_SETATTR(stpuri, 'TransformRole', 'StoredProcess');
|
||||
rc23= METADATA_SETATTR(stpuri, 'UsageVersion', '1000000');
|
||||
rc24= METADATA_SETATTR(stpuri, 'Desc', "&stpdesc");
|
||||
|
||||
/* tidy up if err */
|
||||
if sum(of rc15-rc24) ne 0 then do;
|
||||
putlog "%str(WARN)ING: Issue creating STP.";
|
||||
if stpuri ne . then do;
|
||||
putlog ' Removing orphans: ' prompturi fileuri texturi stpuri;
|
||||
rc = METADATA_DELOBJ(prompturi);
|
||||
rc = METADATA_DELOBJ(fileuri);
|
||||
rc = METADATA_DELOBJ(texturi);
|
||||
rc = METADATA_DELOBJ(stpuri);
|
||||
put (_all_)(=);
|
||||
end;
|
||||
end;
|
||||
else do;
|
||||
fullpath=cats('_program=',treepath,"/&stpname");
|
||||
putlog "NOTE: Stored Process Created!";
|
||||
putlog "NOTE- "; putlog "NOTE-"; putlog "NOTE-" fullpath;
|
||||
putlog "NOTE- "; putlog "NOTE-";
|
||||
end;
|
||||
output;
|
||||
stop;
|
||||
run;
|
||||
%end;
|
||||
%else %if &stptype=2 %then %do;
|
||||
/* type 2 stp - code is stored in metadata */
|
||||
%if %sysevalf(&sysver lt 9.3) %then %do;
|
||||
%put WARNING: SAS version 9.3 or later required to create type2 STPs;
|
||||
%return;
|
||||
%end;
|
||||
/* check we have the correct ServerContext */
|
||||
%mm_getservercontexts(outds=contexts)
|
||||
%local serveruri; %let serveruri=NOTFOUND;
|
||||
data _null_;
|
||||
set contexts;
|
||||
where upcase(servername)="%upcase(&server)";
|
||||
call symputx('serveruri',serveruri);
|
||||
run;
|
||||
%if &serveruri=NOTFOUND %then %do;
|
||||
%put WARNING: ServerContext *&server* not found!;
|
||||
%return;
|
||||
%end;
|
||||
|
||||
/**
|
||||
* First, create a Hello World type 2 stored process
|
||||
*/
|
||||
filename &frefin temp;
|
||||
data _null_;
|
||||
file &frefin;
|
||||
treeuri=quote(symget('treeuri'));
|
||||
serveruri=quote(symget('serveruri'));
|
||||
stpdesc=quote(symget('stpdesc'));
|
||||
stpname=quote(symget('stpname'));
|
||||
|
||||
put "<AddMetadata><Reposid>$METAREPOSITORY</Reposid><Metadata> "/
|
||||
'<ClassifierMap UsageVersion="2000000" IsHidden="0" IsUserDefined="0" '/
|
||||
' IsActive="1" PublicType="StoredProcess" TransformRole="StoredProcess" '/
|
||||
' Name=' stpname ' Desc=' stpdesc '>'/
|
||||
" <ComputeLocations>"/
|
||||
" <ServerContext ObjRef=" serveruri "/>"/
|
||||
" </ComputeLocations>"/
|
||||
"<Notes> "/
|
||||
' <TextStore IsHidden="0" Name="SourceCode" UsageVersion="0" '/
|
||||
' TextRole="StoredProcessSourceCode" StoredText="%put hello world!;" />'/
|
||||
' <TextStore IsHidden="0" Name="Stored Process" UsageVersion="0" '/
|
||||
' TextRole="StoredProcessConfiguration" TextType="XML" '/
|
||||
' StoredText="<?xml version="1.0" encoding="UTF-8&qu'@@
|
||||
'ot;?><StoredProcess><ServerContext LogicalServerType="S'@@
|
||||
'ps" OtherAllowed="false"/><ResultCapabilities Packa'@@
|
||||
'ge="' @@ "&package" @@ '" Streaming="' @@ "&streaming" @@
|
||||
'"/><OutputParameters/></StoredProcess>" />' /
|
||||
" </Notes> "/
|
||||
" <Prompts> "/
|
||||
' <PromptGroup Name="Parameters" GroupType="2" IsHidden="0" '/
|
||||
' PublicType="Embedded:PromptGroup" UsageVersion="1000000" '/
|
||||
' GroupInfo="<PromptGroup promptId="PromptGroup_1502797359253'@@
|
||||
'_802080" version="1.0"><Label><Text xml:lang='@@
|
||||
'"en-US">Parameters</Text></Label></PromptGro'@@
|
||||
'up>" />'/
|
||||
" </Prompts> "/
|
||||
"<Trees><Tree ObjRef=" treeuri "/></Trees>"/
|
||||
"</ClassifierMap></Metadata><NS>SAS</NS>"/
|
||||
"<Flags>268435456</Flags></AddMetadata>";
|
||||
run;
|
||||
|
||||
filename &frefout temp;
|
||||
|
||||
proc metadata in= &frefin out=&frefout ;
|
||||
run;
|
||||
|
||||
%if &mdebug=1 %then %do;
|
||||
/* write the response to the log for debugging */
|
||||
data _null_;
|
||||
infile &frefout lrecl=1048576;
|
||||
input;
|
||||
put _infile_;
|
||||
run;
|
||||
%end;
|
||||
|
||||
/**
|
||||
* Next, add the source code
|
||||
*/
|
||||
%mm_updatestpsourcecode(stp=&tree/&stpname
|
||||
,stpcode="&directory/&filename"
|
||||
,frefin=&frefin.
|
||||
,frefout=&frefout.
|
||||
,mdebug=&mdebug
|
||||
,minify=&minify)
|
||||
|
||||
|
||||
%end;
|
||||
%else %do;
|
||||
%put WARNING: STPTYPE=*&stptype* not recognised!;
|
||||
%end;
|
||||
|
||||
%mend;
|
||||
406
meta/mm_createwebservice.sas
Normal file
406
meta/mm_createwebservice.sas
Normal file
@@ -0,0 +1,406 @@
|
||||
/**
|
||||
@file mm_createwebservice.sas
|
||||
@brief Create a Web Ready Stored Process
|
||||
@details This macro creates a Type 2 Stored Process with the macropeople
|
||||
mm_webout macro included as pre-code.
|
||||
Usage:
|
||||
|
||||
%* compile macros ;
|
||||
filename mc url "https://raw.githubusercontent.com/macropeople/macrocore/master/mc_all.sas";
|
||||
%inc mc;
|
||||
|
||||
%* parmcards lets us write to a text file from open code ;
|
||||
filename ft15f001 temp;
|
||||
parmcards4;
|
||||
%* do some sas, any inputs are now already WORK tables;
|
||||
data example1 example2;
|
||||
set sashelp.class;
|
||||
run;
|
||||
%* send data back;
|
||||
%webout(OPEN)
|
||||
%webout(ARR,example1) * Array format, fast, suitable for large tables ;
|
||||
%webout(OBJ,example2) * Object format, easier to work with ;
|
||||
%webout(CLOSE)
|
||||
;;;;
|
||||
%mm_createwebservice(path=/Public/app/common,name=appInit,code=ft15f001,replace=YES)
|
||||
|
||||
<h4> Dependencies </h4>
|
||||
@li mm_createstp.sas
|
||||
@li mf_getuser.sas
|
||||
@li mm_createfolder.sas
|
||||
@li mm_deletestp.sas
|
||||
|
||||
@param path= The full path (in SAS Metadata) where the service will be created
|
||||
@param name= Stored Process name. Avoid spaces - testing has shown that
|
||||
the check to avoid creating multiple STPs in the same folder with the same
|
||||
name does not work when the name contains spaces.
|
||||
@param desc= The description of the service (optional)
|
||||
@param precode= Space separated list of filerefs, pointing to the code that
|
||||
needs to be attached to the beginning of the service (optional)
|
||||
@param code= Space seperated fileref(s) of the actual code to be added
|
||||
@param server= The server which will run the STP. Server name or uri is fine.
|
||||
@param mDebug= set to 1 to show debug messages in the log
|
||||
@param replace= select YES to replace any existing service in that location
|
||||
@param adapter= the macro uses the sasjs adapter by default. To use another
|
||||
adapter, add a (different) fileref here.
|
||||
|
||||
@version 9.2
|
||||
@author Allan Bowe
|
||||
|
||||
**/
|
||||
|
||||
%macro mm_createwebservice(path=
|
||||
,name=initService
|
||||
,precode=
|
||||
,code=
|
||||
,desc=This stp was created automagically by the mm_createwebservice macro
|
||||
,mDebug=0
|
||||
,server=SASApp
|
||||
,replace=NO
|
||||
,adapter=sasjs
|
||||
)/*/STORE SOURCE*/;
|
||||
|
||||
%if &syscc ge 4 %then %do;
|
||||
%put &=syscc - &sysmacroname will not execute in this state;
|
||||
%return;
|
||||
%end;
|
||||
|
||||
%local mD;
|
||||
%if &mDebug=1 %then %let mD=;
|
||||
%else %let mD=%str(*);
|
||||
%&mD.put Executing mm_createwebservice.sas;
|
||||
%&mD.put _local_;
|
||||
|
||||
* remove any trailing slash ;
|
||||
%if "%substr(&path,%length(&path),1)" = "/" %then
|
||||
%let path=%substr(&path,1,%length(&path)-1);
|
||||
|
||||
/**
|
||||
* Add webout macro
|
||||
* These put statements are auto generated - to change the macro, change the
|
||||
* source (mm_webout) and run `build.py`
|
||||
*/
|
||||
filename sasjs temp;
|
||||
data _null_;
|
||||
file sasjs lrecl=3000 ;
|
||||
put "/* Created on %sysfunc(datetime(),datetime19.) by %mf_getuser() */";
|
||||
/* WEBOUT BEGIN */
|
||||
put ' ';
|
||||
put '%macro mp_jsonout(action,ds,jref=_webout,dslabel=,fmt=Y,engine=PROCJSON,dbg=0 ';
|
||||
put ')/*/STORE SOURCE*/; ';
|
||||
put '%put output location=&jref; ';
|
||||
put '%if &action=OPEN %then %do; ';
|
||||
put ' data _null_;file &jref encoding=''utf-8''; ';
|
||||
put ' put ''{"START_DTTM" : "'' "%sysfunc(datetime(),datetime20.3)" ''"''; ';
|
||||
put ' run; ';
|
||||
put '%end; ';
|
||||
put '%else %if (&action=ARR or &action=OBJ) %then %do; ';
|
||||
put ' options validvarname=upcase; ';
|
||||
put ' data _null_;file &jref mod encoding=''utf-8''; ';
|
||||
put ' put ", ""%lowcase(%sysfunc(coalescec(&dslabel,&ds)))"":"; ';
|
||||
put ' ';
|
||||
put ' %if &engine=PROCJSON %then %do; ';
|
||||
put ' data;run;%let tempds=&syslast; ';
|
||||
put ' proc sql;drop table &tempds; ';
|
||||
put ' data &tempds /view=&tempds;set &ds; ';
|
||||
put ' %if &fmt=N %then format _numeric_ best32.;; ';
|
||||
put ' proc json out=&jref ';
|
||||
put ' %if &action=ARR %then nokeys ; ';
|
||||
put ' %if &dbg ge 131 %then pretty ; ';
|
||||
put ' ;export &tempds / nosastags fmtnumeric; ';
|
||||
put ' run; ';
|
||||
put ' proc sql;drop view &tempds; ';
|
||||
put ' %end; ';
|
||||
put ' %else %if &engine=DATASTEP %then %do; ';
|
||||
put ' %local cols i tempds; ';
|
||||
put ' %let cols=0; ';
|
||||
put ' %if %sysfunc(exist(&ds)) ne 1 & %sysfunc(exist(&ds,VIEW)) ne 1 %then %do; ';
|
||||
put ' %put &sysmacroname: &ds NOT FOUND!!!; ';
|
||||
put ' %return; ';
|
||||
put ' %end; ';
|
||||
put ' data _null_;file &jref mod ; ';
|
||||
put ' put "["; call symputx(''cols'',0,''l''); ';
|
||||
put ' proc sort data=sashelp.vcolumn(where=(libname=''WORK'' & memname="%upcase(&ds)")) ';
|
||||
put ' out=_data_; ';
|
||||
put ' by varnum; ';
|
||||
put ' ';
|
||||
put ' data _null_; ';
|
||||
put ' set _last_ end=last; ';
|
||||
put ' call symputx(cats(''name'',_n_),name,''l''); ';
|
||||
put ' call symputx(cats(''type'',_n_),type,''l''); ';
|
||||
put ' call symputx(cats(''len'',_n_),length,''l''); ';
|
||||
put ' if last then call symputx(''cols'',_n_,''l''); ';
|
||||
put ' run; ';
|
||||
put ' ';
|
||||
put ' proc format; /* credit yabwon for special null removal */ ';
|
||||
put ' value bart ._ - .z = null ';
|
||||
put ' other = [best.]; ';
|
||||
put ' ';
|
||||
put ' data;run; %let tempds=&syslast; /* temp table for spesh char management */ ';
|
||||
put ' proc sql; drop table &tempds; ';
|
||||
put ' data &tempds/view=&tempds; ';
|
||||
put ' attrib _all_ label=''''; ';
|
||||
put ' %do i=1 %to &cols; ';
|
||||
put ' %if &&type&i=char %then %do; ';
|
||||
put ' length &&name&i $32767; ';
|
||||
put ' format &&name&i $32767.; ';
|
||||
put ' %end; ';
|
||||
put ' %end; ';
|
||||
put ' set &ds; ';
|
||||
put ' format _numeric_ bart.; ';
|
||||
put ' %do i=1 %to &cols; ';
|
||||
put ' %if &&type&i=char %then %do; ';
|
||||
put ' &&name&i=''"''!!trim(prxchange(''s/"/\"/'',-1, ';
|
||||
put ' prxchange(''s/''!!''0A''x!!''/\n/'',-1, ';
|
||||
put ' prxchange(''s/''!!''0D''x!!''/\r/'',-1, ';
|
||||
put ' prxchange(''s/''!!''09''x!!''/\t/'',-1, ';
|
||||
put ' prxchange(''s/\\/\\\\/'',-1,&&name&i) ';
|
||||
put ' )))))!!''"''; ';
|
||||
put ' %end; ';
|
||||
put ' %end; ';
|
||||
put ' run; ';
|
||||
put ' /* write to temp loc to avoid _webout truncation - https://support.sas.com/kb/49/325.html */ ';
|
||||
put ' filename _sjs temp lrecl=131068 encoding=''utf-8''; ';
|
||||
put ' data _null_; file _sjs lrecl=131068 encoding=''utf-8'' mod; ';
|
||||
put ' set &tempds; ';
|
||||
put ' if _n_>1 then put "," @; put ';
|
||||
put ' %if &action=ARR %then "[" ; %else "{" ; ';
|
||||
put ' %do i=1 %to &cols; ';
|
||||
put ' %if &i>1 %then "," ; ';
|
||||
put ' %if &action=OBJ %then """&&name&i"":" ; ';
|
||||
put ' &&name&i ';
|
||||
put ' %end; ';
|
||||
put ' %if &action=ARR %then "]" ; %else "}" ; ; ';
|
||||
put ' proc sql; ';
|
||||
put ' drop view &tempds; ';
|
||||
put ' /* now write the long strings to _webout 1 byte at a time */ ';
|
||||
put ' data _null_; ';
|
||||
put ' length filein 8 fileid 8; ';
|
||||
put ' filein = fopen("_sjs",''I'',1,''B''); ';
|
||||
put ' fileid = fopen("&jref",''A'',1,''B''); ';
|
||||
put ' rec = ''20''x; ';
|
||||
put ' do while(fread(filein)=0); ';
|
||||
put ' rc = fget(filein,rec,1); ';
|
||||
put ' rc = fput(fileid, rec); ';
|
||||
put ' rc =fwrite(fileid); ';
|
||||
put ' end; ';
|
||||
put ' rc = fclose(filein); ';
|
||||
put ' rc = fclose(fileid); ';
|
||||
put ' run; ';
|
||||
put ' filename _sjs clear; ';
|
||||
put ' data _null_; file &jref mod encoding=''utf-8''; ';
|
||||
put ' put "]"; ';
|
||||
put ' run; ';
|
||||
put ' %end; ';
|
||||
put '%end; ';
|
||||
put ' ';
|
||||
put '%else %if &action=CLOSE %then %do; ';
|
||||
put ' data _null_;file &jref encoding=''utf-8''; ';
|
||||
put ' put "}"; ';
|
||||
put ' run; ';
|
||||
put '%end; ';
|
||||
put '%mend; ';
|
||||
put '%macro mm_webout(action,ds,dslabel=,fref=_webout,fmt=Y); ';
|
||||
put '%global _webin_file_count _webin_fileref1 _webin_name1 _program _debug; ';
|
||||
put '%local i tempds; ';
|
||||
put ' ';
|
||||
put '%if &action=FETCH %then %do; ';
|
||||
put ' %if %str(&_debug) ge 131 %then %do; ';
|
||||
put ' options mprint notes mprintnest; ';
|
||||
put ' %end; ';
|
||||
put ' %let _webin_file_count=%eval(&_webin_file_count+0); ';
|
||||
put ' /* now read in the data */ ';
|
||||
put ' %do i=1 %to &_webin_file_count; ';
|
||||
put ' %if &_webin_file_count=1 %then %do; ';
|
||||
put ' %let _webin_fileref1=&_webin_fileref; ';
|
||||
put ' %let _webin_name1=&_webin_name; ';
|
||||
put ' %end; ';
|
||||
put ' data _null_; ';
|
||||
put ' infile &&_webin_fileref&i termstr=crlf; ';
|
||||
put ' input; ';
|
||||
put ' call symputx(''input_statement'',_infile_); ';
|
||||
put ' putlog "&&_webin_name&i input statement: " _infile_; ';
|
||||
put ' stop; ';
|
||||
put ' data &&_webin_name&i; ';
|
||||
put ' infile &&_webin_fileref&i firstobs=2 dsd termstr=crlf encoding=''utf-8''; ';
|
||||
put ' input &input_statement; ';
|
||||
put ' %if %str(&_debug) ge 131 %then %do; ';
|
||||
put ' if _n_<20 then putlog _infile_; ';
|
||||
put ' %end; ';
|
||||
put ' run; ';
|
||||
put ' %end; ';
|
||||
put '%end; ';
|
||||
put ' ';
|
||||
put '%else %if &action=OPEN %then %do; ';
|
||||
put ' /* fix encoding */ ';
|
||||
put ' OPTIONS NOBOMFILE; ';
|
||||
put ' data _null_; ';
|
||||
put ' rc = stpsrv_header(''Content-type'',"text/html; encoding=utf-8"); ';
|
||||
put ' run; ';
|
||||
put ' ';
|
||||
put ' /* setup json */ ';
|
||||
put ' data _null_;file &fref encoding=''utf-8''; ';
|
||||
put ' %if %str(&_debug) ge 131 %then %do; ';
|
||||
put ' put ''>>weboutBEGIN<<''; ';
|
||||
put ' %end; ';
|
||||
put ' put ''{"START_DTTM" : "'' "%sysfunc(datetime(),datetime20.3)" ''"''; ';
|
||||
put ' run; ';
|
||||
put ' ';
|
||||
put '%end; ';
|
||||
put ' ';
|
||||
put '%else %if &action=ARR or &action=OBJ %then %do; ';
|
||||
put ' %if &sysver=9.4 %then %do; ';
|
||||
put ' %mp_jsonout(&action,&ds,dslabel=&dslabel,fmt=&fmt ';
|
||||
put ' ,engine=PROCJSON,dbg=%str(&_debug) ';
|
||||
put ' ) ';
|
||||
put ' %end; ';
|
||||
put ' %else %do; ';
|
||||
put ' %mp_jsonout(&action,&ds,dslabel=&dslabel,fmt=&fmt ';
|
||||
put ' ,engine=DATASTEP,dbg=%str(&_debug) ';
|
||||
put ' ) ';
|
||||
put ' %end; ';
|
||||
put '%end; ';
|
||||
put '%else %if &action=CLOSE %then %do; ';
|
||||
put ' %if %str(&_debug) ge 131 %then %do; ';
|
||||
put ' /* if debug mode, send back first 10 records of each work table also */ ';
|
||||
put ' options obs=10; ';
|
||||
put ' data;run;%let tempds=%scan(&syslast,2,.); ';
|
||||
put ' ods output Members=&tempds; ';
|
||||
put ' proc datasets library=WORK memtype=data; ';
|
||||
put ' %local wtcnt;%let wtcnt=0; ';
|
||||
put ' data _null_; ';
|
||||
put ' set &tempds; ';
|
||||
put ' if not (name =:"DATA"); ';
|
||||
put ' i+1; ';
|
||||
put ' call symputx(''wt''!!left(i),name,''l''); ';
|
||||
put ' call symputx(''wtcnt'',i,''l''); ';
|
||||
put ' data _null_; file &fref encoding=''utf-8''; ';
|
||||
put ' put ",""WORK"":{"; ';
|
||||
put ' %do i=1 %to &wtcnt; ';
|
||||
put ' %let wt=&&wt&i; ';
|
||||
put ' proc contents noprint data=&wt ';
|
||||
put ' out=_data_ (keep=name type length format:); ';
|
||||
put ' run;%let tempds=%scan(&syslast,2,.); ';
|
||||
put ' data _null_; file &fref encoding=''utf-8''; ';
|
||||
put ' dsid=open("WORK.&wt",''is''); ';
|
||||
put ' nlobs=attrn(dsid,''NLOBS''); ';
|
||||
put ' nvars=attrn(dsid,''NVARS''); ';
|
||||
put ' rc=close(dsid); ';
|
||||
put ' if &i>1 then put '',''@; ';
|
||||
put ' put " ""&wt"" : {"; ';
|
||||
put ' put ''"nlobs":'' nlobs; ';
|
||||
put ' put '',"nvars":'' nvars; ';
|
||||
put ' %mp_jsonout(OBJ,&tempds,jref=&fref,dslabel=colattrs,engine=DATASTEP) ';
|
||||
put ' %mp_jsonout(OBJ,&wt,jref=&fref,dslabel=first10rows,engine=DATASTEP) ';
|
||||
put ' data _null_; file &fref encoding=''utf-8''; ';
|
||||
put ' put "}"; ';
|
||||
put ' %end; ';
|
||||
put ' data _null_; file &fref encoding=''utf-8''; ';
|
||||
put ' put "}"; ';
|
||||
put ' run; ';
|
||||
put ' %end; ';
|
||||
put ' /* close off json */ ';
|
||||
put ' data _null_;file &fref mod encoding=''utf-8''; ';
|
||||
put ' _PROGRAM=quote(trim(resolve(symget(''_PROGRAM'')))); ';
|
||||
put ' put ",""SYSUSERID"" : ""&sysuserid"" "; ';
|
||||
put ' put ",""MF_GETUSER"" : ""%mf_getuser()"" "; ';
|
||||
put ' put ",""_DEBUG"" : ""&_debug"" "; ';
|
||||
put ' _METAUSER=quote(trim(symget(''_METAUSER''))); ';
|
||||
put ' put ",""_METAUSER"": " _METAUSER; ';
|
||||
put ' _METAPERSON=quote(trim(symget(''_METAPERSON''))); ';
|
||||
put ' put '',"_METAPERSON": '' _METAPERSON; ';
|
||||
put ' put '',"_PROGRAM" : '' _PROGRAM ; ';
|
||||
put ' put ",""SYSCC"" : ""&syscc"" "; ';
|
||||
put ' put ",""SYSERRORTEXT"" : ""&syserrortext"" "; ';
|
||||
put ' put ",""SYSHOSTNAME"" : ""&syshostname"" "; ';
|
||||
put ' put ",""SYSJOBID"" : ""&sysjobid"" "; ';
|
||||
put ' put ",""SYSSITE"" : ""&syssite"" "; ';
|
||||
put ' put ",""SYSWARNINGTEXT"" : ""&syswarningtext"" "; ';
|
||||
put ' put '',"END_DTTM" : "'' "%sysfunc(datetime(),datetime20.3)" ''" ''; ';
|
||||
put ' put "}" @; ';
|
||||
put ' %if %str(&_debug) ge 131 %then %do; ';
|
||||
put ' put ''>>weboutEND<<''; ';
|
||||
put ' %end; ';
|
||||
put ' run; ';
|
||||
put '%end; ';
|
||||
put ' ';
|
||||
put '%mend; ';
|
||||
put ' ';
|
||||
put '%macro mf_getuser(type=META ';
|
||||
put ')/*/STORE SOURCE*/; ';
|
||||
put ' %local user metavar; ';
|
||||
put ' %if &type=OS %then %let metavar=_secureusername; ';
|
||||
put ' %else %let metavar=_metaperson; ';
|
||||
put ' ';
|
||||
put ' %if %symexist(SYS_COMPUTE_SESSION_OWNER) %then %let user=&SYS_COMPUTE_SESSION_OWNER; ';
|
||||
put ' %else %if %symexist(&metavar) %then %do; ';
|
||||
put ' %if %length(&&&metavar)=0 %then %let user=&sysuserid; ';
|
||||
put ' /* sometimes SAS will add @domain extension - remove for consistency */ ';
|
||||
put ' %else %let user=%scan(&&&metavar,1,@); ';
|
||||
put ' %end; ';
|
||||
put ' %else %let user=&sysuserid; ';
|
||||
put ' ';
|
||||
put ' %quote(&user) ';
|
||||
put ' ';
|
||||
put '%mend; ';
|
||||
/* WEBOUT END */
|
||||
put '%macro webout(action,ds,dslabel=,fmt=);';
|
||||
put ' %mm_webout(&action,ds=&ds,dslabel=&dslabel,fmt=&fmt)';
|
||||
put '%mend;';
|
||||
run;
|
||||
|
||||
/* add precode and code */
|
||||
%local work tmpfile;
|
||||
%let work=%sysfunc(pathname(work));
|
||||
%let tmpfile=__mm_createwebservice.temp;
|
||||
%local x fref freflist mod;
|
||||
%let freflist= &adapter &precode &code ;
|
||||
%do x=1 %to %sysfunc(countw(&freflist));
|
||||
%if &x>1 %then %let mod=mod;
|
||||
|
||||
%let fref=%scan(&freflist,&x);
|
||||
%put &sysmacroname: adding &fref;
|
||||
data _null_;
|
||||
file "&work/&tmpfile" lrecl=3000 &mod;
|
||||
infile &fref;
|
||||
input;
|
||||
put _infile_;
|
||||
run;
|
||||
%end;
|
||||
|
||||
/* create the metadata folder if not already there */
|
||||
%mm_createfolder(path=&path)
|
||||
%if &syscc ge 4 %then %return;
|
||||
|
||||
%if %upcase(&replace)=YES %then %do;
|
||||
%mm_deletestp(target=&path/&name)
|
||||
%end;
|
||||
|
||||
/* create the web service */
|
||||
%mm_createstp(stpname=&name
|
||||
,filename=&tmpfile
|
||||
,directory=&work
|
||||
,tree=&path
|
||||
,stpdesc=&desc
|
||||
,mDebug=&mdebug
|
||||
,server=&server
|
||||
,stptype=2)
|
||||
|
||||
/* find the web app url */
|
||||
%local url;
|
||||
%let url=localhost/SASStoredProcess;
|
||||
data _null_;
|
||||
length url $128;
|
||||
rc=METADATA_GETURI("Stored Process Web App",url);
|
||||
if rc=0 then call symputx('url',url,'l');
|
||||
run;
|
||||
|
||||
%put ;%put ;%put ;%put ;%put ;%put ;
|
||||
%put &sysmacroname: STP &name successfully created in &path;
|
||||
%put ;%put ;%put ;
|
||||
%put Check it out here:;
|
||||
%put ;%put ;%put ;
|
||||
%put &url?_PROGRAM=&path/&name;
|
||||
%put ;%put ;%put ;%put ;%put ;%put ;
|
||||
|
||||
%mend;
|
||||
71
meta/mm_deletedocument.sas
Normal file
71
meta/mm_deletedocument.sas
Normal file
@@ -0,0 +1,71 @@
|
||||
/**
|
||||
@file mm_deletedocument.sas
|
||||
@brief Deletes a Document using path as reference
|
||||
@details
|
||||
|
||||
Usage:
|
||||
|
||||
%mm_createdocument(tree=/User Folders/&sysuserid,name=MyNote)
|
||||
%mm_deletedocument(target=/User Folders/&sysuserid/MyNote)
|
||||
|
||||
<h4> Dependencies </h4>
|
||||
|
||||
@param target= full path to the document being deleted
|
||||
|
||||
@version 9.4
|
||||
@author Allan Bowe
|
||||
|
||||
**/
|
||||
|
||||
%macro mm_deletedocument(
|
||||
target=
|
||||
)/*/STORE SOURCE*/;
|
||||
|
||||
/**
|
||||
* Check document exist
|
||||
*/
|
||||
%local type;
|
||||
data _null_;
|
||||
length type uri $256;
|
||||
rc=metadata_pathobj("","&target",'Note',type,uri);
|
||||
call symputx('type',type,'l');
|
||||
call symputx('stpuri',uri,'l');
|
||||
run;
|
||||
%if &type ne Document %then %do;
|
||||
%put WARNING: No Document found at ⌖
|
||||
%return;
|
||||
%end;
|
||||
|
||||
filename __in temp lrecl=10000;
|
||||
filename __out temp lrecl=10000;
|
||||
data _null_ ;
|
||||
file __in ;
|
||||
put "<DeleteMetadata><Metadata><Document Id='&stpuri'/>";
|
||||
put "</Metadata><NS>SAS</NS><Flags>268436480</Flags><Options/>";
|
||||
put "</DeleteMetadata>";
|
||||
run ;
|
||||
proc metadata in=__in out=__out verbose;run;
|
||||
|
||||
/* list the result */
|
||||
data _null_;infile __out; input; list; run;
|
||||
|
||||
filename __in clear;
|
||||
filename __out clear;
|
||||
|
||||
/**
|
||||
* Check deletion
|
||||
*/
|
||||
%local isgone;
|
||||
data _null_;
|
||||
length type uri $256;
|
||||
call missing (of _all_);
|
||||
rc=metadata_pathobj("","&target",'Note',type,uri);
|
||||
call symputx('isgone',type,'l');
|
||||
run;
|
||||
%if &isgone = Document %then %do;
|
||||
%put %str(ERR)OR: Document not deleted from ⌖
|
||||
%let syscc=4;
|
||||
%return;
|
||||
%end;
|
||||
|
||||
%mend;
|
||||
70
meta/mm_deletestp.sas
Normal file
70
meta/mm_deletestp.sas
Normal file
@@ -0,0 +1,70 @@
|
||||
/**
|
||||
@file mm_deletestp.sas
|
||||
@brief Deletes a Stored Process using path as reference
|
||||
@details Will only delete the metadata, not any physical files associated.
|
||||
|
||||
Usage:
|
||||
|
||||
%mm_deletestp(target=/some/meta/path/myStoredProcess)
|
||||
|
||||
<h4> Dependencies </h4>
|
||||
|
||||
@param target= full path to the STP being deleted
|
||||
|
||||
@version 9.4
|
||||
@author Allan Bowe
|
||||
|
||||
**/
|
||||
|
||||
%macro mm_deletestp(
|
||||
target=
|
||||
)/*/STORE SOURCE*/;
|
||||
|
||||
/**
|
||||
* Check STP does exist
|
||||
*/
|
||||
%local cmtype;
|
||||
data _null_;
|
||||
length type uri $256;
|
||||
rc=metadata_pathobj("","&target",'StoredProcess',type,uri);
|
||||
call symputx('cmtype',type,'l');
|
||||
call symputx('stpuri',uri,'l');
|
||||
run;
|
||||
%if &cmtype ne ClassifierMap %then %do;
|
||||
%put NOTE: No Stored Process found at ⌖
|
||||
%return;
|
||||
%end;
|
||||
|
||||
filename __in temp lrecl=10000;
|
||||
filename __out temp lrecl=10000;
|
||||
data _null_ ;
|
||||
file __in ;
|
||||
put "<DeleteMetadata><Metadata><ClassifierMap Id='&stpuri'/>";
|
||||
put "</Metadata><NS>SAS</NS><Flags>268436480</Flags><Options/>";
|
||||
put "</DeleteMetadata>";
|
||||
run ;
|
||||
proc metadata in=__in out=__out verbose;run;
|
||||
|
||||
/* list the result */
|
||||
data _null_;infile __out; input; list; run;
|
||||
|
||||
filename __in clear;
|
||||
filename __out clear;
|
||||
|
||||
/**
|
||||
* Check deletion
|
||||
*/
|
||||
%local isgone;
|
||||
data _null_;
|
||||
length type uri $256;
|
||||
call missing (of _all_);
|
||||
rc=metadata_pathobj("","&target",'Note',type,uri);
|
||||
call symputx('isgone',type,'l');
|
||||
run;
|
||||
%if &isgone = ClassifierMap %then %do;
|
||||
%put %str(ERR)OR: STP not deleted from ⌖
|
||||
%let syscc=4;
|
||||
%return;
|
||||
%end;
|
||||
|
||||
%mend;
|
||||
116
meta/mm_getauthinfo.sas
Normal file
116
meta/mm_getauthinfo.sas
Normal file
@@ -0,0 +1,116 @@
|
||||
/**
|
||||
@file mm_getauthinfo.sas
|
||||
@brief extracts authentication info
|
||||
@details usage:
|
||||
|
||||
%mm_getauthinfo(outds=auths)
|
||||
|
||||
@param outds= the ONE LEVEL work dataset to create
|
||||
|
||||
<h4> Dependencies </h4>
|
||||
@li mm_getobjects.sas
|
||||
@li mf_getuniquefileref.sas
|
||||
@li mm_getdetails.sas
|
||||
|
||||
@version 9.4
|
||||
@author Allan Bowe
|
||||
|
||||
**/
|
||||
|
||||
%macro mm_getauthinfo(outds=mm_getauthinfo
|
||||
)/*/STORE SOURCE*/;
|
||||
|
||||
%if %length(&outds)>30 %then %do;
|
||||
%put %str(ERR)OR: Temp tables are created with the &outds prefix, which therefore
|
||||
needs to be 30 characters or less;
|
||||
%return;
|
||||
%end;
|
||||
%if %index(&outds,'.')>0 %then %do;
|
||||
%put %str(ERR)OR: Table &outds should be ONE LEVEL (no library);
|
||||
%return;
|
||||
%end;
|
||||
|
||||
%mm_getobjects(type=Login,outds=&outds.0)
|
||||
|
||||
%local fileref;
|
||||
%let fileref=%mf_getuniquefileref();
|
||||
|
||||
data _null_;
|
||||
file &fileref;
|
||||
set &outds.0 end=last;
|
||||
/* run macro */
|
||||
str=cats('%mm_getdetails(uri=',id,",outattrs=&outds.d",_n_
|
||||
,",outassocs=&outds.a",_n_,")");
|
||||
put str;
|
||||
/* transpose attributes */
|
||||
str=cats("proc transpose data=&outds.d",_n_,"(drop=type) out=&outds.da"
|
||||
,_n_,"(drop=_name_);var value;id name;run;");
|
||||
put str;
|
||||
/* add extra info to attributes */
|
||||
str=cats("data &outds.da",_n_,";length login_id login_name $256; login_id="
|
||||
,quote(trim(id)),";set &outds.da",_n_
|
||||
,";login_name=trim(subpad(name,1,256));drop name;run;");
|
||||
put str;
|
||||
/* add extra info to associations */
|
||||
str=cats("data &outds.a",_n_,";length login_id login_name $256; login_id="
|
||||
,quote(trim(id)),";login_name=",quote(trim(name))
|
||||
,";set &outds.a",_n_,";run;");
|
||||
put str;
|
||||
if last then do;
|
||||
/* collate attributes */
|
||||
str=cats("data &outds._logat; set &outds.da1-&outds.da",_n_,";run;");
|
||||
put str;
|
||||
/* collate associations */
|
||||
str=cats("data &outds._logas; set &outds.a1-&outds.a",_n_,";run;");
|
||||
put str;
|
||||
/* tidy up */
|
||||
str=cats("proc delete data=&outds.da1-&outds.da",_n_,";run;");
|
||||
put str;
|
||||
str=cats("proc delete data=&outds.d1-&outds.d",_n_,";run;");
|
||||
put str;
|
||||
str=cats("proc delete data=&outds.a1-&outds.a",_n_,";run;");
|
||||
put str;
|
||||
end;
|
||||
run;
|
||||
%inc &fileref;
|
||||
|
||||
/* get libraries */
|
||||
proc sort data=&outds._logas(where=(assoc='Libraries')) out=&outds._temp;
|
||||
by login_id;
|
||||
data &outds._temp;
|
||||
set &outds._temp;
|
||||
by login_id;
|
||||
length library_list $32767;
|
||||
retain library_list;
|
||||
if first.login_id then library_list=name;
|
||||
else library_list=catx(' !! ',library_list,name);
|
||||
proc sql;
|
||||
/* get auth domain */
|
||||
create table &outds._dom as
|
||||
select login_id,name as domain
|
||||
from &outds._logas
|
||||
where assoc='Domain';
|
||||
create unique index login_id on &outds._dom(login_id);
|
||||
/* join it all together */
|
||||
create table &outds._logins as
|
||||
select a.*
|
||||
,c.domain
|
||||
,b.library_list
|
||||
from &outds._logat (drop=ishidden lockedby usageversion publictype) a
|
||||
left join &outds._temp b
|
||||
on a.login_id=b.login_id
|
||||
left join &outds._dom c
|
||||
on a.login_id=c.login_id;
|
||||
drop table &outds._temp;
|
||||
drop table &outds._logat;
|
||||
drop table &outds._logas;
|
||||
|
||||
data _null_;
|
||||
infile &fileref;
|
||||
if _n_=1 then putlog // "Now executing the following code:" //;
|
||||
input; putlog _infile_;
|
||||
run;
|
||||
|
||||
filename &fileref clear;
|
||||
|
||||
%mend;
|
||||
53
meta/mm_getcols.sas
Normal file
53
meta/mm_getcols.sas
Normal file
@@ -0,0 +1,53 @@
|
||||
/**
|
||||
@file
|
||||
@brief Creates a dataset with all metadata columns for a particular table
|
||||
@details
|
||||
|
||||
usage:
|
||||
|
||||
%mm_getcols(tableuri=A5X8AHW1.B40001S5)
|
||||
|
||||
@param outds the dataset to create that contains the list of columns
|
||||
@param uri the uri of the table for which to return columns
|
||||
|
||||
@returns outds dataset containing all columns, specifically:
|
||||
- colname
|
||||
- coluri
|
||||
- coldesc
|
||||
|
||||
@version 9.2
|
||||
@author Allan Bowe
|
||||
|
||||
**/
|
||||
|
||||
%macro mm_getcols(
|
||||
tableuri=
|
||||
,outds=work.mm_getcols
|
||||
)/*/STORE SOURCE*/;
|
||||
|
||||
data &outds;
|
||||
keep col: SAS:;
|
||||
length assoc uri coluri colname coldesc SASColumnType SASFormat SASInformat
|
||||
SASPrecision SASColumnLength $256;
|
||||
call missing (of _all_);
|
||||
uri=symget('tableuri');
|
||||
n=1;
|
||||
do while (metadata_getnasn(uri,'Columns',n,coluri)>0);
|
||||
rc3=metadata_getattr(coluri,"Name",colname);
|
||||
rc3=metadata_getattr(coluri,"Desc",coldesc);
|
||||
rc4=metadata_getattr(coluri,"SASColumnType",SASColumnType);
|
||||
rc5=metadata_getattr(coluri,"SASFormat",SASFormat);
|
||||
rc6=metadata_getattr(coluri,"SASInformat",SASInformat);
|
||||
rc7=metadata_getattr(coluri,"SASPrecision",SASPrecision);
|
||||
rc8=metadata_getattr(coluri,"SASColumnLength",SASColumnLength);
|
||||
output;
|
||||
call missing(colname,coldesc,SASColumnType,SASFormat,SASInformat
|
||||
,SASPrecision,SASColumnLength);
|
||||
n+1;
|
||||
end;
|
||||
run;
|
||||
proc sort;
|
||||
by colname;
|
||||
run;
|
||||
|
||||
%mend;
|
||||
65
meta/mm_getdetails.sas
Normal file
65
meta/mm_getdetails.sas
Normal file
@@ -0,0 +1,65 @@
|
||||
/**
|
||||
@file mm_getdetails.sas
|
||||
@brief extracts metadata attributes and associations for a particular uri
|
||||
|
||||
@param uri the metadata object for which to return attributes / associations
|
||||
@param outattrs= the dataset to create that contains the list of attributes
|
||||
@param outassocs= the dataset to contain the list of associations
|
||||
|
||||
@version 9.2
|
||||
@author Allan Bowe
|
||||
|
||||
**/
|
||||
|
||||
%macro mm_getdetails(uri
|
||||
,outattrs=work.attributes
|
||||
,outassocs=work.associations
|
||||
)/*/STORE SOURCE*/;
|
||||
|
||||
data &outassocs;
|
||||
keep assoc assocuri name;
|
||||
length assoc assocuri name $256;
|
||||
call missing(of _all_);
|
||||
rc1=1;n1=1;
|
||||
do while(rc1>0);
|
||||
/* Walk through all possible associations of this object. */
|
||||
rc1=metadata_getnasl("&uri",n1,assoc);
|
||||
rc2=1;n2=1;
|
||||
do while(rc2>0);
|
||||
/* Walk through all the associations on this machine object. */
|
||||
rc2=metadata_getnasn("&uri",trim(assoc),n2,assocuri);
|
||||
if (rc2>0) then do;
|
||||
rc3=metadata_getattr(assocuri,"Name",name);
|
||||
output;
|
||||
end;
|
||||
call missing(name,assocuri);
|
||||
n2+1;
|
||||
end;
|
||||
n1+1;
|
||||
end;
|
||||
run;
|
||||
proc sort;
|
||||
by assoc name;
|
||||
run;
|
||||
|
||||
data &outattrs;
|
||||
keep type name value;
|
||||
length type $4 name $256 value $32767;
|
||||
rc1=1;n1=1;type='Prop';
|
||||
do while(rc1>0);
|
||||
rc1=metadata_getnprp("&uri",n1,name,value);
|
||||
if rc1>0 then output;
|
||||
n1+1;
|
||||
end;
|
||||
rc1=1;n1=1;type='Attr';
|
||||
do while(rc1>0);
|
||||
rc1=metadata_getnatr("&uri",n1,name,value);
|
||||
if rc1>0 then output;
|
||||
n1+1;
|
||||
end;
|
||||
run;
|
||||
proc sort;
|
||||
by type name;
|
||||
run;
|
||||
|
||||
%mend;
|
||||
55
meta/mm_getdirectories.sas
Executable file
55
meta/mm_getdirectories.sas
Executable file
@@ -0,0 +1,55 @@
|
||||
/**
|
||||
@file
|
||||
@brief Returns a dataset with the meta directory object for a physical path
|
||||
@details Provide a file path to get matching directory objects, or leave
|
||||
blank to return all directories. The Directory object is used to reference
|
||||
a physical filepath (eg when registering a .sas program in a Stored process)
|
||||
|
||||
@param path= the physical path for which to return a meta Directory object
|
||||
@param outds= the dataset to create that contains the list of directories
|
||||
@param mDebug= set to 1 to show debug messages in the log
|
||||
|
||||
@returns outds dataset containing the following columns:
|
||||
- directoryuri
|
||||
- groupname
|
||||
- groupdesc
|
||||
|
||||
@version 9.2
|
||||
@author Allan Bowe
|
||||
|
||||
**/
|
||||
|
||||
%macro mm_getDirectories(
|
||||
path=
|
||||
,outds=work.mm_getDirectories
|
||||
,mDebug=0
|
||||
)/*/STORE SOURCE*/;
|
||||
|
||||
%local mD;
|
||||
%if &mDebug=1 %then %let mD=;
|
||||
%else %let mD=%str(*);
|
||||
%&mD.put Executing mm_getDirectories.sas;
|
||||
%&mD.put _local_;
|
||||
|
||||
data &outds (keep=directoryuri name directoryname directorydesc );
|
||||
length directoryuri name directoryname directorydesc $256;
|
||||
call missing(of _all_);
|
||||
__i+1;
|
||||
%if %length(&path)=0 %then %do;
|
||||
do while
|
||||
(metadata_getnobj("omsobj:Directory?@Id contains '.'",__i,directoryuri)>0);
|
||||
%end; %else %do;
|
||||
do while
|
||||
(metadata_getnobj("omsobj:Directory?@DirectoryName='&path'",__i,directoryuri)>0);
|
||||
%end;
|
||||
__rc1=metadata_getattr(directoryuri, "Name", name);
|
||||
__rc2=metadata_getattr(directoryuri, "DirectoryName", directoryname);
|
||||
__rc3=metadata_getattr(directoryuri, "Desc", directorydesc);
|
||||
&mD.putlog (_all_) (=);
|
||||
drop __:;
|
||||
__i+1;
|
||||
if sum(of __rc1-__rc3)=0 then output;
|
||||
end;
|
||||
run;
|
||||
|
||||
%mend;
|
||||
145
meta/mm_getdocument.sas
Normal file
145
meta/mm_getdocument.sas
Normal file
@@ -0,0 +1,145 @@
|
||||
/**
|
||||
@file
|
||||
@brief Writes the TextStore of a Document Object to an external file
|
||||
@details If the document exists, and has a textstore object, the contents
|
||||
of that textstore are written to an external file.
|
||||
|
||||
usage:
|
||||
|
||||
%mm_getdocument(tree=/some/meta/path
|
||||
,name=someDocument
|
||||
,outref=/some/unquoted/filename.ext
|
||||
)
|
||||
|
||||
<h4> Dependencies </h4>
|
||||
@li mp_abort.sas
|
||||
|
||||
@param tree= The metadata path of the document
|
||||
@param name= Document object name.
|
||||
@param outref= full and unquoted path to the desired text file. This will be
|
||||
overwritten if it already exists.
|
||||
|
||||
@author Allan Bowe
|
||||
|
||||
**/
|
||||
|
||||
%macro mm_getdocument(
|
||||
tree=/User Folders/sasdemo
|
||||
,name=myNote
|
||||
,outref=%sysfunc(pathname(work))/mm_getdocument.txt
|
||||
,mDebug=1
|
||||
);
|
||||
|
||||
%local mD;
|
||||
%if &mDebug=1 %then %let mD=;
|
||||
%else %let mD=%str(*);
|
||||
%&mD.put Executing &sysmacroname..sas;
|
||||
%&mD.put _local_;
|
||||
|
||||
/**
|
||||
* check tree exists
|
||||
*/
|
||||
|
||||
data _null_;
|
||||
length type uri $256;
|
||||
rc=metadata_pathobj("","&tree","Folder",type,uri);
|
||||
call symputx('type',type,'l');
|
||||
call symputx('treeuri',uri,'l');
|
||||
run;
|
||||
|
||||
%mp_abort(
|
||||
iftrue= (&type ne Tree)
|
||||
,mac=mm_getdocument.sas
|
||||
,msg=Tree &tree does not exist!
|
||||
)
|
||||
|
||||
/**
|
||||
* Check object exists
|
||||
*/
|
||||
data _null_;
|
||||
length type docuri tsuri tsid $256 ;
|
||||
rc1=metadata_pathobj("","&tree/&name","Note",type,docuri);
|
||||
rc2=metadata_getnasn(docuri,"Notes",1,tsuri);
|
||||
rc3=metadata_getattr(tsuri,"Id",tsid);
|
||||
call symputx('type',type,'l');
|
||||
call symputx("tsid",tsid,'l');
|
||||
putlog (_all_)(=);
|
||||
run;
|
||||
|
||||
%mp_abort(
|
||||
iftrue= (&type ne Document)
|
||||
,mac=mm_getdocument.sas
|
||||
,msg=Document &name could not be found in &tree!
|
||||
)
|
||||
|
||||
/**
|
||||
* Now we can extract the textstore
|
||||
*/
|
||||
filename __getdoc temp lrecl=10000000;
|
||||
proc metadata
|
||||
in="<GetMetadata><Reposid>$METAREPOSITORY</Reposid>
|
||||
<Metadata><TextStore Id='&tsid'/></Metadata>
|
||||
<Ns>SAS</Ns><Flags>1</Flags><Options/></GetMetadata>"
|
||||
out=__getdoc ;
|
||||
run;
|
||||
|
||||
/* find the beginning of the text */
|
||||
data _null_;
|
||||
infile __getdoc lrecl=10000;
|
||||
input;
|
||||
start=index(_infile_,'StoredText="');
|
||||
if start then do;
|
||||
call symputx("start",start+11);
|
||||
put start= "type=&type";
|
||||
putlog '"' _infile_ '"';
|
||||
end;
|
||||
stop;
|
||||
|
||||
/* read the content, byte by byte, resolving escaped chars */
|
||||
filename __outdoc "&outref" lrecl=100000;
|
||||
data _null_;
|
||||
length filein 8 fileid 8;
|
||||
filein = fopen("__getdoc","I",1,"B");
|
||||
fileid = fopen("__outdoc","O",1,"B");
|
||||
rec = "20"x;
|
||||
length entity $6;
|
||||
do while(fread(filein)=0);
|
||||
x+1;
|
||||
if x>&start then do;
|
||||
rc = fget(filein,rec,1);
|
||||
if rec='"' then leave;
|
||||
else if rec="&" then do;
|
||||
entity=rec;
|
||||
do until (rec=";");
|
||||
if fread(filein) ne 0 then goto getout;
|
||||
rc = fget(filein,rec,1);
|
||||
entity=cats(entity,rec);
|
||||
end;
|
||||
select (entity);
|
||||
when ('&' ) rec='&' ;
|
||||
when ('<' ) rec='<' ;
|
||||
when ('>' ) rec='>' ;
|
||||
when (''') rec="'" ;
|
||||
when ('"') rec='"' ;
|
||||
when ('
') rec='0A'x;
|
||||
when ('
') rec='0D'x;
|
||||
when ('$' ) rec='$' ;
|
||||
otherwise putlog "WARNING: missing value for " entity=;
|
||||
end;
|
||||
rc =fput(fileid, substr(rec,1,1));
|
||||
rc =fwrite(fileid);
|
||||
end;
|
||||
else do;
|
||||
rc =fput(fileid,rec);
|
||||
rc =fwrite(fileid);
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
getout:
|
||||
rc=fclose(filein);
|
||||
rc=fclose(fileid);
|
||||
run;
|
||||
filename __getdoc clear;
|
||||
filename __outdoc clear;
|
||||
|
||||
%mend;
|
||||
88
meta/mm_getfoldertree.sas
Normal file
88
meta/mm_getfoldertree.sas
Normal file
@@ -0,0 +1,88 @@
|
||||
/**
|
||||
@file mm_getfoldertree.sas
|
||||
@brief Returns all folders / subfolder content for a particular root
|
||||
@details Shows all members and SubTrees recursively for a particular root.
|
||||
Note - for big sites, this returns a lot of data! So you may wish to reduce
|
||||
the logging to speed up the process (see example below)
|
||||
Usage:
|
||||
|
||||
options ps=max nonotes nosource;
|
||||
%mm_getfoldertree(root=/My/Meta/Path, outds=iwantthisdataset)
|
||||
options notes source;
|
||||
|
||||
@param root= the parent folder under which to return all contents
|
||||
@param outds= the dataset to create that contains the list of directories
|
||||
@param mDebug= set to 1 to show debug messages in the log
|
||||
|
||||
<h4> Dependencies </h4>
|
||||
|
||||
@version 9.4
|
||||
@author Allan Bowe
|
||||
|
||||
**/
|
||||
%macro mm_getfoldertree(
|
||||
root=
|
||||
,outds=work.mm_getfoldertree
|
||||
,mDebug=0
|
||||
,depth=50 /* how many nested folders to query */
|
||||
,level=1 /* system var - to track current level depth */
|
||||
,append=NO /* system var - when YES means appending within nested loop */
|
||||
)/*/STORE SOURCE*/;
|
||||
|
||||
%if &level>&depth %then %return;
|
||||
|
||||
%local mD;
|
||||
%if &mDebug=1 %then %let mD=;
|
||||
%else %let mD=%str(*);
|
||||
%&mD.put Executing &sysmacroname;
|
||||
%&mD.put _local_;
|
||||
|
||||
%if &append=NO %then %do;
|
||||
/* ensure table doesn't exist already */
|
||||
data &outds; run;
|
||||
proc sql; drop table &outds;
|
||||
%end;
|
||||
|
||||
/* get folder contents */
|
||||
data &outds.TMP/view=&outds.TMP;
|
||||
length metauri pathuri $64 name $256 path $1024
|
||||
assoctype publictype MetadataUpdated MetadataCreated $32;
|
||||
keep metauri assoctype name publictype MetadataUpdated MetadataCreated path;
|
||||
call missing(of _all_);
|
||||
path="&root";
|
||||
rc=metadata_pathobj("",path,"Folder",publictype,pathuri);
|
||||
if publictype ne 'Tree' then do;
|
||||
putlog "%str(WAR)NING: Tree " path 'does not exist!' publictype=;
|
||||
stop;
|
||||
end;
|
||||
__n1=1;
|
||||
do while(metadata_getnasl(pathuri,__n1,assoctype)>0);
|
||||
__n1+1;
|
||||
/* Walk through all possible associations of this object. */
|
||||
__n2=1;
|
||||
if assoctype in ('Members','SubTrees') then
|
||||
do while(metadata_getnasn(pathuri,assoctype,__n2,metauri)>0);
|
||||
__n2+1;
|
||||
call missing(name,publictype,MetadataUpdated,MetadataCreated);
|
||||
__rc1=metadata_getattr(metauri,"Name", name);
|
||||
__rc2=metadata_getattr(metauri,"MetadataUpdated", MetadataUpdated);
|
||||
__rc3=metadata_getattr(metauri,"MetadataCreated", MetadataCreated);
|
||||
__rc4=metadata_getattr(metauri,"PublicType", PublicType);
|
||||
output;
|
||||
end;
|
||||
n1+1;
|
||||
end;
|
||||
drop __:;
|
||||
run;
|
||||
|
||||
proc append base=&outds data=&outds.TMP;
|
||||
run;
|
||||
|
||||
data _null_;
|
||||
set &outds.TMP(where=(assoctype='SubTrees'));
|
||||
call execute('%mm_getfoldertree(root='
|
||||
!!cats(path,"/",name)!!",outds=&outds,mDebug=&mdebug,depth=&depth"
|
||||
!!",level=%eval(&level+1),append=YES)");
|
||||
run;
|
||||
|
||||
%mend;
|
||||
72
meta/mm_getgroupmembers.sas
Executable file
72
meta/mm_getgroupmembers.sas
Executable file
@@ -0,0 +1,72 @@
|
||||
/**
|
||||
@file
|
||||
@brief Creates dataset with all members of a metadata group
|
||||
@details
|
||||
|
||||
usage:
|
||||
|
||||
%mm_getgroupmembers(someGroupName
|
||||
,outds=work.mm_getgroupmembers
|
||||
,emails=YES)
|
||||
|
||||
@param group metadata group for which to bring back members
|
||||
@param outds= the dataset to create that contains the list of members
|
||||
@param emails= set to YES to bring back email addresses
|
||||
@param id= set to yes if passing an ID rather than a group name
|
||||
|
||||
@returns outds dataset containing all members of the metadata group
|
||||
|
||||
@version 9.2
|
||||
@author Allan Bowe
|
||||
|
||||
**/
|
||||
|
||||
%macro mm_getgroupmembers(
|
||||
group /* metadata group for which to bring back members */
|
||||
,outds=work.mm_getgroupmembers /* output dataset to contain the results */
|
||||
,emails=NO /* set to yes to bring back emails also */
|
||||
,id=NO /* set to yes if passing an ID rather than group name */
|
||||
)/*/STORE SOURCE*/;
|
||||
|
||||
data &outds ;
|
||||
attrib uriGrp uriMem GroupId GroupName Group_or_Role MemberName MemberType
|
||||
euri email length=$64
|
||||
GroupDesc length=$256
|
||||
rcGrp rcMem rc i j length=3;
|
||||
call missing (of _all_);
|
||||
drop uriGrp uriMem rcGrp rcMem rc i j arc ;
|
||||
|
||||
i=1;
|
||||
* Grab the URI for the first Group ;
|
||||
%if &id=NO %then %do;
|
||||
rcGrp=metadata_getnobj("omsobj:IdentityGroup?@Name='&group'",i,uriGrp);
|
||||
%end;
|
||||
%else %do;
|
||||
rcGrp=metadata_getnobj("omsobj:IdentityGroup?@Id='&group'",i,uriGrp);
|
||||
%end;
|
||||
* If Group found, enter do loop ;
|
||||
if rcGrp>0 then do;
|
||||
call missing (rcMem,uriMem,GroupId,GroupName,Group_or_Role
|
||||
,MemberName,MemberType);
|
||||
* get group info ;
|
||||
rc = metadata_getattr(uriGrp,"Id",GroupId);
|
||||
rc = metadata_getattr(uriGrp,"Name",GroupName);
|
||||
rc = metadata_getattr(uriGrp,"PublicType",Group_or_Role);
|
||||
rc = metadata_getattr(uriGrp,"Desc",GroupDesc);
|
||||
j=1;
|
||||
do while (metadata_getnasn(uriGrp,"MemberIdentities",j,uriMem) > 0);
|
||||
call missing (MemberName, MemberType, email);
|
||||
rc = metadata_getattr(uriMem,"Name",MemberName);
|
||||
rc = metadata_getattr(uriMem,"PublicType",MemberType);
|
||||
if membertype='User' and "&emails"='YES' then do;
|
||||
if metadata_getnasn(uriMem,"EmailAddresses",1,euri)>0 then do;
|
||||
arc=metadata_getattr(euri,"Address",email);
|
||||
end;
|
||||
end;
|
||||
output;
|
||||
j+1;
|
||||
end;
|
||||
end;
|
||||
run;
|
||||
|
||||
%mend;
|
||||
95
meta/mm_getgroups.sas
Executable file
95
meta/mm_getgroups.sas
Executable file
@@ -0,0 +1,95 @@
|
||||
/**
|
||||
@file
|
||||
@brief Creates dataset with all groups or just those for a particular user
|
||||
@details Provide a metadata user to get groups for just that user, or leave
|
||||
blank to return all groups.
|
||||
Usage:
|
||||
|
||||
- all groups
|
||||
%mm_getGroups()
|
||||
|
||||
- all groups for a particular user
|
||||
%mm_getgroups(user=&sysuserid)
|
||||
|
||||
@param user= the metadata user to return groups for. Leave blank for all
|
||||
groups.
|
||||
@param outds= the dataset to create that contains the list of groups
|
||||
@param repo= the metadata repository that contains the user/group information
|
||||
@param mDebug= set to 1 to show debug messages in the log
|
||||
|
||||
@returns outds dataset containing all groups in a column named "metagroup"
|
||||
- groupuri
|
||||
- groupname
|
||||
- groupdesc
|
||||
|
||||
@version 9.2
|
||||
@author Allan Bowe
|
||||
|
||||
**/
|
||||
|
||||
%macro mm_getGroups(
|
||||
user=
|
||||
,outds=work.mm_getGroups
|
||||
,repo=foundation
|
||||
,mDebug=0
|
||||
)/*/STORE SOURCE*/;
|
||||
|
||||
%local mD oldrepo;
|
||||
%let oldrepo=%sysfunc(getoption(metarepository));
|
||||
%if &mDebug=1 %then %let mD=;
|
||||
%else %let mD=%str(*);
|
||||
%&mD.put Executing mm_getGroups.sas;
|
||||
%&mD.put _local_;
|
||||
|
||||
/* on some sites, user / group info is in a different metadata repo to the default */
|
||||
%if &oldrepo ne &repo %then %do;
|
||||
options metarepository=&repo;
|
||||
%end;
|
||||
|
||||
%if %length(&user)=0 %then %do;
|
||||
data &outds (keep=groupuri groupname groupdesc);
|
||||
length groupuri groupname groupdesc group_or_role $256;
|
||||
call missing(of _all_);
|
||||
i+1;
|
||||
do while
|
||||
(metadata_getnobj("omsobj:IdentityGroup?@Id contains '.'",i,groupuri)>0);
|
||||
rc=metadata_getattr(groupuri, "Name", groupname);
|
||||
rc=metadata_getattr(groupuri, "Desc", groupdesc);
|
||||
rc=metadata_getattr(groupuri,"PublicType",group_or_role);
|
||||
if Group_or_Role = 'UserGroup' then output;
|
||||
i+1;
|
||||
end;
|
||||
run;
|
||||
%end;
|
||||
%else %do;
|
||||
data &outds (keep=groupuri groupname groupdesc);
|
||||
length uri groupuri groupname groupdesc group_or_role $256;
|
||||
call missing(of _all_);
|
||||
rc=metadata_getnobj("omsobj:Person?@Name='&user'",1,uri);
|
||||
if rc<=0 then do;
|
||||
putlog "%str(WARN)ING: rc=" rc "&user not found "
|
||||
", or there was an issue reading the repository.";
|
||||
stop;
|
||||
end;
|
||||
a=1;
|
||||
grpassn=metadata_getnasn(uri,"IdentityGroups",a,groupuri);
|
||||
if grpassn in (-3,-4) then do;
|
||||
putlog "%str(WARN)ING: No metadata groups found for &user";
|
||||
output;
|
||||
end;
|
||||
else do while (grpassn > 0);
|
||||
rc=metadata_getattr(groupuri, "Name", groupname);
|
||||
rc=metadata_getattr(groupuri, "Desc", groupdesc);
|
||||
a+1;
|
||||
rc=metadata_getattr(groupuri,"PublicType",group_or_role);
|
||||
if Group_or_Role = 'UserGroup' then output;
|
||||
grpassn=metadata_getnasn(uri,"IdentityGroups",a,groupuri);
|
||||
end;
|
||||
run;
|
||||
%end;
|
||||
|
||||
%if &oldrepo ne &repo %then %do;
|
||||
options metarepository=&oldrepo;
|
||||
%end;
|
||||
|
||||
%mend;
|
||||
97
meta/mm_getlibs.sas
Executable file
97
meta/mm_getlibs.sas
Executable file
@@ -0,0 +1,97 @@
|
||||
/**
|
||||
@file
|
||||
@brief Creates a dataset with all metadata libraries
|
||||
@details Will only show the libraries to which a user has the requisite
|
||||
metadata access.
|
||||
|
||||
@param outds the dataset to create that contains the list of libraries
|
||||
@param mDebug set to anything but * or 0 to show debug messages in the log
|
||||
|
||||
@returns outds dataset containing all groups in a column named "metagroup"
|
||||
(defaults to work.mm_getlibs). The following columns are provided:
|
||||
- LibraryId
|
||||
- LibraryName
|
||||
- LibraryRef
|
||||
- Engine
|
||||
|
||||
@warning The following filenames are created and then de-assigned:
|
||||
|
||||
filename sxlemap clear;
|
||||
filename response clear;
|
||||
libname _XML_ clear;
|
||||
|
||||
@version 9.2
|
||||
@author Allan Bowe
|
||||
|
||||
**/
|
||||
|
||||
%macro mm_getlibs(
|
||||
outds=work.mm_getLibs
|
||||
)/*/STORE SOURCE*/;
|
||||
|
||||
/*
|
||||
flags:
|
||||
|
||||
OMI_SUCCINCT (2048) Do not return attributes with null values.
|
||||
OMI_GET_METADATA (256) Executes a GetMetadata call for each object that
|
||||
is returned by the GetMetadataObjects method.
|
||||
OMI_ALL_SIMPLE (8) Gets all of the attributes of the requested object.
|
||||
*/
|
||||
data _null_;
|
||||
flags=2048+256+8;
|
||||
call symputx('flags',flags,'l');
|
||||
run;
|
||||
|
||||
* use a temporary fileref to hold the response;
|
||||
filename response temp;
|
||||
/* get list of libraries */
|
||||
proc metadata in=
|
||||
'<GetMetadataObjects>
|
||||
<Reposid>$METAREPOSITORY</Reposid>
|
||||
<Type>SASLibrary</Type>
|
||||
<Objects/>
|
||||
<NS>SAS</NS>
|
||||
<Flags>&flags</Flags>
|
||||
<Options/>
|
||||
</GetMetadataObjects>'
|
||||
out=response;
|
||||
run;
|
||||
|
||||
/* write the response to the log for debugging */
|
||||
data _null_;
|
||||
infile response lrecl=32767;
|
||||
input;
|
||||
put _infile_;
|
||||
run;
|
||||
|
||||
/* create an XML map to read the response */
|
||||
filename sxlemap temp;
|
||||
data _null_;
|
||||
file sxlemap;
|
||||
put '<SXLEMAP version="1.2" name="SASLibrary">';
|
||||
put '<TABLE name="SASLibrary">';
|
||||
put '<TABLE-PATH syntax="XPath">//Objects/SASLibrary</TABLE-PATH>';
|
||||
put '<COLUMN name="LibraryId">><LENGTH>17</LENGTH>';
|
||||
put '<PATH syntax="XPath">//Objects/SASLibrary/@Id</PATH></COLUMN>';
|
||||
put '<COLUMN name="LibraryName"><LENGTH>256</LENGTH>>';
|
||||
put '<PATH syntax="XPath">//Objects/SASLibrary/@Name</PATH></COLUMN>';
|
||||
put '<COLUMN name="LibraryRef"><LENGTH>8</LENGTH>';
|
||||
put '<PATH syntax="XPath">//Objects/SASLibrary/@Libref</PATH></COLUMN>';
|
||||
put '<COLUMN name="Engine">><LENGTH>12</LENGTH>';
|
||||
put '<PATH syntax="XPath">//Objects/SASLibrary/@Engine</PATH></COLUMN>';
|
||||
put '</TABLE></SXLEMAP>';
|
||||
run;
|
||||
libname _XML_ xml xmlfileref=response xmlmap=sxlemap;
|
||||
|
||||
/* sort the response by library name */
|
||||
proc sort data=_XML_.saslibrary out=&outds;
|
||||
by libraryname;
|
||||
run;
|
||||
|
||||
|
||||
/* clear references */
|
||||
filename sxlemap clear;
|
||||
filename response clear;
|
||||
libname _XML_ clear;
|
||||
|
||||
%mend;
|
||||
69
meta/mm_getobjects.sas
Normal file
69
meta/mm_getobjects.sas
Normal file
@@ -0,0 +1,69 @@
|
||||
/**
|
||||
@file
|
||||
@brief Creates a dataset with all metadata objects for a particular type
|
||||
|
||||
@param type= the metadata type for which to return all objects
|
||||
@param outds= the dataset to create that contains the list of types
|
||||
|
||||
@returns outds dataset containing all objects
|
||||
|
||||
@warning The following filenames are created and then de-assigned:
|
||||
|
||||
filename sxlemap clear;
|
||||
filename response clear;
|
||||
libname _XML_ clear;
|
||||
|
||||
@version 9.2
|
||||
@author Allan Bowe
|
||||
|
||||
**/
|
||||
|
||||
%macro mm_getobjects(
|
||||
type=SASLibrary
|
||||
,outds=work.mm_getobjects
|
||||
)/*/STORE SOURCE*/;
|
||||
|
||||
|
||||
* use a temporary fileref to hold the response;
|
||||
filename response temp;
|
||||
/* get list of libraries */
|
||||
proc metadata in=
|
||||
"<GetMetadataObjects><Reposid>$METAREPOSITORY</Reposid>
|
||||
<Type>&type</Type><Objects/><NS>SAS</NS>
|
||||
<Flags>0</Flags><Options/></GetMetadataObjects>"
|
||||
out=response;
|
||||
run;
|
||||
|
||||
/* write the response to the log for debugging */
|
||||
data _null_;
|
||||
infile response lrecl=1048576;
|
||||
input;
|
||||
put _infile_;
|
||||
run;
|
||||
|
||||
/* create an XML map to read the response */
|
||||
filename sxlemap temp;
|
||||
data _null_;
|
||||
file sxlemap;
|
||||
put '<SXLEMAP version="1.2" name="SASObjects"><TABLE name="SASObjects">';
|
||||
put "<TABLE-PATH syntax='XPath'>/GetMetadataObjects/Objects/&type</TABLE-PATH>";
|
||||
put '<COLUMN name="id">';
|
||||
put "<PATH syntax='XPath'>/GetMetadataObjects/Objects/&type/@Id</PATH>";
|
||||
put "<TYPE>character</TYPE><DATATYPE>string</DATATYPE><LENGTH>200</LENGTH>";
|
||||
put '</COLUMN><COLUMN name="name">';
|
||||
put "<PATH syntax='XPath'>/GetMetadataObjects/Objects/&type/@Name</PATH>";
|
||||
put "<TYPE>character</TYPE><DATATYPE>string</DATATYPE><LENGTH>200</LENGTH>";
|
||||
put '</COLUMN></TABLE></SXLEMAP>';
|
||||
run;
|
||||
libname _XML_ xml xmlfileref=response xmlmap=sxlemap;
|
||||
|
||||
proc sort data= _XML_.SASObjects out=&outds;
|
||||
by name;
|
||||
run;
|
||||
|
||||
/* clear references */
|
||||
filename sxlemap clear;
|
||||
filename response clear;
|
||||
libname _XML_ clear;
|
||||
|
||||
%mend;
|
||||
85
meta/mm_getpublictypes.sas
Normal file
85
meta/mm_getpublictypes.sas
Normal file
@@ -0,0 +1,85 @@
|
||||
/**
|
||||
@file mm_getpublictypes.sas
|
||||
@brief Creates a dataset with all deployable public types
|
||||
@details More info: https://support.sas.com/documentation/cdl/en/bisag/65422/HTML/default/viewer.htm#n1nkrdzsq5iunln18bk2236istkb.htm
|
||||
|
||||
Usage:
|
||||
|
||||
* dataset will contain one column - publictype ($64);
|
||||
%mm_getpublictypes(outds=types)
|
||||
|
||||
@returns outds= dataset containing all types
|
||||
|
||||
@version 9.3
|
||||
@author Allan Bowe
|
||||
|
||||
**/
|
||||
|
||||
%macro mm_getpublictypes(
|
||||
outds=work.mm_getpublictypes
|
||||
)/*/STORE SOURCE*/;
|
||||
|
||||
proc sql;
|
||||
create table &outds (publictype char(64)); /* longest is currently 52 */
|
||||
insert into &outds values ('ACT');
|
||||
insert into &outds values ('Action');
|
||||
insert into &outds values ('Application');
|
||||
insert into &outds values ('ApplicationServer');
|
||||
insert into &outds values ('BurstDefinition');
|
||||
insert into &outds values ('Channel');
|
||||
insert into &outds values ('Condition');
|
||||
insert into &outds values ('ConditionActionSet');
|
||||
insert into &outds values ('ContentSubscriber');
|
||||
insert into &outds values ('Cube');
|
||||
insert into &outds values ('DataExploration');
|
||||
insert into &outds values ('DeployedFlow');
|
||||
insert into &outds values ('DeployedJob');
|
||||
insert into &outds values ('Document');
|
||||
insert into &outds values ('EventSubscriber');
|
||||
insert into &outds values ('ExternalFile');
|
||||
insert into &outds values ('FavoritesFolder');
|
||||
insert into &outds values ('Folder');
|
||||
insert into &outds values ('Folder.SecuredData');
|
||||
insert into &outds values ('GeneratedTransform');
|
||||
insert into &outds values ('InformationMap');
|
||||
insert into &outds values ('InformationMap.OLAP');
|
||||
insert into &outds values ('InformationMap.Relational');
|
||||
insert into &outds values ('JMSDestination (Java Messaging System message queue)');
|
||||
insert into &outds values ('Job');
|
||||
insert into &outds values ('Job.Cube');
|
||||
insert into &outds values ('Library');
|
||||
insert into &outds values ('MessageQueue');
|
||||
insert into &outds values ('MiningResults');
|
||||
insert into &outds values ('MQM.JMS (queue manager for Java Messaging Service)');
|
||||
insert into &outds values ('MQM.MSMQ (queue manager for MSMQ)');
|
||||
insert into &outds values ('MQM.Websphere (queue manager for WebSphere MQ)');
|
||||
insert into &outds values ('Note');
|
||||
insert into &outds values ('OLAPSchema');
|
||||
insert into &outds values ('Project');
|
||||
insert into &outds values ('Project.EG');
|
||||
insert into &outds values ('Project.AMOExcel');
|
||||
insert into &outds values ('Project.AMOPowerPoint');
|
||||
insert into &outds values ('Project.AMOWord');
|
||||
insert into &outds values ('Prompt');
|
||||
insert into &outds values ('PromptGroup');
|
||||
insert into &outds values ('Report');
|
||||
insert into &outds values ('Report.Component');
|
||||
insert into &outds values ('Report.Image');
|
||||
insert into &outds values ('Report.StoredProcess');
|
||||
insert into &outds values ('Role');
|
||||
insert into &outds values ('SearchFolder');
|
||||
insert into &outds values ('SecuredLibrary');
|
||||
insert into &outds values ('Server');
|
||||
insert into &outds values ('Service.SoapGenerated');
|
||||
insert into &outds values ('SharedDimension');
|
||||
insert into &outds values ('Spawner.Connect');
|
||||
insert into &outds values ('Spawner.IOM (object spawner)');
|
||||
insert into &outds values ('StoredProcess');
|
||||
insert into &outds values ('SubscriberGroup.Content');
|
||||
insert into &outds values ('SubscriberGroup.Event');
|
||||
insert into &outds values ('Table');
|
||||
insert into &outds values ('User');
|
||||
insert into &outds values ('UserGroup');
|
||||
quit;
|
||||
|
||||
%mend;
|
||||
117
meta/mm_getrepos.sas
Normal file
117
meta/mm_getrepos.sas
Normal file
@@ -0,0 +1,117 @@
|
||||
/**
|
||||
@file
|
||||
@brief Creates a dataset with all available repositories
|
||||
|
||||
@param outds= the dataset to create that contains the list of repos
|
||||
|
||||
@returns outds dataset containing all repositories
|
||||
|
||||
@warning The following filenames are created and then de-assigned:
|
||||
|
||||
filename sxlemap clear;
|
||||
filename response clear;
|
||||
libname _XML_ clear;
|
||||
|
||||
@version 9.2
|
||||
@author Allan Bowe
|
||||
|
||||
**/
|
||||
|
||||
%macro mm_getrepos(
|
||||
outds=work.mm_getrepos
|
||||
)/*/STORE SOURCE*/;
|
||||
|
||||
|
||||
* use a temporary fileref to hold the response;
|
||||
filename response temp;
|
||||
/* get list of libraries */
|
||||
proc metadata in=
|
||||
"<GetRepositories><Repositories/><Flags>1</Flags><Options/></GetRepositories>"
|
||||
out=response;
|
||||
run;
|
||||
|
||||
/* write the response to the log for debugging */
|
||||
/*
|
||||
data _null_;
|
||||
infile response lrecl=1048576;
|
||||
input;
|
||||
put _infile_;
|
||||
run;
|
||||
*/
|
||||
|
||||
/* create an XML map to read the response */
|
||||
filename sxlemap temp;
|
||||
data _null_;
|
||||
file sxlemap;
|
||||
put '<SXLEMAP version="1.2" name="SASRepos"><TABLE name="SASRepos">';
|
||||
put "<TABLE-PATH syntax='XPath'>/GetRepositories/Repositories/Repository</TABLE-PATH>";
|
||||
put '<COLUMN name="id">';
|
||||
put "<PATH syntax='XPath'>/GetRepositories/Repositories/Repository/@Id</PATH>";
|
||||
put "<TYPE>character</TYPE><DATATYPE>string</DATATYPE><LENGTH>200</LENGTH>";
|
||||
put '</COLUMN>';
|
||||
put '<COLUMN name="name">';
|
||||
put "<PATH syntax='XPath'>/GetRepositories/Repositories/Repository/@Name</PATH>";
|
||||
put "<TYPE>character</TYPE><DATATYPE>string</DATATYPE><LENGTH>200</LENGTH>";
|
||||
put '</COLUMN>';
|
||||
put '<COLUMN name="desc">';
|
||||
put "<PATH syntax='XPath'>/GetRepositories/Repositories/Repository/@Desc</PATH>";
|
||||
put "<TYPE>character</TYPE><DATATYPE>string</DATATYPE><LENGTH>200</LENGTH>";
|
||||
put '</COLUMN>';
|
||||
put '<COLUMN name="DefaultNS">';
|
||||
put "<PATH syntax='XPath'>/GetRepositories/Repositories/Repository/@DefaultNS</PATH>";
|
||||
put "<TYPE>character</TYPE><DATATYPE>string</DATATYPE><LENGTH>200</LENGTH>";
|
||||
put '</COLUMN>';
|
||||
put '<COLUMN name="RepositoryType">';
|
||||
put "<PATH syntax='XPath'>/GetRepositories/Repositories/Repository/@RepositoryType</PATH>";
|
||||
put "<TYPE>character</TYPE><DATATYPE>string</DATATYPE><LENGTH>20</LENGTH>";
|
||||
put '</COLUMN>';
|
||||
put '<COLUMN name="RepositoryFormat">';
|
||||
put "<PATH syntax='XPath'>/GetRepositories/Repositories/Repository/@RepositoryFormat</PATH>";
|
||||
put "<TYPE>character</TYPE><DATATYPE>string</DATATYPE><LENGTH>10</LENGTH>";
|
||||
put '</COLUMN>';
|
||||
put '<COLUMN name="Access">';
|
||||
put "<PATH syntax='XPath'>/GetRepositories/Repositories/Repository/@Access</PATH>";
|
||||
put "<TYPE>character</TYPE><DATATYPE>string</DATATYPE><LENGTH>16</LENGTH>";
|
||||
put '</COLUMN>';
|
||||
put '<COLUMN name="CurrentAccess">';
|
||||
put "<PATH syntax='XPath'>/GetRepositories/Repositories/Repository/@CurrentAccess</PATH>";
|
||||
put "<TYPE>character</TYPE><DATATYPE>string</DATATYPE><LENGTH>16</LENGTH>";
|
||||
put '</COLUMN>';
|
||||
put '<COLUMN name="PauseState">';
|
||||
put "<PATH syntax='XPath'>/GetRepositories/Repositories/Repository/@PauseState</PATH>";
|
||||
put "<TYPE>character</TYPE><DATATYPE>string</DATATYPE><LENGTH>16</LENGTH>";
|
||||
put '</COLUMN>';
|
||||
put '<COLUMN name="Path">';
|
||||
put "<PATH syntax='XPath'>/GetRepositories/Repositories/Repository/@Path</PATH>";
|
||||
put "<TYPE>character</TYPE><DATATYPE>string</DATATYPE><LENGTH>256</LENGTH>";
|
||||
put '</COLUMN>';
|
||||
put '<COLUMN name="Engine">';
|
||||
put "<PATH syntax='XPath'>/GetRepositories/Repositories/Repository/@Engine</PATH>";
|
||||
put "<TYPE>character</TYPE><DATATYPE>string</DATATYPE><LENGTH>8</LENGTH>";
|
||||
put '</COLUMN>';
|
||||
put '<COLUMN name="Options">';
|
||||
put "<PATH syntax='XPath'>/GetRepositories/Repositories/Repository/@Options</PATH>";
|
||||
put "<TYPE>character</TYPE><DATATYPE>string</DATATYPE><LENGTH>32</LENGTH>";
|
||||
put '</COLUMN>';
|
||||
put '<COLUMN name="MetadataCreated">';
|
||||
put "<PATH syntax='XPath'>/GetRepositories/Repositories/Repository/@MetadataCreated</PATH>";
|
||||
put "<TYPE>character</TYPE><DATATYPE>string</DATATYPE><LENGTH>24</LENGTH>";
|
||||
put '</COLUMN>';
|
||||
put '<COLUMN name="MetadataUpdated">';
|
||||
put "<PATH syntax='XPath'>/GetRepositories/Repositories/Repository/@MetadataUpdated</PATH>";
|
||||
put "<TYPE>character</TYPE><DATATYPE>string</DATATYPE><LENGTH>24</LENGTH>";
|
||||
put '</COLUMN>';
|
||||
put '</TABLE></SXLEMAP>';
|
||||
run;
|
||||
libname _XML_ xml xmlfileref=response xmlmap=sxlemap;
|
||||
|
||||
proc sort data= _XML_.SASRepos out=&outds;
|
||||
by name;
|
||||
run;
|
||||
|
||||
/* clear references */
|
||||
filename sxlemap clear;
|
||||
filename response clear;
|
||||
libname _XML_ clear;
|
||||
|
||||
%mend;
|
||||
69
meta/mm_getroles.sas
Normal file
69
meta/mm_getroles.sas
Normal file
@@ -0,0 +1,69 @@
|
||||
/**
|
||||
@file mm_getroles.sas
|
||||
@brief Creates a table containing a list of roles
|
||||
@details
|
||||
|
||||
Usage:
|
||||
|
||||
%mm_getroles()
|
||||
|
||||
@param outds the dataset to create that contains the list of roles
|
||||
|
||||
@returns outds dataset containing all roles, with the following columns:
|
||||
- uri
|
||||
- name
|
||||
|
||||
@warning The following filenames are created and then de-assigned:
|
||||
|
||||
filename sxlemap clear;
|
||||
filename response clear;
|
||||
libname _XML_ clear;
|
||||
|
||||
@version 9.3
|
||||
@author Allan Bowe
|
||||
|
||||
**/
|
||||
|
||||
%macro mm_getroles(
|
||||
outds=work.mm_getroles
|
||||
)/*/STORE SOURCE*/;
|
||||
|
||||
filename response temp;
|
||||
options noquotelenmax;
|
||||
proc metadata in= '<GetMetadataObjects><Reposid>$METAREPOSITORY</Reposid>
|
||||
<Type>IdentityGroup</Type><NS>SAS</NS><Flags>388</Flags>
|
||||
<Options>
|
||||
<Templates><IdentityGroup Name="" Desc="" PublicType=""/></Templates>
|
||||
<XMLSelect search="@PublicType=''Role''"/>
|
||||
</Options>
|
||||
</GetMetadataObjects>'
|
||||
out=response;
|
||||
run;
|
||||
|
||||
filename sxlemap temp;
|
||||
data _null_;
|
||||
file sxlemap;
|
||||
put '<SXLEMAP version="1.2" name="roles"><TABLE name="roles">';
|
||||
put "<TABLE-PATH syntax='XPath'>/GetMetadataObjects/Objects/IdentityGroup</TABLE-PATH>";
|
||||
put '<COLUMN name="roleuri">';
|
||||
put "<PATH syntax='XPath'>/GetMetadataObjects/Objects/IdentityGroup/@Id</PATH>";
|
||||
put "<TYPE>character</TYPE><DATATYPE>string</DATATYPE><LENGTH>32</LENGTH>";
|
||||
put '</COLUMN><COLUMN name="rolename">';
|
||||
put "<PATH syntax='XPath'>/GetMetadataObjects/Objects/IdentityGroup/@Name</PATH>";
|
||||
put "<TYPE>character</TYPE><DATATYPE>string</DATATYPE><LENGTH>256</LENGTH>";
|
||||
put '</COLUMN><COLUMN name="roledesc">';
|
||||
put "<PATH syntax='XPath'>/GetMetadataObjects/Objects/IdentityGroup/@Desc</PATH>";
|
||||
put "<TYPE>character</TYPE><DATATYPE>string</DATATYPE><LENGTH>500</LENGTH>";
|
||||
put '</COLUMN></TABLE></SXLEMAP>';
|
||||
run;
|
||||
libname _XML_ xml xmlfileref=response xmlmap=sxlemap;
|
||||
|
||||
proc sort data= _XML_.roles out=&outds;
|
||||
by rolename;
|
||||
run;
|
||||
|
||||
filename sxlemap clear;
|
||||
filename response clear;
|
||||
libname _XML_ clear;
|
||||
|
||||
%mend;
|
||||
84
meta/mm_getservercontexts.sas
Normal file
84
meta/mm_getservercontexts.sas
Normal file
@@ -0,0 +1,84 @@
|
||||
/**
|
||||
@file mm_getservercontexts.sas
|
||||
@brief Creates a dataset with all server contexts in all repos
|
||||
@details
|
||||
Usage:
|
||||
|
||||
%mm_getservercontexts(outds=mm_getservercontexts)
|
||||
|
||||
@param outds= the dataset to create that contains the list
|
||||
|
||||
@warning The following filenames are created and then de-assigned:
|
||||
|
||||
filename __mc1 clear;
|
||||
filename __mc2 clear;
|
||||
libname __mc3 clear;
|
||||
|
||||
<h4> Dependencies </h4>
|
||||
@li mm_getrepos.sas
|
||||
|
||||
@version 9.3
|
||||
@author Allan Bowe
|
||||
|
||||
**/
|
||||
|
||||
%macro mm_getservercontexts(
|
||||
outds=work.mm_getrepos
|
||||
)/*/STORE SOURCE*/;
|
||||
%local repo repocnt x;
|
||||
%let repo=%sysfunc(getoption(metarepository));
|
||||
|
||||
/* first get list of available repos */
|
||||
%mm_getrepos(outds=work.repos)
|
||||
%let repocnt=0;
|
||||
data _null_;
|
||||
set repos;
|
||||
where repositorytype in('CUSTOM','FOUNDATION');
|
||||
keep id name ;
|
||||
call symputx('repo'!!left(_n_),name,'l');
|
||||
call symputx('repocnt',_n_,'l');
|
||||
run;
|
||||
|
||||
filename __mc1 temp;
|
||||
filename __mc2 temp;
|
||||
data &outds; length serveruri servername $200; stop;run;
|
||||
%do x=1 %to &repocnt;
|
||||
options metarepository=&&repo&x;
|
||||
proc metadata in=
|
||||
"<GetMetadataObjects><Reposid>$METAREPOSITORY</Reposid>
|
||||
<Type>ServerContext</Type><Objects/><NS>SAS</NS>
|
||||
<Flags>0</Flags><Options/></GetMetadataObjects>"
|
||||
out=__mc1;
|
||||
run;
|
||||
/*
|
||||
data _null_;
|
||||
infile __mc1 lrecl=1048576;
|
||||
input;
|
||||
put _infile_;
|
||||
run;
|
||||
*/
|
||||
data _null_;
|
||||
file __mc2;
|
||||
put '<SXLEMAP version="1.2" name="SASContexts"><TABLE name="SASContexts">';
|
||||
put "<TABLE-PATH syntax='XPath'>/GetMetadataObjects/Objects/ServerContext</TABLE-PATH>";
|
||||
put '<COLUMN name="serveruri">';
|
||||
put "<PATH syntax='XPath'>/GetMetadataObjects/Objects/ServerContext/@Id</PATH>";
|
||||
put "<TYPE>character</TYPE><DATATYPE>string</DATATYPE><LENGTH>200</LENGTH>";
|
||||
put '</COLUMN>';
|
||||
put '<COLUMN name="servername">';
|
||||
put "<PATH syntax='XPath'>/GetMetadataObjects/Objects/ServerContext/@Name</PATH>";
|
||||
put "<TYPE>character</TYPE><DATATYPE>string</DATATYPE><LENGTH>200</LENGTH>";
|
||||
put '</COLUMN>';
|
||||
put '</TABLE></SXLEMAP>';
|
||||
run;
|
||||
libname __mc3 xml xmlfileref=__mc1 xmlmap=__mc2;
|
||||
proc append base=&outds data=__mc3.SASContexts;run;
|
||||
libname __mc3 clear;
|
||||
%end;
|
||||
|
||||
options metarepository=&repo;
|
||||
|
||||
filename __mc1 clear;
|
||||
filename __mc2 clear;
|
||||
|
||||
%mend;
|
||||
155
meta/mm_getstpcode.sas
Normal file
155
meta/mm_getstpcode.sas
Normal file
@@ -0,0 +1,155 @@
|
||||
/**
|
||||
@file
|
||||
@brief Writes the code of an to an external file, or the log if none provided
|
||||
@details Get the
|
||||
|
||||
usage:
|
||||
|
||||
%mm_getstpcode(tree=/some/meta/path
|
||||
,name=someSTP
|
||||
,outloc=/some/unquoted/filename.ext
|
||||
)
|
||||
|
||||
@param tree= The metadata path of the Stored Process (can also contain name)
|
||||
@param name= Stored Process name. Leave blank if included above.
|
||||
@param outloc= full and unquoted path to the desired text file. This will be
|
||||
overwritten if it already exists. If not provided, the code will be written
|
||||
to the log.
|
||||
|
||||
@author Allan Bowe
|
||||
|
||||
**/
|
||||
|
||||
%macro mm_getstpcode(
|
||||
tree=/User Folders/sasdemo/somestp
|
||||
,name=
|
||||
,outloc=
|
||||
,mDebug=1
|
||||
);
|
||||
|
||||
%local mD;
|
||||
%if &mDebug=1 %then %let mD=;
|
||||
%else %let mD=%str(*);
|
||||
%&mD.put Executing &sysmacroname..sas;
|
||||
%&mD.put _local_;
|
||||
|
||||
%if %length(&name)>0 %then %let name=/&name;
|
||||
|
||||
/* first, check if STP exists */
|
||||
%local tsuri;
|
||||
%let tsuri=stopifempty ;
|
||||
|
||||
data _null_;
|
||||
format type uri tsuri value $200.;
|
||||
call missing (of _all_);
|
||||
path="&tree&name(StoredProcess)";
|
||||
/* first, find the STP ID */
|
||||
if metadata_pathobj("",path,"StoredProcess",type,uri)>0 then do;
|
||||
/* get sourcecode */
|
||||
cnt=1;
|
||||
do while (metadata_getnasn(uri,"Notes",cnt,tsuri)>0);
|
||||
rc=metadata_getattr(tsuri,"Name",value);
|
||||
put tsuri= value=;
|
||||
if value="SourceCode" then do;
|
||||
/* found it! */
|
||||
rc=metadata_getattr(tsuri,"Id",value);
|
||||
call symputx('tsuri',value,'l');
|
||||
stop;
|
||||
end;
|
||||
cnt+1;
|
||||
end;
|
||||
end;
|
||||
else put (_all_)(=);
|
||||
run;
|
||||
|
||||
%if &tsuri=stopifempty %then %do;
|
||||
%put %str(WARN)ING: &tree&name.(StoredProcess) not found!;
|
||||
%return;
|
||||
%end;
|
||||
|
||||
|
||||
/**
|
||||
* Now we can extract the textstore
|
||||
*/
|
||||
filename __getdoc temp lrecl=10000000;
|
||||
proc metadata
|
||||
in="<GetMetadata><Reposid>$METAREPOSITORY</Reposid>
|
||||
<Metadata><TextStore Id='&tsuri'/></Metadata>
|
||||
<Ns>SAS</Ns><Flags>1</Flags><Options/></GetMetadata>"
|
||||
out=__getdoc ;
|
||||
run;
|
||||
|
||||
/* find the beginning of the text */
|
||||
%local start;
|
||||
data _null_;
|
||||
infile __getdoc lrecl=10000;
|
||||
input;
|
||||
start=index(_infile_,'StoredText="');
|
||||
if start then do;
|
||||
call symputx("start",start+11);
|
||||
*putlog '"' _infile_ '"';
|
||||
end;
|
||||
stop;
|
||||
|
||||
%local outeng;
|
||||
%if %length(&outloc)=0 %then %let outeng=TEMP;
|
||||
%else %let outeng="&outloc";
|
||||
/* read the content, byte by byte, resolving escaped chars */
|
||||
filename __outdoc &outeng lrecl=100000;
|
||||
data _null_;
|
||||
length filein 8 fileid 8;
|
||||
filein = fopen("__getdoc","I",1,"B");
|
||||
fileid = fopen("__outdoc","O",1,"B");
|
||||
rec = "20"x;
|
||||
length entity $6;
|
||||
do while(fread(filein)=0);
|
||||
x+1;
|
||||
if x>&start then do;
|
||||
rc = fget(filein,rec,1);
|
||||
if rec='"' then leave;
|
||||
else if rec="&" then do;
|
||||
entity=rec;
|
||||
do until (rec=";");
|
||||
if fread(filein) ne 0 then goto getout;
|
||||
rc = fget(filein,rec,1);
|
||||
entity=cats(entity,rec);
|
||||
end;
|
||||
select (entity);
|
||||
when ('&' ) rec='&' ;
|
||||
when ('<' ) rec='<' ;
|
||||
when ('>' ) rec='>' ;
|
||||
when (''') rec="'" ;
|
||||
when ('"') rec='"' ;
|
||||
when ('
') rec='0A'x;
|
||||
when ('
') rec='0D'x;
|
||||
when ('$' ) rec='$' ;
|
||||
otherwise putlog "%str(WARN)ING: missing value for " entity=;
|
||||
end;
|
||||
rc =fput(fileid, substr(rec,1,1));
|
||||
rc =fwrite(fileid);
|
||||
end;
|
||||
else do;
|
||||
rc =fput(fileid,rec);
|
||||
rc =fwrite(fileid);
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
getout:
|
||||
rc=fclose(filein);
|
||||
rc=fclose(fileid);
|
||||
run;
|
||||
|
||||
%if &outeng=TEMP %then %do;
|
||||
data _null_;
|
||||
infile __outdoc lrecl=32767 end=last;
|
||||
input;
|
||||
if _n_=1 then putlog '>>stpcodeBEGIN<<';
|
||||
putlog _infile_;
|
||||
if last then putlog '>>stpcodeEND<<';
|
||||
run;
|
||||
%end;
|
||||
|
||||
filename __getdoc clear;
|
||||
filename __outdoc clear;
|
||||
|
||||
%mend;
|
||||
109
meta/mm_getstps.sas
Normal file
109
meta/mm_getstps.sas
Normal file
@@ -0,0 +1,109 @@
|
||||
/**
|
||||
@file
|
||||
@brief Returns a dataset with all Stored Processes, or just those in a
|
||||
particular folder / with a particular name.
|
||||
@details Leave blank to get all stps. Provide a Tree (path or uri) or a
|
||||
name (not case sensitive) to filter that way also.
|
||||
usage:
|
||||
|
||||
%mm_getstps()
|
||||
|
||||
%mm_getstps(name=My STP)
|
||||
|
||||
%mm_getstps(tree=/My Folder/My STPs)
|
||||
|
||||
%mm_getstps(tree=/My Folder/My STPs, name=My STP)
|
||||
|
||||
<h4> Dependencies </h4>
|
||||
@li mm_gettree.sas
|
||||
|
||||
@param tree= the metadata folder location in which to search. Leave blank
|
||||
for all folders. Does not search subdirectories.
|
||||
@param name= Provide the name of an STP to search for just that one. Can
|
||||
combine with the <code>tree=</code> parameter.
|
||||
@param outds= the dataset to create that contains the list of stps.
|
||||
@param mDebug= set to 1 to show debug messages in the log
|
||||
@showDesc= provide a non blank value to return stored process descriptions
|
||||
@showUsageVersion= provide a non blank value to return the UsageVersion. This
|
||||
is either 1000000 (type 1, 9.2) or 2000000 (type2, 9.3 onwards).
|
||||
|
||||
@returns outds dataset containing the following columns
|
||||
- stpuri
|
||||
- stpname
|
||||
- treeuri
|
||||
- stpdesc (if requested)
|
||||
- usageversion (if requested)
|
||||
|
||||
@version 9.2
|
||||
@author Allan Bowe
|
||||
|
||||
**/
|
||||
|
||||
%macro mm_getstps(
|
||||
tree=
|
||||
,name=
|
||||
,outds=work.mm_getstps
|
||||
,mDebug=0
|
||||
,showDesc=
|
||||
,showUsageVersion=
|
||||
)/*/STORE SOURCE*/;
|
||||
|
||||
%local mD;
|
||||
%if &mDebug=1 %then %let mD=;
|
||||
%else %let mD=%str(*);
|
||||
%&mD.put Executing mm_getstps.sas;
|
||||
%&mD.put _local_;
|
||||
|
||||
data &outds;
|
||||
length stpuri stpname usageversion treeuri stpdesc $256;
|
||||
call missing (of _all_);
|
||||
run;
|
||||
|
||||
%if %length(&tree)>0 %then %do;
|
||||
/* get tree info */
|
||||
%mm_gettree(tree=&tree,inds=&outds, outds=&outds, mDebug=&mDebug)
|
||||
%if %mf_nobs(&outds)=0 %then %do;
|
||||
%put NOTE: Tree &tree did not exist!!;
|
||||
%return;
|
||||
%end;
|
||||
%end;
|
||||
|
||||
|
||||
|
||||
data &outds ;
|
||||
set &outds(rename=(treeuri=treeuri_compare));
|
||||
length treeuri query stpuri $256;
|
||||
i+1;
|
||||
%if %length(&name)>0 %then %do;
|
||||
query="omsobj:ClassifierMap?@PublicType='StoredProcess' and @Name='&name'";
|
||||
putlog query=;
|
||||
%end;
|
||||
%else %do;
|
||||
query="omsobj:ClassifierMap?@PublicType='StoredProcess'";
|
||||
%end;
|
||||
%if &mDebug=1 %then %do;
|
||||
putlog 'start' (_all_)(=);
|
||||
%end;
|
||||
do while(0<metadata_getnobj(query,i,stpuri));
|
||||
i+1;
|
||||
rc1=metadata_getattr(stpuri,"Name", stpname);
|
||||
rc2=metadata_getnasn(stpuri,"Trees",1,treeuri);
|
||||
%if %length(&tree)>0 %then %do;
|
||||
if treeuri ne treeuri_compare then goto exitloop;
|
||||
%end;
|
||||
%if %length(&showDesc)>0 %then %do;
|
||||
rc3=metadata_getattr(stpuri,"Desc", stpdesc);
|
||||
keep stpdesc;
|
||||
%end;
|
||||
%if %length(&showUsageVersion)>0 %then %do;
|
||||
rc4=metadata_getattr(stpuri,"UsageVersion",UsageVersion);
|
||||
keep usageversion;
|
||||
%end;
|
||||
output;
|
||||
&mD.put (_all_)(=);
|
||||
exitloop:
|
||||
end;
|
||||
keep stpuri stpname treeuri;
|
||||
run;
|
||||
|
||||
%mend;
|
||||
118
meta/mm_gettables.sas
Normal file
118
meta/mm_gettables.sas
Normal file
@@ -0,0 +1,118 @@
|
||||
/**
|
||||
@file
|
||||
@brief Creates a dataset with all metadata tables for a particular library
|
||||
@details Will only show the tables to which a user has the requisite
|
||||
metadata access.
|
||||
|
||||
usage:
|
||||
|
||||
%mm_gettables(uri=A5X8AHW1.B40001S5)
|
||||
|
||||
@param outds the dataset to create that contains the list of tables
|
||||
@param uri the uri of the library for which to return tables
|
||||
@param getauth= YES|NO - fetch the authdomain used in database connections.
|
||||
Set to NO to improve runtimes in larger environments, as there can be a
|
||||
performance hit on the `metadata_getattr(domainuri, "Name", AuthDomain)` call.
|
||||
|
||||
@returns outds dataset containing all groups in a column named "metagroup"
|
||||
(defaults to work.mm_getlibs). The following columns are provided:
|
||||
- tablename
|
||||
- tableuri
|
||||
- libref
|
||||
- libname
|
||||
- libdesc
|
||||
|
||||
@version 9.2
|
||||
@author Allan Bowe
|
||||
|
||||
**/
|
||||
|
||||
%macro mm_gettables(
|
||||
uri=
|
||||
,outds=work.mm_gettables
|
||||
,getauth=YES
|
||||
)/*/STORE SOURCE*/;
|
||||
|
||||
|
||||
data &outds;
|
||||
length uri serveruri conn_uri domainuri libname ServerContext AuthDomain
|
||||
path_schema usingpkguri type tableuri $256 id $17
|
||||
libdesc $200 libref engine $8 IsDBMSLibname $1
|
||||
tablename $50 /* metadata table names can be longer than $32 */
|
||||
;
|
||||
keep libname libdesc libref engine ServerContext path_schema AuthDomain tableuri
|
||||
tablename IsPreassigned IsDBMSLibname id;
|
||||
call missing (of _all_);
|
||||
|
||||
uri=symget('uri');
|
||||
rc= metadata_getattr(uri, "Name", libname);
|
||||
if rc <0 then do;
|
||||
put 'The library is not defined in this metadata repository.';
|
||||
stop;
|
||||
end;
|
||||
rc= metadata_getattr(uri, "Desc", libdesc);
|
||||
rc= metadata_getattr(uri, "Libref", libref);
|
||||
rc= metadata_getattr(uri, "Engine", engine);
|
||||
rc= metadata_getattr(uri, "IsDBMSLibname", IsDBMSLibname);
|
||||
rc= metadata_getattr(uri, "IsPreassigned", IsPreassigned);
|
||||
rc= metadata_getattr(uri, "Id", Id);
|
||||
|
||||
/*** Get associated ServerContext ***/
|
||||
rc= metadata_getnasn(uri, "DeployedComponents", 1, serveruri);
|
||||
if rc > 0 then rc2= metadata_getattr(serveruri, "Name", ServerContext);
|
||||
else ServerContext='';
|
||||
|
||||
/*** If the library is a DBMS library, get the Authentication Domain
|
||||
associated with the DBMS connection credentials ***/
|
||||
if IsDBMSLibname="1" and "&getauth"='YES' then do;
|
||||
rc= metadata_getnasn(uri, "LibraryConnection", 1, conn_uri);
|
||||
if rc>0 then do;
|
||||
rc2= metadata_getnasn(conn_uri, "Domain", 1, domainuri);
|
||||
if rc2>0 then rc3= metadata_getattr(domainuri, "Name", AuthDomain);
|
||||
end;
|
||||
end;
|
||||
|
||||
/*** Get the path/database schema for this library ***/
|
||||
rc=metadata_getnasn(uri, "UsingPackages", 1, usingpkguri);
|
||||
if rc>0 then do;
|
||||
rc=metadata_resolve(usingpkguri,type,id);
|
||||
if type='Directory' then
|
||||
rc=metadata_getattr(usingpkguri, "DirectoryName", path_schema);
|
||||
else if type='DatabaseSchema' then
|
||||
rc=metadata_getattr(usingpkguri, "Name", path_schema);
|
||||
else path_schema="unknown";
|
||||
end;
|
||||
|
||||
/*** Get the tables associated with this library ***/
|
||||
/*** If DBMS, tables are associated with DatabaseSchema ***/
|
||||
if type='DatabaseSchema' then do;
|
||||
t=1;
|
||||
ntab=metadata_getnasn(usingpkguri, "Tables", t, tableuri);
|
||||
if ntab>0 then do t=1 to ntab;
|
||||
tableuri='';
|
||||
tablename='';
|
||||
ntab=metadata_getnasn(usingpkguri, "Tables", t, tableuri);
|
||||
tabrc= metadata_getattr(tableuri, "Name", tablename);
|
||||
output;
|
||||
end;
|
||||
else put 'Library ' libname ' has no tables registered';
|
||||
end;
|
||||
else if type in ('Directory','SASLibrary') then do;
|
||||
t=1;
|
||||
ntab=metadata_getnasn(uri, "Tables", t, tableuri);
|
||||
if ntab>0 then do t=1 to ntab;
|
||||
tableuri='';
|
||||
tablename='';
|
||||
ntab=metadata_getnasn(uri, "Tables", t, tableuri);
|
||||
tabrc= metadata_getattr(tableuri, "Name", tablename);
|
||||
output;
|
||||
end;
|
||||
else put 'Library ' libname ' has no tables registered';
|
||||
end;
|
||||
run;
|
||||
|
||||
proc sort;
|
||||
by tablename tableuri;
|
||||
run;
|
||||
|
||||
%mend;
|
||||
68
meta/mm_gettree.sas
Executable file
68
meta/mm_gettree.sas
Executable file
@@ -0,0 +1,68 @@
|
||||
/**
|
||||
@file
|
||||
@brief Returns the metadata path and object from either the path or object
|
||||
@details Provide a metadata BIP tree path, or the uri for the bottom level
|
||||
folder, to obtain a dataset (<code>&outds</code>) containing both the path
|
||||
and uri.
|
||||
|
||||
Usage:
|
||||
|
||||
%mm_getTree(tree=/User Folders/sasdemo)
|
||||
|
||||
|
||||
@param tree= the BIP Tree folder path or uri
|
||||
@param outds= the dataset to create that contains the tree path & uri
|
||||
@param inds= an optional input dataset to augment with treepath & treeuri
|
||||
@param mDebug= set to 1 to show debug messages in the log
|
||||
|
||||
@returns outds dataset containing the following columns:
|
||||
- treeuri
|
||||
- treepath
|
||||
|
||||
@version 9.2
|
||||
@author Allan Bowe
|
||||
|
||||
**/
|
||||
|
||||
%macro mm_getTree(
|
||||
tree=
|
||||
,inds=
|
||||
,outds=work.mm_getTree
|
||||
,mDebug=0
|
||||
)/*/STORE SOURCE*/;
|
||||
|
||||
%local mD;
|
||||
%if &mDebug=1 %then %let mD=;
|
||||
%else %let mD=%str(*);
|
||||
%&mD.put Executing mm_getTree.sas;
|
||||
%&mD.put _local_;
|
||||
|
||||
data &outds;
|
||||
length treeuri __parenturi __type __name $256 treepath $512;
|
||||
%if %length(&inds)>0 %then %do;
|
||||
set &inds;
|
||||
%end;
|
||||
__rc1=metadata_resolve("&tree",__type,treeuri);
|
||||
|
||||
if __type='Tree' then do;
|
||||
__rc2=metadata_getattr(treeuri,"Name",__name);
|
||||
treepath=cats('/',__name);
|
||||
/* get parents */
|
||||
do while (metadata_getnasn(treeuri,"ParentTree",1,__parenturi)>0);
|
||||
__rc3=metadata_getattr(__parenturi,"Name",__name);
|
||||
treepath=cats('/',__name,treepath);
|
||||
treeuri=__parenturi;
|
||||
end;
|
||||
treeuri="&tree";
|
||||
end;
|
||||
else do;
|
||||
__rc2=metadata_pathobj(' ',"&tree",'Folder',__type,treeuri);
|
||||
treepath="&tree";
|
||||
end;
|
||||
|
||||
&mD.put (_all_)(=);
|
||||
drop __:;
|
||||
if treeuri ne "" and treepath ne "" then output;
|
||||
stop;
|
||||
run;
|
||||
%mend;
|
||||
75
meta/mm_gettypes.sas
Normal file
75
meta/mm_gettypes.sas
Normal file
@@ -0,0 +1,75 @@
|
||||
/**
|
||||
@file
|
||||
@brief Creates a dataset with all metadata types
|
||||
@details Usage:
|
||||
|
||||
%mm_gettypes(outds=types)
|
||||
|
||||
@param outds the dataset to create that contains the list of types
|
||||
@returns outds dataset containing all types
|
||||
@warning The following filenames are created and then de-assigned:
|
||||
|
||||
filename sxlemap clear;
|
||||
filename response clear;
|
||||
libname _XML_ clear;
|
||||
|
||||
@version 9.2
|
||||
@author Allan Bowe
|
||||
|
||||
**/
|
||||
|
||||
%macro mm_gettypes(
|
||||
outds=work.mm_gettypes
|
||||
)/*/STORE SOURCE*/;
|
||||
|
||||
* use a temporary fileref to hold the response;
|
||||
filename response temp;
|
||||
/* get list of libraries */
|
||||
proc metadata in=
|
||||
'<GetTypes>
|
||||
<Types/>
|
||||
<NS>SAS</NS>
|
||||
<!-- specify the OMI_SUCCINCT flag -->
|
||||
<Flags>2048</Flags>
|
||||
<Options>
|
||||
<!-- include <REPOSID> XML element and a repository identifier -->
|
||||
<Reposid>$METAREPOSITORY</Reposid>
|
||||
</Options>
|
||||
</GetTypes>'
|
||||
out=response;
|
||||
run;
|
||||
|
||||
/* write the response to the log for debugging */
|
||||
data _null_;
|
||||
infile response lrecl=1048576;
|
||||
input;
|
||||
put _infile_;
|
||||
run;
|
||||
|
||||
/* create an XML map to read the response */
|
||||
filename sxlemap temp;
|
||||
data _null_;
|
||||
file sxlemap;
|
||||
put '<SXLEMAP version="1.2" name="SASTypes"><TABLE name="SASTypes">';
|
||||
put '<TABLE-PATH syntax="XPath">//GetTypes/Types/Type</TABLE-PATH>';
|
||||
put '<COLUMN name="ID"><LENGTH>64</LENGTH>';
|
||||
put '<PATH syntax="XPath">//GetTypes/Types/Type/@Id</PATH></COLUMN>';
|
||||
put '<COLUMN name="Desc"><LENGTH>256</LENGTH>';
|
||||
put '<PATH syntax="XPath">//GetTypes/Types/Type/@Desc</PATH></COLUMN>';
|
||||
put '<COLUMN name="HasSubtypes">';
|
||||
put '<PATH syntax="XPath">//GetTypes/Types/Type/@HasSubtypes</PATH></COLUMN>';
|
||||
put '</TABLE></SXLEMAP>';
|
||||
run;
|
||||
libname _XML_ xml xmlfileref=response xmlmap=sxlemap;
|
||||
/* sort the response by library name */
|
||||
proc sort data=_XML_.sastypes out=&outds;
|
||||
by id;
|
||||
run;
|
||||
|
||||
|
||||
/* clear references */
|
||||
filename sxlemap clear;
|
||||
filename response clear;
|
||||
libname _XML_ clear;
|
||||
|
||||
%mend;
|
||||
70
meta/mm_getusers.sas
Normal file
70
meta/mm_getusers.sas
Normal file
@@ -0,0 +1,70 @@
|
||||
/**
|
||||
@file mm_getusers.sas
|
||||
@brief Creates a table containing a list of all users
|
||||
@details Only shows a limited number of attributes as some sites will have a
|
||||
LOT of users.
|
||||
|
||||
Usage:
|
||||
|
||||
%mm_getusers()
|
||||
|
||||
@param outds the dataset to create that contains the list of libraries
|
||||
|
||||
@returns outds dataset containing all users, with the following columns:
|
||||
- uri
|
||||
- name
|
||||
|
||||
@warning The following filenames are created and then de-assigned:
|
||||
|
||||
filename sxlemap clear;
|
||||
filename response clear;
|
||||
libname _XML_ clear;
|
||||
|
||||
@version 9.3
|
||||
@author Allan Bowe
|
||||
|
||||
**/
|
||||
|
||||
%macro mm_getusers(
|
||||
outds=work.mm_getusers
|
||||
)/*/STORE SOURCE*/;
|
||||
|
||||
filename response temp;
|
||||
proc metadata in= '<GetMetadataObjects>
|
||||
<Reposid>$METAREPOSITORY</Reposid>
|
||||
<Type>Person</Type>
|
||||
<NS>SAS</NS>
|
||||
<Flags>0</Flags>
|
||||
<Options>
|
||||
<Templates>
|
||||
<Person Name=""/>
|
||||
</Templates>
|
||||
</Options>
|
||||
</GetMetadataObjects>'
|
||||
out=response;
|
||||
run;
|
||||
|
||||
filename sxlemap temp;
|
||||
data _null_;
|
||||
file sxlemap;
|
||||
put '<SXLEMAP version="1.2" name="SASObjects"><TABLE name="SASObjects">';
|
||||
put "<TABLE-PATH syntax='XPath'>/GetMetadataObjects/Objects/Person</TABLE-PATH>";
|
||||
put '<COLUMN name="uri">';
|
||||
put "<PATH syntax='XPath'>/GetMetadataObjects/Objects/Person/@Id</PATH>";
|
||||
put "<TYPE>character</TYPE><DATATYPE>string</DATATYPE><LENGTH>32</LENGTH>";
|
||||
put '</COLUMN><COLUMN name="name">';
|
||||
put "<PATH syntax='XPath'>/GetMetadataObjects/Objects/Person/@Name</PATH>";
|
||||
put "<TYPE>character</TYPE><DATATYPE>string</DATATYPE><LENGTH>256</LENGTH>";
|
||||
put '</COLUMN></TABLE></SXLEMAP>';
|
||||
run;
|
||||
libname _XML_ xml xmlfileref=response xmlmap=sxlemap;
|
||||
|
||||
proc sort data= _XML_.SASObjects out=&outds;
|
||||
by name;
|
||||
run;
|
||||
|
||||
filename sxlemap clear;
|
||||
filename response clear;
|
||||
libname _XML_ clear;
|
||||
|
||||
%mend;
|
||||
134
meta/mm_getwebappsrvprops.sas
Normal file
134
meta/mm_getwebappsrvprops.sas
Normal file
@@ -0,0 +1,134 @@
|
||||
/**
|
||||
@file
|
||||
@brief Retrieves properties of the SAS web app server
|
||||
@description usage:
|
||||
|
||||
%mm_getwebappsrvprops(outds= some_ds)
|
||||
data _null_;
|
||||
set some_ds(where=(name='webappsrv.server.url'));
|
||||
put value=;
|
||||
run;
|
||||
|
||||
@param outds the dataset to create that contains the list of properties
|
||||
|
||||
@returns outds dataset containing all properties
|
||||
|
||||
@warning The following filenames are created and then de-assigned:
|
||||
|
||||
filename __in clear;
|
||||
filename __out clear;
|
||||
libname __shake clear;
|
||||
|
||||
@version 9.4
|
||||
@author Allan Bowe
|
||||
@source https://github.com/macropeople/macrocore
|
||||
|
||||
**/
|
||||
|
||||
%macro mm_getwebappsrvprops(
|
||||
outds= mm_getwebappsrvprops
|
||||
)/*/STORE SOURCE*/;
|
||||
|
||||
filename __in temp lrecl=10000;
|
||||
filename __out temp lrecl=10000;
|
||||
filename __shake temp lrecl=10000;
|
||||
data _null_ ;
|
||||
file __in ;
|
||||
put '<GetMetadataObjects>' ;
|
||||
put '<Reposid>$METAREPOSITORY</Reposid>' ;
|
||||
put '<Type>TextStore</Type>' ;
|
||||
put '<NS>SAS</NS>' ;
|
||||
put '<Flags>388</Flags>' ;
|
||||
put '<Options>' ;
|
||||
put '<XMLSelect search="TextStore[@Name='@@;
|
||||
put "'Public Configuration Properties']" @@;
|
||||
put '[Objects/SoftwareComponent[@ClassIdentifier=''webappsrv'']]' ;
|
||||
put '"/>';
|
||||
put '<Templates>' ;
|
||||
put '<TextStore StoredText="">' ;
|
||||
put '</TextStore>' ;
|
||||
put '</Templates>' ;
|
||||
put '</Options>' ;
|
||||
put '</GetMetadataObjects>' ;
|
||||
run ;
|
||||
proc metadata in=__in out=__out verbose;run;
|
||||
|
||||
/* find the beginning of the text */
|
||||
%local start;
|
||||
%let start=0;
|
||||
data _null_;
|
||||
infile __out lrecl=10000;
|
||||
input;
|
||||
length cleartemplate $32000;
|
||||
cleartemplate=tranwrd(_infile_,'StoredText=""','');
|
||||
start=index(cleartemplate,'StoredText="');
|
||||
if start then do;
|
||||
call symputx("start",start+11+length('StoredText=""')-1);
|
||||
putlog cleartemplate ;
|
||||
end;
|
||||
stop;
|
||||
run;
|
||||
%put &=start;
|
||||
%if &start>0 %then %do;
|
||||
/* read the content, byte by byte, resolving escaped chars */
|
||||
data _null_;
|
||||
length filein 8 fileid 8;
|
||||
filein = fopen("__out","I",1,"B");
|
||||
fileid = fopen("__shake","O",1,"B");
|
||||
rec = "20"x;
|
||||
length entity $6;
|
||||
do while(fread(filein)=0);
|
||||
x+1;
|
||||
if x>&start then do;
|
||||
rc = fget(filein,rec,1);
|
||||
if rec='"' then leave;
|
||||
else if rec="&" then do;
|
||||
entity=rec;
|
||||
do until (rec=";");
|
||||
if fread(filein) ne 0 then goto getout;
|
||||
rc = fget(filein,rec,1);
|
||||
entity=cats(entity,rec);
|
||||
end;
|
||||
select (entity);
|
||||
when ('&' ) rec='&' ;
|
||||
when ('<' ) rec='<' ;
|
||||
when ('>' ) rec='>' ;
|
||||
when (''') rec="'" ;
|
||||
when ('"') rec='"' ;
|
||||
when ('
') rec='0A'x;
|
||||
when ('
') rec='0D'x;
|
||||
when ('$' ) rec='$' ;
|
||||
otherwise putlog "WARNING: missing value for " entity=;
|
||||
end;
|
||||
rc =fput(fileid, substr(rec,1,1));
|
||||
rc =fwrite(fileid);
|
||||
end;
|
||||
else do;
|
||||
rc =fput(fileid,rec);
|
||||
rc =fwrite(fileid);
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
getout:
|
||||
rc=fclose(filein);
|
||||
rc=fclose(fileid);
|
||||
run;
|
||||
data &outds ;
|
||||
infile __shake dlm='=' missover;
|
||||
length name $50 value $500;
|
||||
input name $ value $;
|
||||
run;
|
||||
%end;
|
||||
%else %do;
|
||||
%put NOTE: Unable to retrieve Web App Server Properties;
|
||||
data &outds;
|
||||
length name $50 value $500;
|
||||
run;
|
||||
%end;
|
||||
|
||||
/* clear references */
|
||||
filename __in clear;
|
||||
filename __out clear;
|
||||
filename __shake clear;
|
||||
|
||||
%mend;
|
||||
120
meta/mm_spkexport.sas
Normal file
120
meta/mm_spkexport.sas
Normal file
@@ -0,0 +1,120 @@
|
||||
/**
|
||||
@file mm_spkexport.sas
|
||||
@brief Creates an batch spk export command
|
||||
@details Creates a script that will export everything in a metadata folder to
|
||||
a specified location.
|
||||
If you have XCMD enabled, then you can use mmx_spkexport (which performs
|
||||
the actual export)
|
||||
|
||||
Note - the batch tools require a username and password. For security,
|
||||
these are expected to have been provided in a protected directory.
|
||||
|
||||
Usage:
|
||||
|
||||
%* import the macros (or make them available some other way);
|
||||
filename mc url "https://raw.githubusercontent.com/macropeople/macrocore/master/mc_all.sas";
|
||||
%inc mc;
|
||||
|
||||
%* create sample text file as input to the macro;
|
||||
filename tmp temp;
|
||||
data _null_;
|
||||
file tmp;
|
||||
put '%let mmxuser="sasdemo";';
|
||||
put '%let mmxpass="Mars321";';
|
||||
run;
|
||||
|
||||
filename myref "%sysfunc(pathname(work))/mmxexport.sh";
|
||||
%mm_spkexport(metaloc=%str(/my/meta/loc)
|
||||
,outref=myref
|
||||
,secureref=tmp
|
||||
,cmdoutloc=%str(/tmp)
|
||||
)
|
||||
|
||||
Alternatively, call without inputs to create a function style output
|
||||
|
||||
filename myref "/tmp/mmscript.sh";
|
||||
%mm_spkexport(metaloc=%str(/my/meta/loc)
|
||||
outref=myref
|
||||
,cmdoutloc=%str(/tmp)
|
||||
,cmdoutname=mmx
|
||||
)
|
||||
|
||||
You can then navigate and execute as follows:
|
||||
|
||||
cd /tmp
|
||||
./mmscript.sh "myuser" "mypass"
|
||||
|
||||
|
||||
<h4> Dependencies </h4>
|
||||
@li mf_loc.sas
|
||||
@li mm_tree.sas
|
||||
@li mf_getuniquefileref.sas
|
||||
@li mf_isblank.sas
|
||||
@li mp_abort.sas
|
||||
|
||||
@param metaloc= the metadata folder to export
|
||||
@param secureref= fileref containing the username / password (should point to
|
||||
a file in a secure location). Leave blank to substitute $bash type vars.
|
||||
@param outref= fileref to which to write the command
|
||||
@param cmdoutloc= the directory to which the command will write the SPK
|
||||
(default=WORK)
|
||||
@param cmdoutname= the name of the spk / log files to create (will be
|
||||
identical just with .spk or .log extension)
|
||||
|
||||
@version 9.4
|
||||
@author Allan Bowe
|
||||
|
||||
**/
|
||||
|
||||
%macro mm_spkexport(metaloc=
|
||||
,secureref=
|
||||
,outref=
|
||||
,cmdoutloc=%sysfunc(pathname(work))
|
||||
,cmdoutname=mmxport
|
||||
);
|
||||
|
||||
%if &sysscp=WIN %then %do;
|
||||
%put %str(WARN)ING: the script has been written assuming a unix system;
|
||||
%put %str(WARN)ING- it will run anyway as should be easy to modify;
|
||||
%end;
|
||||
|
||||
/* set creds */
|
||||
%local mmxuser mmxpath;
|
||||
%let mmxuser=$1;
|
||||
%let mmxpass=$2;
|
||||
%if %mf_isblank(&secureref)=0 %then %do;
|
||||
%inc &secureref/nosource;
|
||||
%end;
|
||||
|
||||
/* setup metadata connection options */
|
||||
%local host port platform_object_path connx_string;
|
||||
%let host=%sysfunc(getoption(metaserver));
|
||||
%let port=%sysfunc(getoption(metaport));
|
||||
%let platform_object_path=%mf_loc(POF);
|
||||
|
||||
%let connx_string=%str(-host &host -port &port -user &mmxuser -password &mmxpass);
|
||||
|
||||
%mm_tree(root=%str(&metaloc) ,types=EXPORTABLE ,outds=exportable)
|
||||
|
||||
%if %mf_isblank(&outref)=1 %then %let outref=%mf_getuniquefileref();
|
||||
|
||||
data _null_;
|
||||
set exportable end=last;
|
||||
file &outref lrecl=32767;
|
||||
length str $32767;
|
||||
if _n_=1 then do;
|
||||
put "cd ""&platform_object_path"" \";
|
||||
put "; ./ExportPackage &connx_string -disableX11 \";
|
||||
put " -package ""&cmdoutloc/&cmdoutname..spk"" \";
|
||||
end;
|
||||
str=' -objects '!!cats('"',path,'/',name,"(",publictype,')" \');
|
||||
put str;
|
||||
if last then put " -log ""&cmdoutloc/&cmdoutname..log"" 2>&1 ";
|
||||
run;
|
||||
|
||||
%mp_abort(iftrue= (&syscc ne 0)
|
||||
,mac=&sysmacroname
|
||||
,msg=%str(syscc=&syscc)
|
||||
)
|
||||
|
||||
%mend;
|
||||
171
meta/mm_tree.sas
Normal file
171
meta/mm_tree.sas
Normal file
@@ -0,0 +1,171 @@
|
||||
/**
|
||||
@file mm_tree.sas
|
||||
@brief Returns all folders / subfolder content for a particular root
|
||||
@details Shows all members and SubTrees for a particular root.
|
||||
|
||||
Model:
|
||||
|
||||
metauri char(64),
|
||||
name char(256) format=$256. informat=$256. label='name',
|
||||
path char(1024),
|
||||
publictype char(32),
|
||||
MetadataUpdated char(32),
|
||||
MetadataCreated char(32)
|
||||
|
||||
Usage:
|
||||
|
||||
%* load macros;
|
||||
filename mc url "https://raw.githubusercontent.com/macropeople/macrocore/master/mc_all.sas";
|
||||
%inc mc;
|
||||
|
||||
%* export everything;
|
||||
%mm_tree(root= ,outds=iwantthisdataset)
|
||||
|
||||
%* export everything in a specific folder;
|
||||
%mm_tree(root=%str(/my/folder) ,outds=stuff)
|
||||
|
||||
%* export only folders;
|
||||
%mm_tree(root=%str(/my/folder) ,types=Folder ,outds=stuf)
|
||||
|
||||
%* show only exportable content;
|
||||
%mm_tree(root=%str(/) ,types=EXPORTABLE ,outds=exportable)
|
||||
|
||||
%* with specific types;
|
||||
%mm_tree(root=%str(/my/folder)
|
||||
,types=
|
||||
DeployedJob
|
||||
ExternalFile
|
||||
Folder
|
||||
Folder.SecuredData
|
||||
GeneratedTransform
|
||||
InformationMap.Relational
|
||||
Job
|
||||
Library
|
||||
Prompt
|
||||
StoredProcess
|
||||
Table
|
||||
,outds=morestuff)
|
||||
|
||||
<h4> Dependencies </h4>
|
||||
@li mf_getquotedstr.sas
|
||||
@li mm_getpublictypes.sas
|
||||
@li mf_isblank.sas
|
||||
|
||||
@param root= the parent folder under which to return all contents
|
||||
@param outds= the dataset to create that contains the list of directories
|
||||
@param types= Space-seperated, unquoted list of types for filtering the
|
||||
output. Special types:
|
||||
|
||||
* ALl - return all types (the default)
|
||||
* EXPORTABLE - return only the content types that can be exported in an SPK
|
||||
|
||||
@version 9.4
|
||||
@author Allan Bowe
|
||||
|
||||
**/
|
||||
%macro mm_tree(
|
||||
root=
|
||||
,types=ALL
|
||||
,outds=work.mm_tree
|
||||
)/*/STORE SOURCE*/;
|
||||
options noquotelenmax;
|
||||
|
||||
%if %mf_isblank(&root) %then %let root=/;
|
||||
|
||||
%if %str(&types)=EXPORTABLE %then %do;
|
||||
data;run;%local tempds; %let tempds=&syslast;
|
||||
%mm_getpublictypes(outds=&tempds)
|
||||
proc sql noprint;
|
||||
select publictype into: types separated by ' ' from &tempds;
|
||||
drop table &tempds;
|
||||
%end;
|
||||
|
||||
* use a temporary fileref to hold the response;
|
||||
filename response temp;
|
||||
/* get list of libraries */
|
||||
proc metadata in=
|
||||
'<GetMetadataObjects><Reposid>$METAREPOSITORY</Reposid>
|
||||
<Type>Tree</Type><Objects/><NS>SAS</NS>
|
||||
<Flags>384</Flags>
|
||||
<XMLSelect search="*[@TreeType='BIP Folder']"/>
|
||||
<Options/></GetMetadataObjects>'
|
||||
out=response;
|
||||
run;
|
||||
/*
|
||||
data _null_;
|
||||
infile response;
|
||||
input;
|
||||
put _infile_;
|
||||
run;
|
||||
*/
|
||||
|
||||
/* create an XML map to read the response */
|
||||
filename sxlemap temp;
|
||||
data _null_;
|
||||
file sxlemap;
|
||||
put '<SXLEMAP version="1.2" name="SASObjects"><TABLE name="SASObjects">';
|
||||
put "<TABLE-PATH syntax='XPath'>/GetMetadataObjects/Objects/Tree</TABLE-PATH>";
|
||||
put '<COLUMN name="pathuri">';
|
||||
put "<PATH syntax='XPath'>/GetMetadataObjects/Objects/Tree/@Id</PATH>";
|
||||
put "<TYPE>character</TYPE><DATATYPE>string</DATATYPE><LENGTH>64</LENGTH>";
|
||||
put '</COLUMN><COLUMN name="name">';
|
||||
put "<PATH syntax='XPath'>/GetMetadataObjects/Objects/Tree/@Name</PATH>";
|
||||
put "<TYPE>character</TYPE><DATATYPE>string</DATATYPE><LENGTH>256</LENGTH>";
|
||||
put '</COLUMN></TABLE></SXLEMAP>';
|
||||
run;
|
||||
libname _XML_ xml xmlfileref=response xmlmap=sxlemap;
|
||||
|
||||
data &outds;
|
||||
length metauri pathuri $64 name $256 path $1024
|
||||
publictype MetadataUpdated MetadataCreated $32;
|
||||
set _XML_.SASObjects;
|
||||
keep metauri name publictype MetadataUpdated MetadataCreated path;
|
||||
length parenturi pname $128 ;
|
||||
call missing(parenturi,pname);
|
||||
path=cats('/',name);
|
||||
/* get parents */
|
||||
tmpuri=pathuri;
|
||||
do while (metadata_getnasn(tmpuri,"ParentTree",1,parenturi)>0);
|
||||
rc=metadata_getattr(parenturi,"Name",pname);
|
||||
path=cats('/',pname,path);
|
||||
tmpuri=parenturi;
|
||||
end;
|
||||
|
||||
if path=:"&root";
|
||||
|
||||
%if "&types"="ALL" or ("&types" ne "ALL" and "&types" ne "Folder") %then %do;
|
||||
n=1;
|
||||
do while (metadata_getnasn(pathuri,"Members",n,metauri)>0);
|
||||
n+1;
|
||||
call missing(name,publictype,MetadataUpdated,MetadataCreated);
|
||||
rc=metadata_getattr(metauri,"Name", name);
|
||||
rc=metadata_getattr(metauri,"MetadataUpdated", MetadataUpdated);
|
||||
rc=metadata_getattr(metauri,"MetadataCreated", MetadataCreated);
|
||||
rc=metadata_getattr(metauri,"PublicType", PublicType);
|
||||
%if "&types" ne "ALL" %then %do;
|
||||
if publictype in (%mf_getquotedstr(&types)) then output;
|
||||
%end;
|
||||
%else output; ;
|
||||
end;
|
||||
%end;
|
||||
|
||||
rc=metadata_resolve(pathuri,pname,tmpuri);
|
||||
metauri=cats('OMSOBJ:',pname,'\',pathuri);
|
||||
rc=metadata_getattr(metauri,"Name", name);
|
||||
rc=metadata_getattr(pathuri,"MetadataUpdated", MetadataUpdated);
|
||||
rc=metadata_getattr(pathuri,"MetadataCreated", MetadataCreated);
|
||||
rc=metadata_getattr(pathuri,"PublicType", PublicType);
|
||||
path=substr(path,1,length(path)-length(name)-1);
|
||||
if publictype ne '' then output;
|
||||
run;
|
||||
|
||||
proc sort;
|
||||
by path;
|
||||
run;
|
||||
|
||||
/* clear references */
|
||||
filename sxlemap clear;
|
||||
filename response clear;
|
||||
libname _XML_ clear;
|
||||
|
||||
%mend;
|
||||
134
meta/mm_updateappextension.sas
Normal file
134
meta/mm_updateappextension.sas
Normal file
@@ -0,0 +1,134 @@
|
||||
/**
|
||||
@file
|
||||
@brief Add or update an extension to an application component
|
||||
@details A SAS Application (SoftwareComponent) is a great place to store app
|
||||
specific parameters. There are two main places those params can be stored:
|
||||
1) Configuration, and 2) Extensions. The second location will enable end
|
||||
users to modify parameters even if they don't have the Configuration Manager
|
||||
plugin in SMC. This macro can be used after creating an application with
|
||||
the mm_createapplication.sas macro. If a parameter with the same name
|
||||
exists, it is updated. If it does not, it is created.
|
||||
|
||||
Usage:
|
||||
|
||||
%mm_updateappextension(app=/my/metadata/path/myappname
|
||||
,paramname=My Param
|
||||
,paramvalue=My value
|
||||
,paramdesc=some description)
|
||||
|
||||
|
||||
@param app= the BIP Tree folder path plus Application Name
|
||||
@param paramname= Parameter name
|
||||
@param paramvalue= Parameter value
|
||||
@param paramdesc= Parameter description
|
||||
|
||||
@param frefin= change default inref if it clashes with an existing one
|
||||
@param frefout= change default outref if it clashes with an existing one
|
||||
@param mDebug= set to 1 to show debug messages in the log
|
||||
|
||||
@version 9.4
|
||||
@author Allan Bowe
|
||||
|
||||
**/
|
||||
|
||||
%macro mm_updateappextension(app=
|
||||
,paramname=
|
||||
,paramvalue=
|
||||
,paramdesc=Created by mm_updateappextension
|
||||
,frefin=inmeta,frefout=outmeta
|
||||
, mdebug=0);
|
||||
|
||||
|
||||
/* first, check if app (and param) exists */
|
||||
%local appuri exturi;
|
||||
%let appuri=stopifempty;
|
||||
%let exturi=createifempty;
|
||||
|
||||
data _null_;
|
||||
format type uri tsuri value $200.;
|
||||
call missing (of _all_);
|
||||
paramname=symget('paramname');
|
||||
path="&app(Application)";
|
||||
/* first, find the STP ID */
|
||||
if metadata_pathobj("",path,"Application",type,uri)>0 then do;
|
||||
/* we have an app in this location! */
|
||||
call symputx('appuri',uri,'l');
|
||||
cnt=1;
|
||||
do while (metadata_getnasn(uri,"Extensions",cnt,tsuri)>0);
|
||||
rc=metadata_getattr(tsuri,"Name",value);
|
||||
put tsuri= value=;
|
||||
if value=paramname then do;
|
||||
putlog "&sysmacroname: found existing param - " tsuri;
|
||||
rc=metadata_getattr(tsuri,"Id",value);
|
||||
call symputx('exturi',value,'l');
|
||||
stop;
|
||||
end;
|
||||
cnt+1;
|
||||
end;
|
||||
end;
|
||||
else put (_all_)(=);
|
||||
run;
|
||||
|
||||
%if &appuri=stopifempty %then %do;
|
||||
%put WARNING: &app.(Application) not found!;
|
||||
%return;
|
||||
%end;
|
||||
|
||||
/* escape the description so it can be stored as XML */
|
||||
data _null_;
|
||||
length outstr $32767;
|
||||
outstr=symget('paramdesc');
|
||||
outstr=tranwrd(outstr,'&','&');
|
||||
outstr=tranwrd(outstr,'<','<');
|
||||
outstr=tranwrd(outstr,'>','>');
|
||||
outstr=tranwrd(outstr,"'",''');
|
||||
outstr=tranwrd(outstr,'"','"');
|
||||
outstr=tranwrd(outstr,'0A'x,' ');
|
||||
outstr=tranwrd(outstr,'0D'x,' ');
|
||||
outstr=tranwrd(outstr,'$','$');
|
||||
call symputx('paramdesc',outstr,'l');
|
||||
run;
|
||||
|
||||
filename &frefin temp;
|
||||
|
||||
%if &exturi=createifempty %then %do;
|
||||
/* write header XML */
|
||||
data _null_;
|
||||
file &frefin;
|
||||
pname=quote(trim(symget('paramname')));
|
||||
pdesc=quote(trim(symget('paramdesc')));
|
||||
pvalue=quote(trim(symget('paramvalue')));
|
||||
put "<UpdateMetadata><Reposid>$METAREPOSITORY</Reposid><Metadata>"/
|
||||
" <SoftwareComponent id='&appuri' ><Extensions>" /
|
||||
' <Extension Name=' pname ' Desc=' pdesc ' value= ' pvalue ' />' /
|
||||
' </Extensions></SoftwareComponent>'/
|
||||
'</Metadata><NS>SAS</NS><Flags>268435456</Flags></UpdateMetadata>';
|
||||
run;
|
||||
|
||||
%end;
|
||||
%else %do;
|
||||
data _null_;
|
||||
file &frefin;
|
||||
pdesc=quote(trim(symget('paramdesc')));
|
||||
pvalue=quote(trim(symget('paramvalue')));
|
||||
put "<UpdateMetadata><Reposid>$METAREPOSITORY</Reposid><Metadata>"/
|
||||
" <Extension id='&exturi' Desc=" pdesc ' value= ' pvalue ' />' /
|
||||
'</Metadata><NS>SAS</NS><Flags>268435456</Flags></UpdateMetadata>';
|
||||
run;
|
||||
%end;
|
||||
|
||||
filename &frefout temp;
|
||||
|
||||
proc metadata in= &frefin out=&frefout verbose;
|
||||
run;
|
||||
|
||||
%if &mdebug=1 %then %do;
|
||||
/* write the response to the log for debugging */
|
||||
data _null_;
|
||||
infile &frefout lrecl=1048576;
|
||||
input;
|
||||
put _infile_;
|
||||
run;
|
||||
%end;
|
||||
|
||||
%mend;
|
||||
116
meta/mm_updatedocument.sas
Normal file
116
meta/mm_updatedocument.sas
Normal file
@@ -0,0 +1,116 @@
|
||||
/**
|
||||
@file
|
||||
@brief Update the TextStore in a Document with the same name
|
||||
@details Enables arbitrary content to be stored in a document object
|
||||
|
||||
Usage:
|
||||
|
||||
%mm_updatedocument(path=/my/metadata/path
|
||||
,name=docname
|
||||
,text="/file/system/some.txt")
|
||||
|
||||
|
||||
@param path= the BIP Tree folder path
|
||||
@param name=Document Name
|
||||
@param text=a source file containing the text to be added
|
||||
|
||||
@param frefin= change default inref if it clashes with an existing one
|
||||
@param frefout= change default outref if it clashes with an existing one
|
||||
@param mDebug= set to 1 to show debug messages in the log
|
||||
|
||||
@version 9.3
|
||||
@author Allan Bowe
|
||||
|
||||
**/
|
||||
|
||||
%macro mm_updatedocument(path=
|
||||
,name=
|
||||
,text=
|
||||
,frefin=inmeta
|
||||
,frefout=outmeta
|
||||
,mdebug=0
|
||||
);
|
||||
/* first, check if STP exists */
|
||||
%local tsuri;
|
||||
%let tsuri=stopifempty ;
|
||||
|
||||
data _null_;
|
||||
format type uri tsuri value $200.;
|
||||
call missing (of _all_);
|
||||
path="&path/&name(Note)";
|
||||
/* first, find the STP ID */
|
||||
if metadata_pathobj("",path,"Note",type,uri)>0 then do;
|
||||
/* get sourcetext */
|
||||
cnt=1;
|
||||
do while (metadata_getnasn(uri,"Notes",cnt,tsuri)>0);
|
||||
rc=metadata_getattr(tsuri,"Name",value);
|
||||
put tsuri= value=;
|
||||
if value="&name" then do;
|
||||
/* found it! */
|
||||
rc=metadata_getattr(tsuri,"Id",value);
|
||||
call symputx('tsuri',value,'l');
|
||||
stop;
|
||||
end;
|
||||
cnt+1;
|
||||
end;
|
||||
end;
|
||||
else put (_all_)(=);
|
||||
run;
|
||||
|
||||
%if &tsuri=stopifempty %then %do;
|
||||
%put WARNING: &path/&name.(Document) not found!;
|
||||
%return;
|
||||
%end;
|
||||
|
||||
%if %length(&text)<2 %then %do;
|
||||
%put WARNING: No text supplied!!;
|
||||
%return;
|
||||
%end;
|
||||
|
||||
filename &frefin temp recfm=n;
|
||||
|
||||
/* escape code so it can be stored as XML */
|
||||
/* input file may be over 32k wide, so deal with one char at a time */
|
||||
data _null_;
|
||||
file &frefin recfm=n;
|
||||
infile &text recfm=n;
|
||||
input instr $CHAR1. ;
|
||||
if _n_=1 then put "<UpdateMetadata><Reposid>$METAREPOSITORY</Reposid>
|
||||
<Metadata><TextStore id='&tsuri' StoredText='" @@;
|
||||
select (instr);
|
||||
when ('&') put '&';
|
||||
when ('<') put '<';
|
||||
when ('>') put '>';
|
||||
when ("'") put ''';
|
||||
when ('"') put '"';
|
||||
when ('0A'x) put '
';
|
||||
when ('0D'x) put '
';
|
||||
when ('$') put '$';
|
||||
otherwise put instr $CHAR1.;
|
||||
end;
|
||||
run;
|
||||
|
||||
data _null_;
|
||||
file &frefin mod;
|
||||
put "'></TextStore></Metadata><NS>SAS</NS><Flags>268435456</Flags>
|
||||
</UpdateMetadata>";
|
||||
run;
|
||||
|
||||
|
||||
filename &frefout temp;
|
||||
|
||||
proc metadata in= &frefin
|
||||
%if &mdebug=1 %then out=&frefout verbose;
|
||||
;
|
||||
run;
|
||||
|
||||
%if &mdebug=1 %then %do;
|
||||
/* write the response to the log for debugging */
|
||||
data _null_;
|
||||
infile &frefout lrecl=1048576;
|
||||
input;
|
||||
put _infile_;
|
||||
run;
|
||||
%end;
|
||||
|
||||
%mend;
|
||||
67
meta/mm_updatestpservertype.sas
Normal file
67
meta/mm_updatestpservertype.sas
Normal file
@@ -0,0 +1,67 @@
|
||||
/**
|
||||
@file mm_updatestpservertype.sas
|
||||
@brief Updates a type 2 stored process to run on STP or WKS context
|
||||
@details Only works on Type 2 (9.3 compatible) STPs
|
||||
|
||||
Usage:
|
||||
|
||||
%mm_updatestpservertype(target=/some/meta/path/myStoredProcess
|
||||
,type=WKS)
|
||||
|
||||
<h4> Dependencies </h4>
|
||||
|
||||
@param target= full path to the STP being deleted
|
||||
@param type= Either WKS or STP depending on whether Workspace or Stored Process
|
||||
type required
|
||||
|
||||
@version 9.4
|
||||
@author Allan Bowe
|
||||
|
||||
**/
|
||||
|
||||
%macro mm_updatestpservertype(
|
||||
target=
|
||||
,type=
|
||||
)/*/STORE SOURCE*/;
|
||||
|
||||
/**
|
||||
* Check STP does exist
|
||||
*/
|
||||
%local cmtype;
|
||||
data _null_;
|
||||
length type uri $256;
|
||||
rc=metadata_pathobj("","&target",'StoredProcess',type,uri);
|
||||
call symputx('cmtype',type,'l');
|
||||
call symputx('stpuri',uri,'l');
|
||||
run;
|
||||
%if &cmtype ne ClassifierMap %then %do;
|
||||
%put WARNING: No Stored Process found at ⌖
|
||||
%return;
|
||||
%end;
|
||||
|
||||
%local newtype;
|
||||
%if &type=WKS %then %let newtype=Wks;
|
||||
%else %let newtype=Sps;
|
||||
|
||||
%local result;
|
||||
%let result=NOT FOUND;
|
||||
data _null_;
|
||||
length uri name value $256;
|
||||
n=1;
|
||||
do while(metadata_getnasn("&stpuri","Notes",n,uri)>0);
|
||||
n+1;
|
||||
rc=metadata_getattr(uri,"Name",name);
|
||||
if name='Stored Process' then do;
|
||||
rc = METADATA_SETATTR(uri,'StoredText','<?xml version="1.0" encoding="UTF-8"?>'
|
||||
!!'<StoredProcess><ServerContext LogicalServerType="'!!"&newtype"
|
||||
!!'" OtherAllowed="false"/><ResultCapabilities Package="false" '
|
||||
!!' Streaming="true"/><OutputParameters/></StoredProcess>');
|
||||
if rc=0 then call symputx('result','SUCCESS');
|
||||
stop;
|
||||
end;
|
||||
end;
|
||||
run;
|
||||
%if &result=SUCCESS %then %put NOTE: SUCCESS: STP &target changed to &type type;
|
||||
%else %put %str(ERR)OR: Issue with &sysmacroname;
|
||||
|
||||
%mend;
|
||||
128
meta/mm_updatestpsourcecode.sas
Normal file
128
meta/mm_updatestpsourcecode.sas
Normal file
@@ -0,0 +1,128 @@
|
||||
/**
|
||||
@file
|
||||
@brief Update the source code of a type 2 STP
|
||||
@details Uploads the contents of a text file or fileref to an existing type 2
|
||||
STP. A type 2 STP has its source code saved in metadata.
|
||||
|
||||
Usage:
|
||||
|
||||
%mm_updatestpsourcecode(stp=/my/metadata/path/mystpname
|
||||
,stpcode="/file/system/source.sas")
|
||||
|
||||
|
||||
@param stp= the BIP Tree folder path plus Stored Process Name
|
||||
@param stpcode= the source file (or fileref) containing the SAS code to load
|
||||
into the stp. For multiple files, they should simply be concatenated first.
|
||||
@param minify= set to YES in order to strip comments, blank lines, and CRLFs.
|
||||
|
||||
@param frefin= change default inref if it clashes with an existing one
|
||||
@param frefout= change default outref if it clashes with an existing one
|
||||
@param mDebug= set to 1 to show debug messages in the log
|
||||
|
||||
@version 9.3
|
||||
@author Allan Bowe
|
||||
|
||||
**/
|
||||
|
||||
%macro mm_updatestpsourcecode(stp=
|
||||
,stpcode=
|
||||
,minify=NO
|
||||
,frefin=inmeta
|
||||
,frefout=outmeta
|
||||
,mdebug=0
|
||||
);
|
||||
/* first, check if STP exists */
|
||||
%local tsuri;
|
||||
%let tsuri=stopifempty ;
|
||||
|
||||
data _null_;
|
||||
format type uri tsuri value $200.;
|
||||
call missing (of _all_);
|
||||
path="&stp.(StoredProcess)";
|
||||
/* first, find the STP ID */
|
||||
if metadata_pathobj("",path,"StoredProcess",type,uri)>0 then do;
|
||||
/* get sourcecode */
|
||||
cnt=1;
|
||||
do while (metadata_getnasn(uri,"Notes",cnt,tsuri)>0);
|
||||
rc=metadata_getattr(tsuri,"Name",value);
|
||||
put tsuri= value=;
|
||||
if value="SourceCode" then do;
|
||||
/* found it! */
|
||||
rc=metadata_getattr(tsuri,"Id",value);
|
||||
call symputx('tsuri',value,'l');
|
||||
stop;
|
||||
end;
|
||||
cnt+1;
|
||||
end;
|
||||
end;
|
||||
else put (_all_)(=);
|
||||
run;
|
||||
|
||||
%if &tsuri=stopifempty %then %do;
|
||||
%put WARNING: &stp.(StoredProcess) not found!;
|
||||
%return;
|
||||
%end;
|
||||
|
||||
%if %length(&stpcode)<2 %then %do;
|
||||
%put WARNING: No SAS code supplied!!;
|
||||
%return;
|
||||
%end;
|
||||
|
||||
filename &frefin temp lrecl=32767;
|
||||
|
||||
/* write header XML */
|
||||
data _null_;
|
||||
file &frefin;
|
||||
put "<UpdateMetadata><Reposid>$METAREPOSITORY</Reposid>
|
||||
<Metadata><TextStore id='&tsuri' StoredText='";
|
||||
run;
|
||||
|
||||
/* escape code so it can be stored as XML */
|
||||
/* write contents */
|
||||
%if %length(&stpcode)>2 %then %do;
|
||||
data _null_;
|
||||
file &frefin mod;
|
||||
infile &stpcode lrecl=32767;
|
||||
length outstr $32767;
|
||||
input outstr ;
|
||||
/* escape code so it can be stored as XML */
|
||||
outstr=tranwrd(_infile_,'&','&');
|
||||
outstr=tranwrd(outstr,'<','<');
|
||||
outstr=tranwrd(outstr,'>','>');
|
||||
outstr=tranwrd(outstr,"'",''');
|
||||
outstr=tranwrd(outstr,'"','"');
|
||||
outstr=tranwrd(outstr,'0A'x,'
');
|
||||
outstr=tranwrd(outstr,'0D'x,'
');
|
||||
outstr=tranwrd(outstr,'$','$');
|
||||
%if &minify=YES %then %do;
|
||||
outstr=cats(outstr);
|
||||
if outstr ne '';
|
||||
if not (outstr=:'/*' and subpad(left(reverse(outstr)),1,2)='/*');
|
||||
%end;
|
||||
outstr=trim(outstr);
|
||||
put outstr ' ';
|
||||
run;
|
||||
%end;
|
||||
|
||||
data _null_;
|
||||
file &frefin mod;
|
||||
put "'></TextStore></Metadata><NS>SAS</NS><Flags>268435456</Flags>
|
||||
</UpdateMetadata>";
|
||||
run;
|
||||
|
||||
|
||||
filename &frefout temp;
|
||||
|
||||
proc metadata in= &frefin out=&frefout;
|
||||
run;
|
||||
|
||||
%if &mdebug=1 %then %do;
|
||||
/* write the response to the log for debugging */
|
||||
data _null_;
|
||||
infile &frefout lrecl=32767;
|
||||
input;
|
||||
put _infile_;
|
||||
run;
|
||||
%end;
|
||||
|
||||
%mend;
|
||||
159
meta/mm_webout.sas
Normal file
159
meta/mm_webout.sas
Normal file
@@ -0,0 +1,159 @@
|
||||
/**
|
||||
@file mm_webout.sas
|
||||
@brief Send data to/from SAS Stored Processes
|
||||
@details This macro should be added to the start of each Stored Process,
|
||||
**immediately** followed by a call to:
|
||||
|
||||
%mm_webout(FETCH)
|
||||
|
||||
This will read all the input data and create same-named SAS datasets in the
|
||||
WORK library. You can then insert your code, and send data back using the
|
||||
following syntax:
|
||||
|
||||
data some datasets; * make some data ;
|
||||
retain some columns;
|
||||
run;
|
||||
|
||||
%mm_webout(OPEN)
|
||||
%mm_webout(ARR,some) * Array format, fast, suitable for large tables ;
|
||||
%mm_webout(OBJ,datasets) * Object format, easier to work with ;
|
||||
|
||||
Finally, wrap everything up send some helpful system variables too
|
||||
|
||||
%mm_webout(CLOSE)
|
||||
|
||||
|
||||
@param action Either FETCH, OPEN, ARR, OBJ or CLOSE
|
||||
@param ds The dataset to send back to the frontend
|
||||
@param dslabel= value to use instead of the real name for sending to JSON
|
||||
@param fmt= set to N to send back unformatted values
|
||||
|
||||
@version 9.3
|
||||
@author Allan Bowe
|
||||
|
||||
**/
|
||||
%macro mm_webout(action,ds,dslabel=,fref=_webout,fmt=Y);
|
||||
%global _webin_file_count _webin_fileref1 _webin_name1 _program _debug;
|
||||
%local i tempds;
|
||||
|
||||
%if &action=FETCH %then %do;
|
||||
%if %str(&_debug) ge 131 %then %do;
|
||||
options mprint notes mprintnest;
|
||||
%end;
|
||||
%let _webin_file_count=%eval(&_webin_file_count+0);
|
||||
/* now read in the data */
|
||||
%do i=1 %to &_webin_file_count;
|
||||
%if &_webin_file_count=1 %then %do;
|
||||
%let _webin_fileref1=&_webin_fileref;
|
||||
%let _webin_name1=&_webin_name;
|
||||
%end;
|
||||
data _null_;
|
||||
infile &&_webin_fileref&i termstr=crlf;
|
||||
input;
|
||||
call symputx('input_statement',_infile_);
|
||||
putlog "&&_webin_name&i input statement: " _infile_;
|
||||
stop;
|
||||
data &&_webin_name&i;
|
||||
infile &&_webin_fileref&i firstobs=2 dsd termstr=crlf encoding='utf-8';
|
||||
input &input_statement;
|
||||
%if %str(&_debug) ge 131 %then %do;
|
||||
if _n_<20 then putlog _infile_;
|
||||
%end;
|
||||
run;
|
||||
%end;
|
||||
%end;
|
||||
|
||||
%else %if &action=OPEN %then %do;
|
||||
/* fix encoding */
|
||||
OPTIONS NOBOMFILE;
|
||||
data _null_;
|
||||
rc = stpsrv_header('Content-type',"text/html; encoding=utf-8");
|
||||
run;
|
||||
|
||||
/* setup json */
|
||||
data _null_;file &fref encoding='utf-8';
|
||||
%if %str(&_debug) ge 131 %then %do;
|
||||
put '>>weboutBEGIN<<';
|
||||
%end;
|
||||
put '{"START_DTTM" : "' "%sysfunc(datetime(),datetime20.3)" '"';
|
||||
run;
|
||||
|
||||
%end;
|
||||
|
||||
%else %if &action=ARR or &action=OBJ %then %do;
|
||||
%if &sysver=9.4 %then %do;
|
||||
%mp_jsonout(&action,&ds,dslabel=&dslabel,fmt=&fmt
|
||||
,engine=PROCJSON,dbg=%str(&_debug)
|
||||
)
|
||||
%end;
|
||||
%else %do;
|
||||
%mp_jsonout(&action,&ds,dslabel=&dslabel,fmt=&fmt
|
||||
,engine=DATASTEP,dbg=%str(&_debug)
|
||||
)
|
||||
%end;
|
||||
%end;
|
||||
%else %if &action=CLOSE %then %do;
|
||||
%if %str(&_debug) ge 131 %then %do;
|
||||
/* if debug mode, send back first 10 records of each work table also */
|
||||
options obs=10;
|
||||
data;run;%let tempds=%scan(&syslast,2,.);
|
||||
ods output Members=&tempds;
|
||||
proc datasets library=WORK memtype=data;
|
||||
%local wtcnt;%let wtcnt=0;
|
||||
data _null_;
|
||||
set &tempds;
|
||||
if not (name =:"DATA");
|
||||
i+1;
|
||||
call symputx('wt'!!left(i),name,'l');
|
||||
call symputx('wtcnt',i,'l');
|
||||
data _null_; file &fref encoding='utf-8';
|
||||
put ",""WORK"":{";
|
||||
%do i=1 %to &wtcnt;
|
||||
%let wt=&&wt&i;
|
||||
proc contents noprint data=&wt
|
||||
out=_data_ (keep=name type length format:);
|
||||
run;%let tempds=%scan(&syslast,2,.);
|
||||
data _null_; file &fref encoding='utf-8';
|
||||
dsid=open("WORK.&wt",'is');
|
||||
nlobs=attrn(dsid,'NLOBS');
|
||||
nvars=attrn(dsid,'NVARS');
|
||||
rc=close(dsid);
|
||||
if &i>1 then put ','@;
|
||||
put " ""&wt"" : {";
|
||||
put '"nlobs":' nlobs;
|
||||
put ',"nvars":' nvars;
|
||||
%mp_jsonout(OBJ,&tempds,jref=&fref,dslabel=colattrs,engine=DATASTEP)
|
||||
%mp_jsonout(OBJ,&wt,jref=&fref,dslabel=first10rows,engine=DATASTEP)
|
||||
data _null_; file &fref encoding='utf-8';
|
||||
put "}";
|
||||
%end;
|
||||
data _null_; file &fref encoding='utf-8';
|
||||
put "}";
|
||||
run;
|
||||
%end;
|
||||
/* close off json */
|
||||
data _null_;file &fref mod encoding='utf-8';
|
||||
_PROGRAM=quote(trim(resolve(symget('_PROGRAM'))));
|
||||
put ",""SYSUSERID"" : ""&sysuserid"" ";
|
||||
put ",""MF_GETUSER"" : ""%mf_getuser()"" ";
|
||||
put ",""_DEBUG"" : ""&_debug"" ";
|
||||
_METAUSER=quote(trim(symget('_METAUSER')));
|
||||
put ",""_METAUSER"": " _METAUSER;
|
||||
_METAPERSON=quote(trim(symget('_METAPERSON')));
|
||||
put ',"_METAPERSON": ' _METAPERSON;
|
||||
put ',"_PROGRAM" : ' _PROGRAM ;
|
||||
put ",""SYSCC"" : ""&syscc"" ";
|
||||
put ",""SYSERRORTEXT"" : ""&syserrortext"" ";
|
||||
put ",""SYSHOSTNAME"" : ""&syshostname"" ";
|
||||
put ",""SYSJOBID"" : ""&sysjobid"" ";
|
||||
put ",""SYSSITE"" : ""&syssite"" ";
|
||||
put ",""SYSWARNINGTEXT"" : ""&syswarningtext"" ";
|
||||
put ',"END_DTTM" : "' "%sysfunc(datetime(),datetime20.3)" '" ';
|
||||
put "}" @;
|
||||
%if %str(&_debug) ge 131 %then %do;
|
||||
put '>>weboutEND<<';
|
||||
%end;
|
||||
run;
|
||||
%end;
|
||||
|
||||
%mend;
|
||||
Reference in New Issue
Block a user