mirror of
https://github.com/sasjs/core.git
synced 2025-12-11 06:24:35 +00:00
Compare commits
36 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b3a8b4323e | |||
| 0592206f2d | |||
| bedc2a443a | |||
| 6f86ed62a2 | |||
| def0cc8476 | |||
| 3a9029557e | |||
| 9dc3bcd513 | |||
| 2bcf6346ac | |||
| 0eccc169f5 | |||
| 493639fe4a | |||
| 4987d2fbbc | |||
| 1a35b357d6 | |||
| a7792d93e4 | |||
| 541dc31ad0 | |||
| abccafab7b | |||
| f6cec012da | |||
| d51be73017 | |||
| cafffbb509 | |||
| a88efacfab | |||
| cc7cc55022 | |||
| 15687be5d6 | |||
| d9a82c0bdf | |||
| 6f06e5540d | |||
| 6b782a4fa2 | |||
| efe4709dde | |||
| 5cb41041d9 | |||
| f50cb03fd3 | |||
| ac46489f11 | |||
|
|
5e45701e74 | ||
|
|
8caaacd9f0 | ||
|
|
91983e0a91 | ||
|
|
7b72f0ac94 | ||
|
|
3eae34d8b7 | ||
|
|
58358c916d | ||
|
|
578ef26cd5 | ||
|
|
33189743cd |
2
LICENSE
2
LICENSE
@@ -1,4 +1,4 @@
|
||||
Copyright 2018 (Allan Bowe)
|
||||
Copyright 2020 (Allan Bowe)
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
|
||||
@version 9.2
|
||||
@author Allan Bowe
|
||||
@cond
|
||||
**/
|
||||
|
||||
%macro mf_abort(mac=mf_abort.sas, type=, msg=, iftrue=%str(1=1)
|
||||
@@ -139,3 +140,5 @@
|
||||
%abort cancel;
|
||||
%end;
|
||||
%mend;
|
||||
|
||||
/** @endcond */
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
@file mf_existfeature.sas
|
||||
@file
|
||||
@brief Checks whether a feature exists
|
||||
@details Check to see if a feature is supported in your environment.
|
||||
Run without arguments to see a list of detectable features.
|
||||
@@ -7,10 +7,10 @@
|
||||
actual feature detection, as that is tricky / impossible to do
|
||||
without generating errors in most cases.
|
||||
|
||||
%put %mf_existfeature(PROCLUA);
|
||||
%put %mf_existfeature(PROCLUA);
|
||||
|
||||
@param feature the feature to detect. Leave blank to list all in log.
|
||||
|
||||
|
||||
@return output returns 1 or 0 (or -1 if not found)
|
||||
|
||||
<h4> Dependencies </h4>
|
||||
@@ -20,6 +20,7 @@
|
||||
@version 8
|
||||
@author Allan Bowe
|
||||
**/
|
||||
/** @cond */
|
||||
|
||||
%macro mf_existfeature(feature
|
||||
)/*/STORE SOURCE*/;
|
||||
@@ -39,4 +40,6 @@
|
||||
-1
|
||||
%put &sysmacroname: &feature not found;
|
||||
%end;
|
||||
%mend;
|
||||
%mend;
|
||||
|
||||
/** @endcond */
|
||||
@@ -12,6 +12,7 @@
|
||||
@version 9.2
|
||||
@author Allan Bowe
|
||||
**/
|
||||
/** @cond */
|
||||
|
||||
%macro mf_existvar(libds /* 2 part dataset name */
|
||||
, var /* variable name */
|
||||
@@ -29,4 +30,6 @@
|
||||
%let rc=%sysfunc(close(&dsid));
|
||||
%end;
|
||||
|
||||
%mend;
|
||||
%mend;
|
||||
|
||||
/** @endcond */
|
||||
@@ -14,6 +14,7 @@
|
||||
|
||||
@version 9.2
|
||||
@author Allan Bowe
|
||||
@cond
|
||||
**/
|
||||
|
||||
%macro mf_existvarlist(libds, varlist
|
||||
@@ -53,4 +54,6 @@
|
||||
0
|
||||
%put Vars not found: &found;
|
||||
%end;
|
||||
%mend;
|
||||
%mend;
|
||||
|
||||
/** @endcond */
|
||||
@@ -3,7 +3,7 @@
|
||||
@brief Returns the engine type of a SAS library
|
||||
@details Usage:
|
||||
|
||||
%put %mf_getEngine(SASHELP);
|
||||
%put %mf_getengine(SASHELP);
|
||||
|
||||
returns:
|
||||
> V9
|
||||
@@ -21,9 +21,11 @@
|
||||
|
||||
@version 9.2
|
||||
@author Allan Bowe
|
||||
**/
|
||||
|
||||
%macro mf_getEngine(libref
|
||||
**/
|
||||
/** @cond */
|
||||
|
||||
%macro mf_getengine(libref
|
||||
)/*/STORE SOURCE*/;
|
||||
%local dsid engnum rc engine;
|
||||
|
||||
@@ -42,3 +44,5 @@
|
||||
&engine
|
||||
|
||||
%mend;
|
||||
|
||||
/** @endcond */
|
||||
@@ -17,6 +17,7 @@
|
||||
|
||||
@version 9.2
|
||||
@author Allan Bowe
|
||||
@cond
|
||||
**/
|
||||
|
||||
%macro mf_getschema(libref
|
||||
@@ -38,3 +39,5 @@
|
||||
&schema
|
||||
|
||||
%mend;
|
||||
|
||||
/** @endcond */
|
||||
|
||||
@@ -1,17 +1,18 @@
|
||||
/**
|
||||
@file
|
||||
@brief Assigns and returns an unused fileref
|
||||
@details Use as follows:
|
||||
@details
|
||||
Use as follows:
|
||||
|
||||
%let fileref1=%mf_getuniquefileref();
|
||||
%let fileref2=%mf_getuniquefileref();
|
||||
%put &fileref1 &fileref2;
|
||||
%let fileref1=%mf_getuniquefileref();
|
||||
%let fileref2=%mf_getuniquefileref();
|
||||
%put &fileref1 &fileref2;
|
||||
|
||||
which returns:
|
||||
|
||||
> mcref0 mcref1
|
||||
|
||||
@prefix= first part of fileref. Remember that filerefs can only be 8
|
||||
@param prefix= first part of fileref. Remember that filerefs can only be 8
|
||||
characters, so a 7 letter prefix would mean that `maxtries` should be 10.
|
||||
@param maxtries= the last part of the libref. Provide an integer value.
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
|
||||
> mclib3
|
||||
|
||||
@prefix= first part of libref. Remember that librefs can only be 8 characters,
|
||||
@param prefix= first part of libref. Remember that librefs can only be 8 characters,
|
||||
so a 7 letter prefix would mean that maxtries should be 10.
|
||||
@param maxtries= the last part of the libref. Provide an integer value.
|
||||
|
||||
|
||||
33
base/mf_isdir.sas
Normal file
33
base/mf_isdir.sas
Normal file
@@ -0,0 +1,33 @@
|
||||
/**
|
||||
@file
|
||||
@brief Checks whether a path is a valid directory
|
||||
@details
|
||||
Usage:
|
||||
|
||||
%let isdir=%mf_isdir(/tmp);
|
||||
|
||||
With thanks and full credit to Andrea Defronzo - https://www.linkedin.com/in/andrea-defronzo-b1a47460/
|
||||
|
||||
@param path full path of the file/directory to be checked
|
||||
|
||||
@return output returns 1 if path is a directory, 0 if it is not
|
||||
|
||||
@version 9.2
|
||||
**/
|
||||
|
||||
%macro mf_isdir(path
|
||||
)/*/STORE SOURCE*/;
|
||||
%local rc did is_directory fref_t;
|
||||
|
||||
%let is_directory = 0;
|
||||
%let rc = %sysfunc(filename(fref_t, %superq(path)));
|
||||
%let did = %sysfunc(dopen(&fref_t.));
|
||||
%if &did. ^= 0 %then %do;
|
||||
%let is_directory = 1;
|
||||
%let rc = %sysfunc(dclose(&did.));
|
||||
%end;
|
||||
%let rc = %sysfunc(filename(fref_t));
|
||||
|
||||
&is_directory
|
||||
|
||||
%mend;
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
%put %mf_trimstr(/blah/,/); * /blah;
|
||||
%put %mf_trimstr(/blah/,h); * /blah/;
|
||||
%put %mf_trimstr(/blah/,h/); */bla;
|
||||
%put %mf_trimstr(/blah/,h/);* /bla;
|
||||
|
||||
<h4> Dependencies </h4>
|
||||
|
||||
@@ -23,13 +23,23 @@
|
||||
**/
|
||||
|
||||
%macro mf_trimstr(basestr,trimstr);
|
||||
%local trimlen trimval;
|
||||
%local baselen trimlen trimval;
|
||||
|
||||
/* return if basestr is shorter than trimstr (or 0) */
|
||||
%let baselen=%length(%superq(basestr));
|
||||
%let trimlen=%length(%superq(trimstr));
|
||||
%if &baselen < &trimlen or &baselen=0 %then %return;
|
||||
|
||||
/* obtain the characters from the end of basestr */
|
||||
%let trimval=%qsubstr(%superq(basestr)
|
||||
,%length(%superq(basestr))-&trimlen+1
|
||||
,&trimlen);
|
||||
|
||||
%if %superq(trimval)=%superq(trimstr) %then %do;
|
||||
/* compare and if matching, chop it off! */
|
||||
%if %superq(basestr)=%superq(trimstr) %then %do;
|
||||
%return;
|
||||
%end;
|
||||
%else %if %superq(trimval)=%superq(trimstr) %then %do;
|
||||
%qsubstr(%superq(basestr),1,%length(%superq(basestr))-&trimlen)
|
||||
%end;
|
||||
%else %do;
|
||||
|
||||
@@ -6,12 +6,19 @@
|
||||
results back to the client in an STP Web App context, or completely stop
|
||||
in the case of a batch run.
|
||||
|
||||
Using SAS Abort Cancel mechanisms can cause hung sessions in some Stored Process
|
||||
environments. This macro takes a unique approach - we set the SAS syscc to 0,
|
||||
run `stpsrvset('program error', 0)` (if SAS 9) and then - we open a macro
|
||||
but don't close it! This provides a graceful abort for SAS web services in all
|
||||
web enabled environments.
|
||||
|
||||
@param mac= to contain the name of the calling macro
|
||||
@param msg= message to be returned
|
||||
@param iftrue= supply a condition under which the macro should be executed.
|
||||
|
||||
@version 9.4M3
|
||||
@author Allan Bowe
|
||||
@cond
|
||||
**/
|
||||
|
||||
%macro mp_abort(mac=mp_abort.sas, type=, msg=, iftrue=%str(1=1)
|
||||
@@ -24,7 +31,7 @@
|
||||
%put NOTE - &msg;
|
||||
|
||||
/* Stored Process Server web app context */
|
||||
%if %symexist(_metaperson)
|
||||
%if %symexist(_metaperson)
|
||||
or (%symexist(SYSPROCESSNAME) and "&SYSPROCESSNAME"="Compute Server" )
|
||||
%then %do;
|
||||
options obs=max replace nosyntaxcheck mprint;
|
||||
@@ -65,8 +72,15 @@
|
||||
%end;
|
||||
|
||||
%if %symexist(SYS_JES_JOB_URI) %then %do;
|
||||
/* refer web service output to file service in one hit */
|
||||
filename _webout filesrvc parenturi="&SYS_JES_JOB_URI" name="_webout.json";
|
||||
/* setup webout */
|
||||
OPTIONS NOBOMFILE;
|
||||
%if "X&SYS_JES_JOB_URI.X"="XX" %then %do;
|
||||
filename _webout temp lrecl=999999 mod;
|
||||
%end;
|
||||
%else %do;
|
||||
filename _webout filesrvc parenturi="&SYS_JES_JOB_URI"
|
||||
name="_webout.json" lrecl=999999 mod;
|
||||
%end;
|
||||
%end;
|
||||
|
||||
/* send response in SASjs JSON format */
|
||||
@@ -138,3 +152,4 @@
|
||||
%end;
|
||||
%mend;
|
||||
|
||||
/** @endcond */
|
||||
@@ -8,6 +8,7 @@
|
||||
applying CRLF line endings and converting embedded cr and crlf to lf.
|
||||
|
||||
usage:
|
||||
|
||||
fileref mycsv "/path/your/csv";
|
||||
%mp_cleancsv(in=mycsv,out=/path/new.csv)
|
||||
|
||||
@@ -17,6 +18,7 @@
|
||||
|
||||
@version 9.2
|
||||
@author Allan Bowe
|
||||
@cond
|
||||
**/
|
||||
|
||||
%macro mp_cleancsv(in=NOTPROVIDED,out=NOTPROVIDED,qchar='22'x);
|
||||
@@ -65,4 +67,5 @@
|
||||
else put inchar $char1.;
|
||||
end;
|
||||
run;
|
||||
%mend;
|
||||
%mend;
|
||||
/** @endcond */
|
||||
144
base/mp_csv2ds.sas
Normal file
144
base/mp_csv2ds.sas
Normal file
@@ -0,0 +1,144 @@
|
||||
/**
|
||||
@file mp_csv2ds.sas
|
||||
@brief Efficient import of arbitrary CSV using a dataset as template
|
||||
@details Used to import relevant columns from a large CSV using
|
||||
a dataset to provide the types and lengths. Assumes that a header
|
||||
row is provided, and datarows start on line 2. Extra columns in
|
||||
both the CSV and base dataset are ignored.
|
||||
|
||||
Usage:
|
||||
|
||||
filename mycsv temp;
|
||||
data _null_;
|
||||
file mycsv;
|
||||
put 'name,age,nickname';
|
||||
put 'John,48,Jonny';
|
||||
put 'Jennifer,23,Jen';
|
||||
run;
|
||||
|
||||
%mp_csv2ds(inref=mycsv,outds=myds,baseds=sashelp.class)
|
||||
|
||||
|
||||
@param inref= fileref to the CSV
|
||||
@param outds= output ds (lib.ds format)
|
||||
@param view= Set to YES or NO to determine whether the output should be
|
||||
a view or not. Default is NO (not a view).
|
||||
@param baseds= Template dataset on which to create the input statement.
|
||||
Is used to determine types, lengths, and any informats.
|
||||
|
||||
@version 9.2
|
||||
@author Allan Bowe
|
||||
|
||||
<h4> Dependencies </h4>
|
||||
@li mp_abort.sas
|
||||
@li mf_existds.sas
|
||||
|
||||
**/
|
||||
|
||||
%macro mp_csv2ds(inref=0,outds=0,baseds=0,view=NO);
|
||||
|
||||
%mp_abort(iftrue=( &inref=0 )
|
||||
,mac=&sysmacroname
|
||||
,msg=%str(the INREF variable must be provided)
|
||||
)
|
||||
%mp_abort(iftrue=( %superq(outds)=0 )
|
||||
,mac=&sysmacroname
|
||||
,msg=%str(the OUTDS variable must be provided)
|
||||
)
|
||||
%mp_abort(iftrue=( &baseds=0 )
|
||||
,mac=&sysmacroname
|
||||
,msg=%str(the BASEDS variable must be provided)
|
||||
)
|
||||
%mp_abort(iftrue=( &baseds=0 )
|
||||
,mac=&sysmacroname
|
||||
,msg=%str(the BASEDS variable must be provided)
|
||||
)
|
||||
%mp_abort(iftrue=( %mf_existds(&baseds)=0 )
|
||||
,mac=&sysmacroname
|
||||
,msg=%str(the BASEDS dataset (&baseds) needs to be assigned, and to exist)
|
||||
)
|
||||
|
||||
/* count rows */
|
||||
%local hasheader; %let hasheader=0;
|
||||
data _null_;
|
||||
if _N_ > 1 then do;
|
||||
call symputx('hasheader',1,'l');
|
||||
stop;
|
||||
end;
|
||||
infile &inref;
|
||||
input;
|
||||
run;
|
||||
%mp_abort(iftrue=( &hasheader=0 )
|
||||
,mac=&sysmacroname
|
||||
,msg=%str(No header row in &inref)
|
||||
)
|
||||
|
||||
/* get the variables in the CSV */
|
||||
data _data_;
|
||||
infile &inref;
|
||||
input;
|
||||
length name $32;
|
||||
do i=1 to countc(_infile_,',')+1;
|
||||
name=upcase(scan(_infile_,i,','));
|
||||
output;
|
||||
end;
|
||||
stop;
|
||||
run;
|
||||
%local csv_vars;%let csv_vars=&syslast;
|
||||
|
||||
/* get the variables in the dataset */
|
||||
proc contents noprint data=&baseds
|
||||
out=_data_ (keep=name type length format: informat);
|
||||
run;
|
||||
%local base_vars; %let base_vars=&syslast;
|
||||
|
||||
proc sql undo_policy=none;
|
||||
create table &csv_vars as
|
||||
select a.*
|
||||
,b.type
|
||||
,b.length
|
||||
,b.format
|
||||
,b.formatd
|
||||
,b.formatl
|
||||
,b.informat
|
||||
from &csv_vars a
|
||||
left join &base_vars b
|
||||
on a.name=upcase(b.name)
|
||||
order by i;
|
||||
|
||||
/* prepare the input statement */
|
||||
%local instat dropvars;
|
||||
data _null_;
|
||||
set &syslast end=last;
|
||||
length in dropvars $32767;
|
||||
retain in dropvars;
|
||||
if missing(type) then do;
|
||||
informat='$1.';
|
||||
dropvars=catx(' ',dropvars,name);
|
||||
end;
|
||||
else if missing(informat) then do;
|
||||
if type=1 then informat='best.';
|
||||
else informat=cats('$',length,'.');
|
||||
end;
|
||||
else informat=cats(informat,'.');
|
||||
in=catx(' ',in,name,':',informat);
|
||||
if last then do;
|
||||
call symputx('instat',in,'l');
|
||||
call symputx('dropvars',dropvars,'l');
|
||||
end;
|
||||
run;
|
||||
|
||||
/* import the CSV */
|
||||
data &outds
|
||||
%if %upcase(&view)=YES %then %do;
|
||||
/view=&outds
|
||||
%end;
|
||||
;
|
||||
infile &inref dsd firstobs=2;
|
||||
input &instat;
|
||||
%if %length(&dropvars)>0 %then %do;
|
||||
drop &dropvars;
|
||||
%end;
|
||||
run;
|
||||
|
||||
%mend;
|
||||
@@ -1,17 +1,17 @@
|
||||
/**
|
||||
@file
|
||||
@brief Returns all files and subdirectories within a specified parent
|
||||
@details When used with getattrs=NO, is not OS specific (uses dopen / dread).
|
||||
@details When used with getattrs=NO, is not OS specific (uses dopen / dread).
|
||||
|
||||
If getattrs=YES then the doptname / foptname functions are used to scan all
|
||||
properties - any characters that are not valid in a SAS name (v7) are simply
|
||||
properties - any characters that are not valid in a SAS name (v7) are simply
|
||||
stripped, and the table is transposed so theat each property is a column
|
||||
and there is one file per row. An attempt is made to get all properties
|
||||
and there is one file per row. An attempt is made to get all properties
|
||||
whether a file or folder, but some files/folders cannot be accessed, and so
|
||||
not all properties can / will be populated.
|
||||
|
||||
Credit for the rename approach:
|
||||
https://communities.sas.com/t5/SAS-Programming/SAS-Function-to-convert-string-to-Legal-SAS-Name/m-p/27375/highlight/true#M5003
|
||||
https://communities.sas.com/t5/SAS-Programming/SAS-Function-to-convert-string-to-Legal-SAS-Name/m-p/27375/highlight/true#M5003
|
||||
|
||||
|
||||
usage:
|
||||
@@ -20,21 +20,25 @@
|
||||
|
||||
%mp_dirlist(outds=cwdfileprops, getattrs=YES)
|
||||
|
||||
@warning In a Unix environment, the existence of a named pipe will cause this
|
||||
%mp_dirlist(fref=MYFREF)
|
||||
|
||||
@warning In a Unix environment, the existence of a named pipe will cause this
|
||||
macro to hang. Therefore this tool should be used with caution in a SAS 9 web
|
||||
application, as it can use up all available multibridge sessions if requests
|
||||
are resubmitted.
|
||||
If anyone finds a way to positively identify a named pipe using SAS (without
|
||||
If anyone finds a way to positively identify a named pipe using SAS (without
|
||||
X CMD) do please raise an issue!
|
||||
|
||||
|
||||
@param path= for which to return contents
|
||||
@param fref= Provide a DISK engine fileref as an alternative to PATH
|
||||
@param outds= the output dataset to create
|
||||
@param getattrs= YES/NO (default=NO). Uses doptname and foptname to return
|
||||
all attributes for each file / folder.
|
||||
@param getattrs= YES/NO (default=NO). Uses doptname and foptname to return
|
||||
all attributes for each file / folder.
|
||||
|
||||
|
||||
@returns outds contains the following variables:
|
||||
- directory (containing folder)
|
||||
- file_or_folder (file / folder)
|
||||
- filepath (path/to/file.name)
|
||||
- filename (just the file name)
|
||||
@@ -47,18 +51,26 @@
|
||||
**/
|
||||
|
||||
%macro mp_dirlist(path=%sysfunc(pathname(work))
|
||||
, fref=0
|
||||
, outds=work.mp_dirlist
|
||||
, getattrs=NO
|
||||
)/*/STORE SOURCE*/;
|
||||
%let getattrs=%upcase(&getattrs)XX;
|
||||
|
||||
data &outds (compress=no keep=file_or_folder filepath filename ext msg);
|
||||
length filepath $500 fref fref2 $8 file_or_folder $6 filename $80 ext $20 msg $200;
|
||||
rc = filename(fref, "&path");
|
||||
data &outds (compress=no keep=file_or_folder filepath filename ext msg directory);
|
||||
length directory filepath $500 fref fref2 $8 file_or_folder $6 filename $80 ext $20 msg $200;
|
||||
%if &fref=0 %then %do;
|
||||
rc = filename(fref, "&path");
|
||||
%end;
|
||||
%else %do;
|
||||
fref="&fref";
|
||||
rc=0;
|
||||
%end;
|
||||
if rc = 0 then do;
|
||||
did = dopen(fref);
|
||||
directory=dinfo(did,'Directory');
|
||||
if did=0 then do;
|
||||
putlog "NOTE: This directory is empty - &path";
|
||||
putlog "NOTE: This directory is empty - " directory;
|
||||
msg=sysmsg();
|
||||
put _all_;
|
||||
stop;
|
||||
@@ -73,7 +85,8 @@ data &outds (compress=no keep=file_or_folder filepath filename ext msg);
|
||||
dnum = dnum(did);
|
||||
do i = 1 to dnum;
|
||||
filename = dread(did, i);
|
||||
rc = filename(fref2, "&path/"!!filename);
|
||||
filepath=cats(directory,'/',filename);
|
||||
rc = filename(fref2,filepath);
|
||||
midd=dopen(fref2);
|
||||
dmsg=sysmsg();
|
||||
if did > 0 then file_or_folder='folder';
|
||||
@@ -82,12 +95,12 @@ data &outds (compress=no keep=file_or_folder filepath filename ext msg);
|
||||
fmsg=sysmsg();
|
||||
if midf > 0 then file_or_folder='file';
|
||||
rc=fclose(midf);
|
||||
|
||||
if index(fmsg,'File is in use') or index(dmsg,'is not a directory')
|
||||
|
||||
if index(fmsg,'File is in use') or index(dmsg,'is not a directory')
|
||||
then file_or_folder='file';
|
||||
else if index(fmsg, 'Insufficient authorization') then file_or_folder='file';
|
||||
else if file_or_folder='' then file_or_folder='locked';
|
||||
|
||||
|
||||
if file_or_folder='file' then do;
|
||||
ext = prxchange('s/.*\.{1,1}(.*)/$1/', 1, filename);
|
||||
if filename = ext then ext = ' ';
|
||||
@@ -96,7 +109,6 @@ data &outds (compress=no keep=file_or_folder filepath filename ext msg);
|
||||
ext='';
|
||||
file_or_folder='folder';
|
||||
end;
|
||||
filepath="&path/"!!filename;
|
||||
output;
|
||||
end;
|
||||
rc = dclose(did);
|
||||
@@ -120,7 +132,7 @@ run;
|
||||
else do i=1 to foptnum(fid);
|
||||
infoname=foptname(fid,i);
|
||||
infoval=finfo(fid,infoname);
|
||||
sasname=compress(infoname, '_', 'adik');
|
||||
sasname=compress(infoname, '_', 'adik');
|
||||
if anydigit(sasname)=1 then sasname=substr(sasname,anyalpha(sasname));
|
||||
if upcase(sasname) ne 'FILENAME' then output;
|
||||
end;
|
||||
@@ -137,7 +149,7 @@ run;
|
||||
else do i=1 to doptnum(fid);
|
||||
infoname=doptname(fid,i);
|
||||
infoval=dinfo(fid,infoname);
|
||||
sasname=compress(infoname, '_', 'adik');
|
||||
sasname=compress(infoname, '_', 'adik');
|
||||
if anydigit(sasname)=1 then sasname=substr(sasname,anyalpha(sasname));
|
||||
if upcase(sasname) ne 'FILENAME' then output;
|
||||
end;
|
||||
|
||||
@@ -103,8 +103,7 @@ proc sql
|
||||
order by ranuni(42)
|
||||
%end;
|
||||
;
|
||||
|
||||
|
||||
reset outobs=max;
|
||||
create table datalines1 as
|
||||
select name,type,length,varnum,format,label from dictionary.columns
|
||||
where libname="%upcase(%scan(&base_ds,1))"
|
||||
|
||||
58
base/mp_ds2csv.sas
Normal file
58
base/mp_ds2csv.sas
Normal file
@@ -0,0 +1,58 @@
|
||||
/**
|
||||
@file
|
||||
@brief Export a dataset to a CSV file
|
||||
@details Export to a file or a fileref
|
||||
Usage:
|
||||
|
||||
%mp_ds2csv(sashelp.class,outref="%sysfunc(pathname(work))/file.csv")
|
||||
|
||||
@param ds The dataset to be exported
|
||||
@param outfile= The output filename - should be quoted.
|
||||
@param outref= The output fileref (takes precedence if provided)
|
||||
@param outencoding= The output encoding to use (unquoted)
|
||||
|
||||
@version 9.2
|
||||
@author Allan Bowe (credit mjsq)
|
||||
**/
|
||||
|
||||
%macro mp_ds2csv(ds, outref=0, outfile=, outencoding=0
|
||||
)/*/STORE SOURCE*/;
|
||||
|
||||
%if not %sysfunc(exist(&ds)) %then %do;
|
||||
%put WARNING: &ds does not exist;
|
||||
%return;
|
||||
%end;
|
||||
|
||||
%if %index(&ds,.)=0 %then %let ds=WORK.&ds;
|
||||
|
||||
%if &outencoding=0 %then %let outencoding=;
|
||||
%else %let outencoding=encoding="&outencoding";
|
||||
|
||||
%local outloc;
|
||||
%if &outref=0 %then %let outloc=&outfile;
|
||||
%else %let outloc=&outref;
|
||||
|
||||
/* credit to mjsq - https://stackoverflow.com/a/55642267 */
|
||||
|
||||
/* first get headers */
|
||||
data _null_;
|
||||
file &outloc dlm=',' dsd &outencoding lrecl=32767;
|
||||
length header $ 2000;
|
||||
dsid=open("&ds.","i");
|
||||
num=attrn(dsid,"nvars");
|
||||
do i=1 to num;
|
||||
header = trim(left(coalescec(varlabel(dsid,i),varname(dsid,i))));
|
||||
put header @;
|
||||
end;
|
||||
rc=close(dsid);
|
||||
run;
|
||||
|
||||
/* next, export data */
|
||||
data _null_;
|
||||
set &ds.;
|
||||
file &outloc mod dlm=',' dsd &outencoding lrecl=32767;
|
||||
put (_all_) (+0);
|
||||
run;
|
||||
|
||||
|
||||
%mend;
|
||||
326
base/mp_getdbml.sas
Normal file
326
base/mp_getdbml.sas
Normal file
@@ -0,0 +1,326 @@
|
||||
/**
|
||||
@file
|
||||
@brief Extract DBML from SAS Libraries
|
||||
@details DBML is an open source markup format to represent databases.
|
||||
More details: https://www.dbml.org/home/
|
||||
|
||||
Usage:
|
||||
|
||||
|
||||
%mp_getdbml(liblist=SASHELP WORK,outref=mydbml,showlog=YES)
|
||||
|
||||
Take the log output and paste it into the renderer at https://dbdiagram.io
|
||||
to view your data model diagram. The code takes a "best guess" at
|
||||
the one to one and one to many relationships (based on constraints
|
||||
and indexes, and assuming that the column names would match).
|
||||
|
||||
You may need to adjust the rendered DBML to suit your needs.
|
||||
|
||||

|
||||
|
||||
|
||||
<h4> SAS Macros </h4>
|
||||
@li mf_getquotedstr.sas
|
||||
@li mp_getconstraints.sas
|
||||
|
||||
@param liblist= Space seperated list of librefs to take as
|
||||
input (Default=SASHELP)
|
||||
@param outref= Fileref to contain the DBML (Default=getdbml)
|
||||
@param showlog= set to YES to show the DBML in the log (Default is NO)
|
||||
|
||||
@version 9.3
|
||||
@author Allan Bowe
|
||||
**/
|
||||
|
||||
%macro mp_getdbml(liblist=SASHELP,outref=getdbml,showlog=NO
|
||||
)/*/STORE SOURCE*/;
|
||||
|
||||
/* check fileref is assigned */
|
||||
%if %sysfunc(fileref(&outref)) > 0 %then %do;
|
||||
filename &outref temp;
|
||||
%end;
|
||||
|
||||
%let liblist=%upcase(&liblist);
|
||||
|
||||
proc sql noprint;
|
||||
create table _data_ as
|
||||
select * from dictionary.tables
|
||||
where upcase(libname) in (%mf_getquotedstr(&liblist))
|
||||
order by libname,memname;
|
||||
%local tabinfo; %let tabinfo=&syslast;
|
||||
|
||||
create table _data_ as
|
||||
select * from dictionary.columns
|
||||
where upcase(libname) in (%mf_getquotedstr(&liblist))
|
||||
order by libname,memname,varnum;
|
||||
%local colinfo; %let colinfo=&syslast;
|
||||
|
||||
%local dsnlist;
|
||||
select distinct upcase(cats(libname,'.',memname)) into: dsnlist
|
||||
separated by ' '
|
||||
from &syslast
|
||||
;
|
||||
|
||||
create table _data_ as
|
||||
select * from dictionary.indexes
|
||||
where upcase(libname) in (%mf_getquotedstr(&liblist))
|
||||
order by idxusage, indxname, indxpos;
|
||||
%local idxinfo; %let idxinfo=&syslast;
|
||||
|
||||
/* Extract all Primary Key and Unique data constraints */
|
||||
%mp_getconstraints(lib=%scan(&liblist,1),outds=_data_)
|
||||
%local colconst; %let colconst=&syslast;
|
||||
|
||||
%do x=2 %to %sysfunc(countw(&liblist));
|
||||
%mp_getconstraints(lib=%scan(&liblist,&x),outds=_data_)
|
||||
proc append base=&colconst data=&syslast;
|
||||
run;
|
||||
%end;
|
||||
|
||||
|
||||
|
||||
|
||||
/* header info */
|
||||
data _null_;
|
||||
file &outref;
|
||||
put "// DBML generated by &sysuserid on %sysfunc(datetime(),datetime19.) ";
|
||||
put "Project sasdbml {";
|
||||
put " database_type: 'SAS'";
|
||||
put " Note: 'Generated by the mp_getdbml() macro'";
|
||||
put "}";
|
||||
run;
|
||||
|
||||
/* create table groups */
|
||||
data _null_;
|
||||
file &outref mod;
|
||||
set &tabinfo;
|
||||
by libname;
|
||||
if first.libname then put "TableGroup " libname "{";
|
||||
ds=quote(cats(libname,'.',memname));
|
||||
put ' ' ds;
|
||||
if last.libname then put "}";
|
||||
run;
|
||||
|
||||
/* table for pks */
|
||||
data _data_;
|
||||
length curds const col $39;
|
||||
call missing (of _all_);
|
||||
stop;
|
||||
run;
|
||||
%let pkds=&syslast;
|
||||
|
||||
%local x curds constraints_used constcheck;
|
||||
%do x=1 %to %sysfunc(countw(&dsnlist,%str( )));
|
||||
%let curds=%scan(&dsnlist,&x,%str( ));
|
||||
%let constraints_used=;
|
||||
%let constcheck=0;
|
||||
data _null_;
|
||||
file &outref mod;
|
||||
length lab $1024 typ $20;
|
||||
set &colinfo (where=(
|
||||
libname="%scan(&curds,1,.)" and upcase(memname)="%scan(&curds,2,.)"
|
||||
)) end=last;
|
||||
|
||||
if _n_=1 then do;
|
||||
table='Table "'!!"&curds"!!'"{';
|
||||
put table;
|
||||
end;
|
||||
name=upcase(name);
|
||||
lab=" note:"!!quote(trim(tranwrd(label,'"',"'")));
|
||||
if upcase(format)=:'DATETIME' then typ='datetime';
|
||||
else if type='char' then typ=cats('char(',length,')');
|
||||
else typ='num';
|
||||
|
||||
if notnull='yes' then notnul=' not null';
|
||||
if notnull='no' and missing(label) then put ' ' name typ;
|
||||
else if notnull='yes' and missing(label) then put ' ' name typ '[' notnul ']';
|
||||
else if notnull='no' then put ' ' name typ '[' lab ']';
|
||||
else put ' ' name typ '[' notnul ',' lab ']';
|
||||
|
||||
run;
|
||||
|
||||
data _data_(keep=curds const col);
|
||||
length ctype $11 cols constraints_used $5000;
|
||||
set &colconst (where=(
|
||||
upcase(libref)="%scan(&curds,1,.)"
|
||||
and upcase(table_name)="%scan(&curds,2,.)"
|
||||
and constraint_type in ('PRIMARY','UNIQUE')
|
||||
)) end=last;
|
||||
file &outref mod;
|
||||
by constraint_type constraint_name;
|
||||
retain cols;
|
||||
column_name=upcase(column_name);
|
||||
|
||||
if _n_=1 then put / ' indexes {';
|
||||
|
||||
if upcase(strip(constraint_type)) = 'PRIMARY' then ctype='[pk]';
|
||||
else ctype='[unique]';
|
||||
|
||||
if first.constraint_name then cols = cats('(',column_name);
|
||||
else cols=cats(cols,',',column_name);
|
||||
|
||||
if last.constraint_name then do;
|
||||
cols=cats(cols,')',ctype)!!' //'!!constraint_name;
|
||||
put ' ' cols;
|
||||
constraints_used=catx(' ',constraints_used, constraint_name);
|
||||
call symputx('constcheck',1);
|
||||
end;
|
||||
|
||||
if last then call symputx('constraints_used',cats(upcase(constraints_used)));
|
||||
|
||||
length curds const col $39;
|
||||
curds="&curds";
|
||||
const=constraint_name;
|
||||
col=column_name;
|
||||
run;
|
||||
|
||||
proc append base=&pkds data=&syslast;run;
|
||||
|
||||
/* Create Unique Indexes, but only if they were not already defined within the Constraints section. */
|
||||
data _data_(keep=curds const col);
|
||||
set &idxinfo (where=(
|
||||
libname="%scan(&curds,1,.)"
|
||||
and upcase(memname)="%scan(&curds,2,.)"
|
||||
and unique='yes'
|
||||
and upcase(indxname) not in (%mf_getquotedstr(&constraints_used))
|
||||
));
|
||||
file &outref mod;
|
||||
by idxusage indxname;
|
||||
name=upcase(name);
|
||||
if &constcheck=1 then stop; /* in fact we only care about PKs so stop if we have */
|
||||
if _n_=1 and &constcheck=0 then put / ' indexes {';
|
||||
|
||||
length cols $5000;
|
||||
retain cols;
|
||||
if first.indxname then cols = cats('(',name);
|
||||
else cols=cats(cols,',',name);
|
||||
|
||||
if last.indxname then do;
|
||||
cols=cats(cols,')[unique]')!!' //'!!indxname;
|
||||
put ' ' cols;
|
||||
call symputx('constcheck',1);
|
||||
end;
|
||||
|
||||
length curds const col $39;
|
||||
curds="&curds";
|
||||
const=indxname;
|
||||
col=name;
|
||||
run;
|
||||
proc append base=&pkds data=&syslast;run;
|
||||
|
||||
data _null_;
|
||||
file &outref mod;
|
||||
if &constcheck =1 then put ' }';
|
||||
put '}';
|
||||
run;
|
||||
|
||||
%end;
|
||||
|
||||
/**
|
||||
* now we need to figure out the relationships
|
||||
*/
|
||||
|
||||
/* sort alphabetically so we can have one set of unique cols per table */
|
||||
proc sort data=&pkds nodupkey;
|
||||
by curds const col;
|
||||
run;
|
||||
|
||||
data &pkds.1 (keep=curds col)
|
||||
&pkds.2 (keep=curds cols);
|
||||
set &pkds;
|
||||
by curds const;
|
||||
length retconst $39 cols $5000;
|
||||
retain retconst cols;
|
||||
if first.curds then do;
|
||||
retconst=const;
|
||||
cols=upcase(col);
|
||||
end;
|
||||
else cols=catx(' ',cols,upcase(col));
|
||||
if retconst=const then do;
|
||||
output &pkds.1;
|
||||
if last.const then output &pkds.2;
|
||||
end;
|
||||
run;
|
||||
|
||||
%let curdslist="0";
|
||||
%do x=1 %to %sysfunc(countw(&dsnlist,%str( )));
|
||||
%let curds=%scan(&dsnlist,&x,%str( ));
|
||||
|
||||
%let pkcols=0;
|
||||
data _null_;
|
||||
set &pkds.2(where=(curds="&curds"));
|
||||
call symputx('pkcols',cols);
|
||||
run;
|
||||
%if &pkcols ne 0 %then %do;
|
||||
%let curdslist=&curdslist,"&curds";
|
||||
|
||||
/* start with one2one */
|
||||
data &pkds.4;
|
||||
file &outref mod;
|
||||
set &pkds.2(where=(cols="&pkcols" and curds not in (&curdslist)));
|
||||
line='Ref: "'!!"&curds"
|
||||
!!cats('".(',"%mf_getquotedstr(&pkcols,dlm=%str(,),quote=%str( ))",')')
|
||||
!!' - '
|
||||
!!cats(quote(trim(curds)),'.(',"%mf_getquotedstr(&pkcols,dlm=%str(,),quote=%str( ))",')');
|
||||
put line;
|
||||
run;
|
||||
|
||||
/* now many2one */
|
||||
/* get table with one row per col */
|
||||
data &pkds.5;
|
||||
set &pkds.1(where=(curds="&curds"));
|
||||
run;
|
||||
/* get tables which contain the PK columns */
|
||||
proc sql;
|
||||
create table &pkds.5a as
|
||||
select upcase(cats(b.libname,'.',b.memname)) as curds
|
||||
,b.name
|
||||
from &pkds.5 a
|
||||
inner join &colinfo b
|
||||
on a.col=upcase(b.name);
|
||||
/* count to make sure those tables contain ALL the columns */
|
||||
create table &pkds.5b as
|
||||
select curds,count(*) as cnt
|
||||
from &pkds.5a
|
||||
where curds not in (select curds from &pkds.2 where cols="&pkcols") /* not a one to one match */
|
||||
and curds ne "&curds" /* exclude self */
|
||||
group by 1;
|
||||
create table &pkds.6 as
|
||||
select a.*
|
||||
,b.cols
|
||||
from &pkds.5b a
|
||||
left join &pkds.4 b
|
||||
on a.curds=b.curds;
|
||||
|
||||
data _null_;
|
||||
set &pkds.6;
|
||||
file &outref mod;
|
||||
colcnt=%sysfunc(countw(&pkcols));
|
||||
if cnt=colcnt then do;
|
||||
/* table contains all the PK cols, and was not a direct / 121 match */
|
||||
line='Ref: "'!!"&curds"
|
||||
!!'".('
|
||||
!!"%mf_getquotedstr(&pkcols,dlm=%str(,),quote=%str( ))"
|
||||
!!') > '
|
||||
!!cats(quote(trim(curds))
|
||||
,'.('
|
||||
,"%mf_getquotedstr(&pkcols,dlm=%str(,),quote=%str( ))"
|
||||
,')'
|
||||
);
|
||||
put line;
|
||||
end;
|
||||
run;
|
||||
%end;
|
||||
%end;
|
||||
|
||||
|
||||
%if %upcase(&showlog)=YES %then %do;
|
||||
options ps=max;
|
||||
data _null_;
|
||||
infile &outref;
|
||||
input;
|
||||
putlog _infile_;
|
||||
run;
|
||||
%end;
|
||||
|
||||
%mend;
|
||||
@@ -30,7 +30,6 @@
|
||||
datetime2 format or regular decimal type
|
||||
@version 9.3
|
||||
@author Allan Bowe
|
||||
@source https://github.com/sasjs/core
|
||||
**/
|
||||
|
||||
%macro mp_getddl(libref,ds,fref=getddl,flavour=SAS,showlog=NO,schema=
|
||||
@@ -132,7 +131,7 @@ run;
|
||||
%let curds=%scan(&dsnlist,&x);
|
||||
data _null_;
|
||||
file &fref mod;
|
||||
length nm lab $1024;
|
||||
length nm lab $1024 typ $20;
|
||||
set &colinfo (where=(upcase(memname)="&curds")) end=last;
|
||||
|
||||
if _n_=1 then do;
|
||||
@@ -146,10 +145,12 @@ run;
|
||||
end;
|
||||
else put " ,"@@;
|
||||
if length(format)>1 then fmt=" format="!!cats(format);
|
||||
len=" length="!!cats(length);
|
||||
lab=" label="!!quote(trim(label));
|
||||
if length(label)>1 then lab=" label="!!quote(trim(label));
|
||||
if notnull='yes' then notnul=' not null';
|
||||
put name type len fmt notnul lab;
|
||||
if type='char' then typ=cats('char(',length,')');
|
||||
else if length ne 8 then typ='num length='!!left(length);
|
||||
else typ='num';
|
||||
put name typ fmt notnul lab;
|
||||
run;
|
||||
|
||||
/* Extra step for data constraints */
|
||||
@@ -159,7 +160,7 @@ run;
|
||||
file &fref mod;
|
||||
put ');';
|
||||
run;
|
||||
|
||||
|
||||
/* Create Unique Indexes, but only if they were not already defined within the Constraints section. */
|
||||
data _null_;
|
||||
*length ds $128;
|
||||
@@ -339,7 +340,7 @@ run;
|
||||
|
||||
%end;
|
||||
%end;
|
||||
%if &showlog=YES %then %do;
|
||||
%if %upcase(&showlog)=YES %then %do;
|
||||
options ps=max;
|
||||
data _null_;
|
||||
infile &fref;
|
||||
|
||||
@@ -3,9 +3,9 @@
|
||||
@brief Guess the primary key of a table
|
||||
@details Tries to guess the primary key of a table based on the following logic:
|
||||
|
||||
* Columns with nulls are ignored
|
||||
* Return only column combinations that provide unique results
|
||||
* Start from one column, then move out to include composite keys of 2 to 6 columns
|
||||
* Columns with nulls are ignored
|
||||
* Return only column combinations that provide unique results
|
||||
* Start from one column, then move out to include composite keys of 2 to 6 columns
|
||||
|
||||
The library of the target should be assigned before using this macro.
|
||||
|
||||
@@ -51,7 +51,7 @@
|
||||
/* get null count and row count */
|
||||
%let tmpvar=%mf_getuniquename();
|
||||
proc sql noprint;
|
||||
create table _data_ as select
|
||||
create table _data_ as select
|
||||
count(*) as &tmpvar
|
||||
%do i=1 %to &vcnt;
|
||||
%let var=%scan(&vars,&i);
|
||||
@@ -85,10 +85,10 @@
|
||||
%put &sysmacroname: &baseds has no combination of unique records! Exiting.;
|
||||
%return;
|
||||
%end;
|
||||
|
||||
|
||||
/* now check cardinality */
|
||||
proc sql noprint;
|
||||
create table _data_ as select
|
||||
create table _data_ as select
|
||||
%do i=1 %to &ppkcnt;
|
||||
%let var=%scan(&posspks,&i);
|
||||
count(distinct &var) as &var
|
||||
@@ -212,7 +212,7 @@
|
||||
%end;
|
||||
%end;
|
||||
%end;
|
||||
|
||||
|
||||
%if &ppkcnt=4 %then %do;
|
||||
%put &sysmacroname: No more PK guess possible;
|
||||
%return;
|
||||
@@ -228,7 +228,7 @@
|
||||
%let lev3=%scan(&posspks,&k);
|
||||
%if &lev1 ne &lev3 and &lev2 ne &lev3 %then %do l=4 %to &ppkcnt;
|
||||
%let lev4=%scan(&posspks,&l);
|
||||
%if &lev1 ne &lev4 and &lev2 ne &lev4 and &lev3 ne &lev4 %then
|
||||
%if &lev1 ne &lev4 and &lev2 ne &lev4 and &lev3 ne &lev4 %then
|
||||
%do m=5 %to &ppkcnt;
|
||||
%let lev5=%scan(&posspks,&m);
|
||||
%if &lev1 ne &lev5 & &lev2 ne &lev5 & &lev3 ne &lev5 & &lev4 ne &lev5 %then %do;
|
||||
@@ -250,7 +250,7 @@
|
||||
%end;
|
||||
%end;
|
||||
%end;
|
||||
|
||||
|
||||
%if &ppkcnt=5 %then %do;
|
||||
%put &sysmacroname: No more PK guess possible;
|
||||
%return;
|
||||
@@ -266,17 +266,17 @@
|
||||
%let lev3=%scan(&posspks,&k);
|
||||
%if &lev1 ne &lev3 and &lev2 ne &lev3 %then %do l=4 %to &ppkcnt;
|
||||
%let lev4=%scan(&posspks,&l);
|
||||
%if &lev1 ne &lev4 and &lev2 ne &lev4 and &lev3 ne &lev4 %then
|
||||
%if &lev1 ne &lev4 and &lev2 ne &lev4 and &lev3 ne &lev4 %then
|
||||
%do m=5 %to &ppkcnt;
|
||||
%let lev5=%scan(&posspks,&m);
|
||||
%if &lev1 ne &lev5 & &lev2 ne &lev5 & &lev3 ne &lev5 & &lev4 ne &lev5 %then
|
||||
%if &lev1 ne &lev5 & &lev2 ne &lev5 & &lev3 ne &lev5 & &lev4 ne &lev5 %then
|
||||
%do n=6 %to &ppkcnt;
|
||||
%let lev6=%scan(&posspks,&n);
|
||||
%if &lev1 ne &lev6 & &lev2 ne &lev6 & &lev3 ne &lev6
|
||||
& &lev4 ne &lev6 & &lev5 ne &lev6 %then
|
||||
%if &lev1 ne &lev6 & &lev2 ne &lev6 & &lev3 ne &lev6
|
||||
& &lev4 ne &lev6 & &lev5 ne &lev6 %then
|
||||
%do;
|
||||
/* check for four level uniqueness */
|
||||
proc sort data=&pkds(keep=&lev1 &lev2 &lev3 &lev4 &lev5 &lev6)
|
||||
proc sort data=&pkds(keep=&lev1 &lev2 &lev3 &lev4 &lev5 &lev6)
|
||||
out=&tmpds noduprec;
|
||||
by _all_;
|
||||
run;
|
||||
@@ -295,7 +295,7 @@
|
||||
%end;
|
||||
%end;
|
||||
%end;
|
||||
|
||||
|
||||
%if &ppkcnt=6 %then %do;
|
||||
%put &sysmacroname: No more PK guess possible;
|
||||
%return;
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
@brief Searches all data in a library
|
||||
@details
|
||||
Scans an entire library and creates a copy of any table
|
||||
containing a specific string or numeric value. Only
|
||||
containing a specific string OR numeric value. Only
|
||||
matching records are written out.
|
||||
If both a string and numval are provided, the string
|
||||
will take precedence.
|
||||
@@ -44,9 +44,13 @@
|
||||
,filter_text=%str(1=1)
|
||||
)/*/STORE SOURCE*/;
|
||||
|
||||
%local table_list table table_num table colnum col start_tm vars type coltype;
|
||||
%local table_list table table_num table colnum col start_tm check_tm vars type coltype;
|
||||
%put process began at %sysfunc(datetime(),datetime19.);
|
||||
|
||||
%if &syscc ge 4 %then %do;
|
||||
%put %str(WAR)NING: SYSCC=&syscc on macro entry;
|
||||
%return;
|
||||
%end;
|
||||
|
||||
%if &string = %then %let type=N;
|
||||
%else %let type=C;
|
||||
@@ -78,6 +82,7 @@ proc sql
|
||||
%put NO COLUMNS IN &lib..&table! This will be skipped.;
|
||||
%end;
|
||||
%else %do;
|
||||
%let check_tm=%sysfunc(datetime());
|
||||
/* build sql statement */
|
||||
create table mpsearch.&table as select * from &lib..&table
|
||||
where %unquote(&filter_text) and
|
||||
@@ -88,14 +93,19 @@ proc sql
|
||||
%let coltype=%mf_getvartype(&lib..&table,&col);
|
||||
%if &type=C and &coltype=C %then %do;
|
||||
/* if a char column, see if it contains the string */
|
||||
or (&col ? "&string")
|
||||
or ("&col"n ? "&string")
|
||||
%end;
|
||||
%else %if &type=N and &coltype=N %then %do;
|
||||
/* if numeric match exactly */
|
||||
or (&col = &numval)
|
||||
or ("&col"n = &numval)
|
||||
%end;
|
||||
%end;
|
||||
);
|
||||
%put Search query for &table took %sysevalf(%sysfunc(datetime())-&check_tm) seconds;
|
||||
%if &sqlrc ne 0 %then %do;
|
||||
%put %str(WAR)NING: SQLRC=&sqlrc when processing &table;
|
||||
%return;
|
||||
%end;
|
||||
%if %mf_nobs(mpsearch.&table)=0 %then %do;
|
||||
drop table mpsearch.&table;
|
||||
%end;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
@file mp_streamfile.sas
|
||||
@file
|
||||
@brief Streams a file to _webout according to content type
|
||||
@details Will set headers using appropriate functions (SAS 9 vs Viya) and send
|
||||
content as a binary stream.
|
||||
@@ -17,6 +17,7 @@
|
||||
|
||||
@param contenttype= Either TEXT, ZIP, CSV, EXCEL (default TEXT)
|
||||
@param inloc= /path/to/file.ext to be sent
|
||||
@param inref= fileref of file to be sent (if provided, overrides `inloc`)
|
||||
@param outname= the name of the file, as downloaded by the browser
|
||||
|
||||
@author Allan Bowe
|
||||
@@ -27,6 +28,7 @@
|
||||
%macro mp_streamfile(
|
||||
contenttype=TEXT
|
||||
,inloc=
|
||||
,inref=0
|
||||
,outname=
|
||||
)/*/STORE SOURCE*/;
|
||||
|
||||
@@ -42,7 +44,7 @@
|
||||
%end;
|
||||
%else %if &platform=SASVIYA %then %do;
|
||||
filename _webout filesrvc parenturi="&SYS_JES_JOB_URI" name='_webout.zip'
|
||||
contenttype='application/zip'
|
||||
contenttype='application/zip'
|
||||
contentdisp="attachment; filename=&outname";
|
||||
%end;
|
||||
%end;
|
||||
@@ -56,7 +58,7 @@
|
||||
%end;
|
||||
%else %if &platform=SASVIYA %then %do;
|
||||
filename _webout filesrvc parenturi="&SYS_JES_JOB_URI" name='_webout.xls'
|
||||
contenttype='application/vnd.ms-excel'
|
||||
contenttype='application/vnd.ms-excel'
|
||||
contentdisp="attachment; filename=&outname";
|
||||
%end;
|
||||
%end;
|
||||
@@ -69,7 +71,7 @@
|
||||
%end;
|
||||
%else %if &platform=SASVIYA %then %do;
|
||||
filename _webout filesrvc parenturi="&SYS_JES_JOB_URI" name='_webout.xls'
|
||||
contenttype='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
|
||||
contenttype='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
|
||||
contentdisp="attachment; filename=&outname";
|
||||
%end;
|
||||
%end;
|
||||
@@ -102,7 +104,7 @@
|
||||
%else %if &contentype=HTML %then %do;
|
||||
%if &platform=SASVIYA %then %do;
|
||||
filename _webout filesrvc parenturi="&SYS_JES_JOB_URI" name="_webout.json"
|
||||
contenttype="text/html";
|
||||
contenttype="text/html";
|
||||
%end;
|
||||
%end;
|
||||
%else %do;
|
||||
@@ -110,6 +112,11 @@
|
||||
%return;
|
||||
%end;
|
||||
|
||||
%mp_binarycopy(inloc="&inloc",outref=_webout)
|
||||
%if &inref ne 0 %then %do;
|
||||
%mp_binarycopy(inref=&inref,outref=_webout)
|
||||
%end;
|
||||
%else %do;
|
||||
%mp_binarycopy(inloc="&inloc",outref=_webout)
|
||||
%end;
|
||||
|
||||
%mend;
|
||||
68
base/mp_tree.sas
Normal file
68
base/mp_tree.sas
Normal file
@@ -0,0 +1,68 @@
|
||||
/**
|
||||
@file
|
||||
@brief Recursively scans a directory tree to get all subfolders and content
|
||||
@details
|
||||
Usage:
|
||||
|
||||
%mp_tree(dir=/tmp, outds=work.tree)
|
||||
|
||||
Credits:
|
||||
|
||||
* Roger Deangelis, https://communities.sas.com/t5/SAS-Programming/listing-all-files-within-a-directory-and-subdirectories/m-p/332616/highlight/true#M74887
|
||||
* Tom, https://communities.sas.com/t5/SAS-Programming/listing-all-files-of-all-types-from-all-subdirectories/m-p/334113/highlight/true#M75419
|
||||
|
||||
|
||||
@param dir= Directory to be scanned (default=/tmp)
|
||||
@param outds= Dataset to create (default=work.mp_tree)
|
||||
|
||||
@returns outds contains the following variables:
|
||||
|
||||
- `dir`: a flag (1/0) to say whether it is a directory or not. This is not
|
||||
reliable - folders that you do not have permission to open will be flagged
|
||||
as directories.
|
||||
- `ext`: file extension
|
||||
- `filename`: file name
|
||||
- `dirname`: directory name
|
||||
- `fullpath`: directory + file name
|
||||
|
||||
@version 9.2
|
||||
**/
|
||||
|
||||
%macro mp_tree(dir=/tmp
|
||||
,outds=work.mp_tree
|
||||
)/*/STORE SOURCE*/;
|
||||
|
||||
data &outds ;
|
||||
length dir 8 ext filename dirname $256 fullpath $512 ;
|
||||
call missing(of _all_);
|
||||
fullpath = "&dir";
|
||||
run;
|
||||
|
||||
%local sep;
|
||||
%if &sysscp=WIN or &SYSSCP eq DNTHOST %then %let sep=\;
|
||||
%else %let sep=/;
|
||||
|
||||
data &outds ;
|
||||
modify &outds ;
|
||||
retain sep "&sep";
|
||||
rc=filename('tmp',fullpath);
|
||||
dir_id=dopen('tmp');
|
||||
dir = (dir_id ne 0) ;
|
||||
if dir then dirname=fullpath;
|
||||
else do;
|
||||
filename=scan(fullpath,-1,sep) ;
|
||||
dirname =substrn(fullpath,1,length(fullpath)-length(filename));
|
||||
if index(filename,'.')>1 then ext=scan(filename,-1,'.');
|
||||
end;
|
||||
replace;
|
||||
if dir then do;
|
||||
do i=1 to dnum(dir_id);
|
||||
fullpath=cats(dirname,sep,dread(dir_id,i));
|
||||
output;
|
||||
end;
|
||||
rc=dclose(dir_id);
|
||||
end;
|
||||
rc=filename('tmp');
|
||||
run;
|
||||
|
||||
%mend;
|
||||
@@ -24,6 +24,7 @@ LAYOUT_FILE = ./doxy/DoxygenLayout.xml
|
||||
MAX_INITIALIZER_LINES = 0
|
||||
PROJECT_NAME = Macro Core
|
||||
PROJECT_LOGO = doxy/Macro_core_website_1.png
|
||||
PROJECT_BRIEF = "Production Ready Macros for SAS Application Developers"
|
||||
RECURSIVE = YES
|
||||
REPEAT_BRIEF = NO
|
||||
SHOW_NAMESPACES = NO
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
<tab type="classmembers" visible="no" title="" intro=""/>
|
||||
</tab>
|
||||
|
||||
<tab type="filelist" visible="yes" title="" intro="List of Files Used in the Macro Core Library"/>
|
||||
<tab type="filelist" visible="yes" title="" intro="List of Files Used in the Macro-Core Library"/>
|
||||
|
||||
<tab type="examples" visible="yes" title="" intro=""/>
|
||||
</navindex>
|
||||
@@ -101,11 +101,11 @@
|
||||
<!-- Layout definition for a directory page -->
|
||||
<directory>
|
||||
<briefdescription visible="yes"/>
|
||||
<detaileddescription visible="yes" title=""/>
|
||||
<directorygraph visible="yes"/>
|
||||
<memberdecl>
|
||||
<dirs visible="yes"/>
|
||||
<files visible="yes"/>
|
||||
</memberdecl>
|
||||
<detaileddescription title=""/>
|
||||
</directory>
|
||||
</doxygenlayout>
|
||||
|
||||
@@ -33,8 +33,12 @@ cd core.github.io
|
||||
rm -r *
|
||||
mv $BUILD_FOLDER/out/doxy/* .
|
||||
echo 'core.sasjs.io' > CNAME
|
||||
git add *
|
||||
git add .
|
||||
git commit -m "build.sh build on $(date +%F:%H:%M:%S)"
|
||||
git push
|
||||
npx sitemap-generator-cli https://core.sasjs.io
|
||||
git add .
|
||||
git commit -m "adding sitemap"
|
||||
git push
|
||||
|
||||
echo "check it out: https://sasjs.github.io/core.github.io/files.html"
|
||||
|
||||
@@ -1,22 +1,23 @@
|
||||
<!-- HTML footer for doxygen 1.8.17-->
|
||||
<!-- start footer part -->
|
||||
<!--BEGIN GENERATE_TREEVIEW-->
|
||||
<li class="footer"><b>$generatedby</b>
|
||||
<a href="http://www.doxygen.org/index.html">
|
||||
<img class="footer" src="doxygen.png" alt="doxygen"/></a>
|
||||
<div id="nav-path" class="navpath"><!-- id is needed for treeview function! -->
|
||||
<ul>
|
||||
$navpath
|
||||
<li class="footer">$generatedby
|
||||
<a href="https://www.doxygen.org/index.html">
|
||||
<img class="footer" src="$relpath^doxygen.png" alt="doxygen"/></a> $doxygenversion </li>
|
||||
<i> For more information visit the </i> <a href="https://github.com/sasjs/core">Macro Core library</a>.</li>
|
||||
|
||||
</ul>
|
||||
</div>
|
||||
<!--END GENERATE_TREEVIEW-->
|
||||
<!--BEGIN !GENERATE_TREEVIEW-->
|
||||
<hr class="footer"/>
|
||||
<table width="100%"><tbody><tr><td>
|
||||
For more information visit the <a href="https://github.com/sasjs/core">Macro Core library</a>.
|
||||
</td><td><address class="footer"><small>
|
||||
©$year<br/>
|
||||
$generatedby <a href="http://www.doxygen.org/index.html">
|
||||
<!--<img class="footer" src="$relpath$doxygen.png" alt="doxygen"/>-->doxygen
|
||||
</a> $doxygenversion
|
||||
</small></address></tr></tbody></table>
|
||||
<hr class="footer"/><address class="footer"><small>
|
||||
$generatedby  <a href="http://www.doxygen.org/index.html">
|
||||
<img class="footer" src="$relpath^doxygen.png" alt="doxygen"/>
|
||||
</a> $doxygenversion
|
||||
</small></address>
|
||||
<!--END !GENERATE_TREEVIEW-->
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<!-- HTML header for doxygen 1.8.14-->
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
<!-- HTML header for doxygen 1.8.17-->
|
||||
<html xmlns="https://www.w3.org/1999/xhtml" lang="en">
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8"/>
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=9"/>
|
||||
@@ -16,51 +16,51 @@ $search
|
||||
$mathjax
|
||||
<link href="$relpath^$stylesheet" rel="stylesheet" type="text/css" />
|
||||
<link REL="icon" HREF="https://sasjs.io/img/runningman.jpg">
|
||||
|
||||
$extrastylesheet
|
||||
</head>
|
||||
<body>
|
||||
<div id="top"><!-- do not remove this div, it is closed by doxygen! -->
|
||||
|
||||
<!--BEGIN TITLEAREA-->
|
||||
<div id="titlearea" style='background-color:white' >
|
||||
<div id="titlearea">
|
||||
<table cellspacing="0" cellpadding="0">
|
||||
<tbody>
|
||||
<tr style="height: 26px;">
|
||||
<tr style="height: 56px;">
|
||||
<!--BEGIN PROJECT_LOGO-->
|
||||
<td id="projectlogo"></td>
|
||||
|
||||
<td id="projectlogo">
|
||||
<img alt="Logo" src="$relpath^$projectlogo"/></td>
|
||||
<!--END PROJECT_LOGO-->
|
||||
<!--BEGIN PROJECT_NAME-->
|
||||
<td>
|
||||
<td id="projectalign" style="padding-left: 0.5em;">
|
||||
<div id="projectname">
|
||||
<!--a href=".">
|
||||
<img alt="Macro Core" src="https://macropeople.com/wp-content/uploads/2018/05/macropeople2014retina_V2.png" height=60/>
|
||||
</a-->
|
||||
<a href=".">
|
||||
<img alt="Macro Core" src="./Macro_core_website_1.png" height=60/>
|
||||
</a>
|
||||
<!--BEGIN PROJECT_NUMBER--> <span id="projectnumber">$projectnumber</span><!--END PROJECT_NUMBER-->
|
||||
</div>
|
||||
<!--BEGIN PROJECT_BRIEF--><div id="projectbrief">
|
||||
Production Ready Macros for SAS Application Developers</br>
|
||||
<a href="https://github.com/sasjs/core">
|
||||
https://github.com/sasjs/core
|
||||
</a>
|
||||
|
||||
</div>
|
||||
<meta name="Description" content="$projectbrief">
|
||||
<!--END PROJECT_BRIEF-->
|
||||
</td>
|
||||
<td>
|
||||
<!--END PROJECT_NAME-->
|
||||
<!--BEGIN !PROJECT_NAME-->
|
||||
<!--BEGIN PROJECT_BRIEF-->
|
||||
<td style="padding-left: 0.5em;">
|
||||
<div id="projectbrief">$projectbrief</div>
|
||||
<table style="padding-left: 2em;" cellspacing="0" cellpadding="0">
|
||||
<tr><td> Production Ready Macros for SAS Application Developers</td></tr>
|
||||
<tr><td><a href="https://github.com/sasjs/core">
|
||||
https://github.com/sasjs/core
|
||||
</a></td></tr>
|
||||
</table>
|
||||
</td>
|
||||
<!--END PROJECT_NAME-->
|
||||
<!--BEGIN !PROJECT_NAME-->
|
||||
<!--BEGIN PROJECT_BRIEF-->
|
||||
</td>
|
||||
<!--END PROJECT_BRIEF-->
|
||||
<!--END !PROJECT_NAME-->
|
||||
<div class="header">
|
||||
<div class="headertitle">
|
||||
|
||||
<!--BEGIN DISABLE_INDEX-->
|
||||
<!--BEGIN SEARCHENGINE-->
|
||||
|
||||
<td>$searchbox</td>
|
||||
<!--END SEARCHENGINE-->
|
||||
<!--END DISABLE_INDEX-->
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
5
main.dox
5
main.dox
@@ -13,6 +13,11 @@
|
||||
* Not metadata aware
|
||||
* No X command
|
||||
* Prefixes: _mf_, _mp_
|
||||
|
||||
Macros starting `mf_` are macro _functions_ and can be used in assignment
|
||||
statements. Those starting `mp_` are macro _procedures_, which generate
|
||||
SAS statements, and must therefore be applied accordingly.
|
||||
|
||||
*/
|
||||
|
||||
/*! \dir meta
|
||||
|
||||
@@ -9,15 +9,15 @@
|
||||
|
||||
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)
|
||||
%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
|
||||
|
||||
@@ -124,6 +124,7 @@ data _null_;
|
||||
when ('
') rec='0A'x;
|
||||
when ('
') rec='0D'x;
|
||||
when ('$' ) rec='$' ;
|
||||
when ('	') rec='09'x;
|
||||
otherwise putlog "WARNING: missing value for " entity=;
|
||||
end;
|
||||
rc =fput(fileid, substr(rec,1,1));
|
||||
|
||||
@@ -123,6 +123,7 @@ data _null_;
|
||||
when ('
') rec='0A'x;
|
||||
when ('
') rec='0D'x;
|
||||
when ('$' ) rec='$' ;
|
||||
when ('	') rec='09'x;
|
||||
otherwise putlog "%str(WARN)ING: missing value for " entity=;
|
||||
end;
|
||||
rc =fput(fileid, substr(rec,1,1));
|
||||
|
||||
@@ -23,8 +23,8 @@
|
||||
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
|
||||
@param showDesc= provide a non blank value to return stored process descriptions
|
||||
@param 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
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/**
|
||||
@file
|
||||
@brief Retrieves properties of the SAS web app server
|
||||
@details
|
||||
@details
|
||||
Usage:
|
||||
|
||||
%mm_getwebappsrvprops(outds= some_ds)
|
||||
@@ -21,8 +21,7 @@
|
||||
libname __shake clear;
|
||||
|
||||
@version 9.4
|
||||
@author Allan Bowe
|
||||
@source https://github.com/sasjs/core
|
||||
@author Allan Bowe https://github.com/sasjs/core
|
||||
|
||||
**/
|
||||
|
||||
@@ -99,6 +98,7 @@ run;
|
||||
when ('
') rec='0A'x;
|
||||
when ('
') rec='0D'x;
|
||||
when ('$' ) rec='$' ;
|
||||
when ('	') rec='09'x;
|
||||
otherwise putlog "WARNING: missing value for " entity=;
|
||||
end;
|
||||
rc =fput(fileid, substr(rec,1,1));
|
||||
|
||||
@@ -15,8 +15,7 @@
|
||||
|
||||
|
||||
@version VIYA V.03.04
|
||||
@author Allan Bowe
|
||||
@source https://github.com/sasjs/core
|
||||
@author Allan Bowe, source: https://github.com/sasjs/core
|
||||
|
||||
<h4> Dependencies </h4>
|
||||
@li mp_abort.sas
|
||||
@@ -42,7 +41,7 @@
|
||||
%end;
|
||||
|
||||
%put &sysmacroname: grant_type=&grant_type;
|
||||
%mp_abort(iftrue=(&grant_type ne authorization_code and &grant_type ne password
|
||||
%mp_abort(iftrue=(&grant_type ne authorization_code and &grant_type ne password
|
||||
and &grant_type ne sas_services
|
||||
)
|
||||
,mac=&sysmacroname
|
||||
@@ -121,7 +120,7 @@ options noquotelenmax;
|
||||
out=&fname2
|
||||
&oauth_bearer
|
||||
url=%unquote(%superq(href));
|
||||
headers
|
||||
headers
|
||||
%if &grant_type=authorization_code %then %do;
|
||||
"Authorization"="Bearer &&&access_token_var"
|
||||
%end;
|
||||
|
||||
@@ -1,27 +1,28 @@
|
||||
/**
|
||||
@file mv_createwebservice.sas
|
||||
@brief Creates a JobExecution web service if it doesn't already exist
|
||||
@details Code is passed in as one or more filerefs.
|
||||
@details
|
||||
Code is passed in as one or more filerefs.
|
||||
|
||||
%* Step 1 - compile macros ;
|
||||
filename mc url "https://raw.githubusercontent.com/sasjs/core/main/all.sas";
|
||||
%inc mc;
|
||||
%* Step 1 - compile macros ;
|
||||
filename mc url "https://raw.githubusercontent.com/sasjs/core/main/all.sas";
|
||||
%inc mc;
|
||||
|
||||
%* Step 2 - Create some code and add it to a web service;
|
||||
filename ft15f001 temp;
|
||||
parmcards4;
|
||||
%webout(FETCH) %* fetch any tables sent from frontend;
|
||||
%* 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)
|
||||
;;;;
|
||||
%mv_createwebservice(path=/Public/app/common,name=appinit)
|
||||
%* Step 2 - Create some code and add it to a web service;
|
||||
filename ft15f001 temp;
|
||||
parmcards4;
|
||||
%webout(FETCH) %* fetch any tables sent from frontend;
|
||||
%* 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)
|
||||
;;;;
|
||||
%mv_createwebservice(path=/Public/app/common,name=appinit)
|
||||
|
||||
|
||||
Notes:
|
||||
@@ -54,8 +55,7 @@
|
||||
a shared context - see https://go.documentation.sas.com/?docsetId=calcontexts&docsetTarget=n1hjn8eobk5pyhn1wg3ja0drdl6h.htm&docsetVersion=3.5&locale=en
|
||||
|
||||
@version VIYA V.03.04
|
||||
@author Allan Bowe
|
||||
@source https://github.com/sasjs/core
|
||||
@author Allan Bowe, source: https://github.com/sasjs/core
|
||||
|
||||
**/
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/**
|
||||
@file mv_deletefoldermember.sas
|
||||
@brief Deletes an item in a Viya folder
|
||||
@details If not executed in Studio 5+ will expect oauth token in a global
|
||||
@details If not executed in Studio 5+ will expect oauth token in a global
|
||||
macro variable (default ACCESS_TOKEN).
|
||||
|
||||
filename mc url "https://raw.githubusercontent.com/sasjs/core/main/all.sas";
|
||||
@@ -20,8 +20,7 @@
|
||||
|
||||
|
||||
@version VIYA V.03.04
|
||||
@author Allan Bowe
|
||||
@source https://github.com/sasjs/core
|
||||
@author Allan Bowe, source: https://github.com/sasjs/core
|
||||
|
||||
<h4> Dependencies </h4>
|
||||
@li mp_abort.sas
|
||||
@@ -48,7 +47,7 @@
|
||||
%let &access_token_var=;
|
||||
%end;
|
||||
%put &sysmacroname: grant_type=&grant_type;
|
||||
%mp_abort(iftrue=(&grant_type ne authorization_code and &grant_type ne password
|
||||
%mp_abort(iftrue=(&grant_type ne authorization_code and &grant_type ne password
|
||||
and &grant_type ne sas_services
|
||||
)
|
||||
,mac=&sysmacroname
|
||||
@@ -98,7 +97,7 @@ run;
|
||||
libname &libref1 JSON fileref=&fname1;
|
||||
data _null_;
|
||||
set &libref1..links;
|
||||
if rel='members' then call symputx('mref',quote(trim(href)),'l');
|
||||
if rel='members' then call symputx('mref',quote("&base_uri"!!trim(href)),'l');
|
||||
run;
|
||||
|
||||
/* get the children */
|
||||
@@ -128,8 +127,8 @@ run;
|
||||
%put NOTE:;%put NOTE- &sysmacroname: &path/&name NOT FOUND;%put NOTE- ;
|
||||
%return;
|
||||
%end;
|
||||
proc http method="DELETE" url="&uri" &oauth_bearer;
|
||||
headers
|
||||
proc http method="DELETE" url="&base_uri&uri" &oauth_bearer;
|
||||
headers
|
||||
%if &grant_type=authorization_code %then %do;
|
||||
"Authorization"="Bearer &&&access_token_var"
|
||||
%end;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/**
|
||||
@file mv_deletejes.sas
|
||||
@brief Creates a job execution service if it does not already exist
|
||||
@details If not executed in Studio 5+ will expect oauth token in a global
|
||||
@details If not executed in Studio 5+ will expect oauth token in a global
|
||||
macro variable (default ACCESS_TOKEN).
|
||||
|
||||
filename mc url "https://raw.githubusercontent.com/sasjs/core/main/all.sas";
|
||||
@@ -19,8 +19,7 @@
|
||||
|
||||
|
||||
@version VIYA V.03.04
|
||||
@author Allan Bowe
|
||||
@source https://github.com/sasjs/core
|
||||
@author Allan Bowe, source: https://github.com/sasjs/core
|
||||
|
||||
<h4> Dependencies </h4>
|
||||
@li mp_abort.sas
|
||||
@@ -46,7 +45,7 @@
|
||||
%let &access_token_var=;
|
||||
%end;
|
||||
%put &sysmacroname: grant_type=&grant_type;
|
||||
%mp_abort(iftrue=(&grant_type ne authorization_code and &grant_type ne password
|
||||
%mp_abort(iftrue=(&grant_type ne authorization_code and &grant_type ne password
|
||||
and &grant_type ne sas_services
|
||||
)
|
||||
,mac=&sysmacroname
|
||||
@@ -126,7 +125,7 @@ run;
|
||||
%return;
|
||||
%end;
|
||||
proc http method="DELETE" url="&uri" &oauth_bearer;
|
||||
headers
|
||||
headers
|
||||
%if &grant_type=authorization_code %then %do;
|
||||
"Authorization"="Bearer &&&access_token_var"
|
||||
%end;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/**
|
||||
@file mv_deleteviyafolder.sas
|
||||
@brief Creates a viya folder if that folder does not already exist
|
||||
@details If not running in Studo 5 +, will expect an oauth token in a global
|
||||
@details If not running in Studo 5 +, will expect an oauth token in a global
|
||||
macro variable (default ACCESS_TOKEN).
|
||||
|
||||
options mprint;
|
||||
@@ -16,8 +16,7 @@
|
||||
|
||||
|
||||
@version VIYA V.03.04
|
||||
@author Allan Bowe
|
||||
@source https://github.com/sasjs/core
|
||||
@author Allan Bowe, source: https://github.com/sasjs/core
|
||||
|
||||
<h4> Dependencies </h4>
|
||||
@li mp_abort.sas
|
||||
@@ -42,7 +41,7 @@
|
||||
%let &access_token_var=;
|
||||
%end;
|
||||
%put &sysmacroname: grant_type=&grant_type;
|
||||
%mp_abort(iftrue=(&grant_type ne authorization_code and &grant_type ne password
|
||||
%mp_abort(iftrue=(&grant_type ne authorization_code and &grant_type ne password
|
||||
and &grant_type ne sas_services
|
||||
)
|
||||
,mac=&sysmacroname
|
||||
@@ -88,9 +87,9 @@ libname &libref1 JSON fileref=&fname1;
|
||||
data _null_;
|
||||
set &libref1..links;
|
||||
if rel='deleteRecursively' then
|
||||
call symputx('href',quote(trim(href)),'l');
|
||||
call symputx('href',quote("&base_uri"!!trim(href)),'l');
|
||||
else if rel='members' then
|
||||
call symputx('mref',quote(cats(href,'?recursive=true')),'l');
|
||||
call symputx('mref',quote(cats("&base_uri",href,'?recursive=true')),'l');
|
||||
run;
|
||||
|
||||
/* before we can delete the folder, we need to delete the children */
|
||||
@@ -111,7 +110,7 @@ data _null_;
|
||||
set &libref1a..items_links;
|
||||
if href=:'/folders/folders' then return;
|
||||
if rel='deleteResource' then
|
||||
call execute('proc http method="DELETE" url='!!quote(trim(href))
|
||||
call execute('proc http method="DELETE" url='!!quote("&base_uri"!!trim(href))
|
||||
!!'; headers "Authorization"="Bearer &&&access_token_var" '
|
||||
!!' "Accept"="*/*";run; /**/');
|
||||
run;
|
||||
@@ -121,10 +120,10 @@ run;
|
||||
%let fname2=%mf_getuniquefileref();
|
||||
proc http method='DELETE' out=&fname2 &oauth_bearer
|
||||
url=%unquote(%superq(href));
|
||||
headers
|
||||
headers
|
||||
%if &grant_type=authorization_code %then %do;
|
||||
"Authorization"="Bearer &&&access_token_var"
|
||||
%end;
|
||||
%end;
|
||||
'Accept'='*/*'; /**/
|
||||
run;
|
||||
%if &SYS_PROCHTTP_STATUS_CODE ne 204 %then %do;
|
||||
|
||||
@@ -3,14 +3,13 @@
|
||||
@brief deprecated - replaced by mv_tokenrefresh.sas
|
||||
|
||||
@version VIYA V.03.04
|
||||
@author Allan Bowe
|
||||
@source https://github.com/sasjs/core
|
||||
|
||||
@author Allan Bowe, source: https://github.com/sasjs/core
|
||||
|
||||
<h4> Dependencies </h4>
|
||||
@li mv_tokenrefresh.sas
|
||||
|
||||
**/
|
||||
|
||||
|
||||
%macro mv_getaccesstoken(client_id=someclient
|
||||
,client_secret=somesecret
|
||||
,grant_type=authorization_code
|
||||
|
||||
@@ -3,14 +3,13 @@
|
||||
@brief deprecated - replaced by mv_registerclient.sas
|
||||
|
||||
@version VIYA V.03.04
|
||||
@author Allan Bowe
|
||||
@source https://github.com/sasjs/core
|
||||
|
||||
@author Allan Bowe, source: https://github.com/sasjs/core
|
||||
|
||||
<h4> Dependencies </h4>
|
||||
@li mv_registerclient.sas
|
||||
|
||||
**/
|
||||
|
||||
|
||||
%macro mv_getapptoken(client_id=someclient
|
||||
,client_secret=somesecret
|
||||
,grant_type=authorization_code
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
@file mv_getgroups.sas
|
||||
@brief Creates a dataset with a list of viya groups
|
||||
@file mv_getclients.sas
|
||||
@brief Get a list of Viya Clients
|
||||
@details First, be sure you have an access token (which requires an app token).
|
||||
|
||||
Using the macros here:
|
||||
@@ -20,7 +20,7 @@
|
||||
|
||||
Now we can run the macro!
|
||||
|
||||
%mv_getgroups()
|
||||
%mv_getclients()
|
||||
|
||||
@param access_token_var= The global macro variable to contain the access token
|
||||
@param grant_type= valid values are "password" or "authorization_code" (unquoted).
|
||||
@@ -29,8 +29,7 @@
|
||||
|
||||
|
||||
@version VIYA V.03.04
|
||||
@author Allan Bowe
|
||||
@source https://github.com/sasjs/core
|
||||
@author Allan Bowe, source: https://github.com/sasjs/core
|
||||
|
||||
<h4> Dependencies </h4>
|
||||
@li mp_abort.sas
|
||||
@@ -95,7 +94,7 @@ run;
|
||||
|
||||
|
||||
|
||||
/* clear refs
|
||||
/* clear refs
|
||||
filename &fname1 clear;
|
||||
libname &libref1 clear;
|
||||
*/
|
||||
|
||||
@@ -15,8 +15,7 @@
|
||||
|
||||
|
||||
@version VIYA V.03.04
|
||||
@author Allan Bowe
|
||||
@source https://github.com/sasjs/core
|
||||
@author Allan Bowe, source: https://github.com/sasjs/core
|
||||
|
||||
<h4> Dependencies </h4>
|
||||
@li mp_abort.sas
|
||||
@@ -42,7 +41,7 @@
|
||||
%let &access_token_var=;
|
||||
%end;
|
||||
%put &sysmacroname: grant_type=&grant_type;
|
||||
%mp_abort(iftrue=(&grant_type ne authorization_code and &grant_type ne password
|
||||
%mp_abort(iftrue=(&grant_type ne authorization_code and &grant_type ne password
|
||||
and &grant_type ne sas_services
|
||||
)
|
||||
,mac=&sysmacroname
|
||||
@@ -64,7 +63,7 @@ options noquotelenmax;
|
||||
%if "&root"="/" %then %do;
|
||||
/* if root just list root folders */
|
||||
proc http method='GET' out=&fname1 &oauth_bearer
|
||||
url='%sysfunc(getoption(servicesbaseurl))/folders/rootFolders';
|
||||
url="&base_uri/folders/rootFolders";
|
||||
%if &grant_type=authorization_code %then %do;
|
||||
headers "Authorization"="Bearer &&&access_token_var";
|
||||
%end;
|
||||
@@ -85,10 +84,16 @@ options noquotelenmax;
|
||||
/*data _null_;infile &fname1;input;putlog _infile_;run;*/
|
||||
libname &libref1 JSON fileref=&fname1;
|
||||
/* now get the followon link to list members */
|
||||
%local href;
|
||||
%let href=0;
|
||||
data _null_;
|
||||
set &libref1..links;
|
||||
if rel='members' then call symputx('href',quote(trim(href)),'l');
|
||||
if rel='members' then call symputx('href',quote("&base_uri"!!trim(href)),'l');
|
||||
run;
|
||||
%if &href=0 %then %do;
|
||||
%put NOTE:;%put NOTE- No members found in &root!!;%put NOTE-;
|
||||
%return;
|
||||
%end;
|
||||
%local fname2 libref2;
|
||||
%let fname2=%mf_getuniquefileref();
|
||||
%let libref2=%mf_getuniquelibref();
|
||||
|
||||
@@ -30,8 +30,7 @@
|
||||
|
||||
|
||||
@version VIYA V.03.04
|
||||
@author Allan Bowe
|
||||
@source https://github.com/sasjs/core
|
||||
@author Allan Bowe, source: https://github.com/sasjs/core
|
||||
|
||||
<h4> Dependencies </h4>
|
||||
@li mp_abort.sas
|
||||
@@ -56,7 +55,7 @@
|
||||
%let &access_token_var=;
|
||||
%end;
|
||||
%put &sysmacroname: grant_type=&grant_type;
|
||||
%mp_abort(iftrue=(&grant_type ne authorization_code and &grant_type ne password
|
||||
%mp_abort(iftrue=(&grant_type ne authorization_code and &grant_type ne password
|
||||
and &grant_type ne sas_services
|
||||
)
|
||||
,mac=&sysmacroname
|
||||
@@ -73,7 +72,7 @@ options noquotelenmax;
|
||||
%let fname1=%mf_getuniquefileref();
|
||||
proc http method='GET' out=&fname1 &oauth_bearer
|
||||
url="&base_uri/identities/groups/&group/members?limit=10000";
|
||||
headers
|
||||
headers
|
||||
%if &grant_type=authorization_code %then %do;
|
||||
"Authorization"="Bearer &&&access_token_var"
|
||||
%end;
|
||||
|
||||
@@ -29,8 +29,7 @@
|
||||
|
||||
|
||||
@version VIYA V.03.04
|
||||
@author Allan Bowe
|
||||
@source https://github.com/sasjs/core
|
||||
@author Allan Bowe, source: https://github.com/sasjs/core
|
||||
|
||||
<h4> Dependencies </h4>
|
||||
@li mp_abort.sas
|
||||
@@ -54,7 +53,7 @@
|
||||
%let &access_token_var=;
|
||||
%end;
|
||||
%put &sysmacroname: grant_type=&grant_type;
|
||||
%mp_abort(iftrue=(&grant_type ne authorization_code and &grant_type ne password
|
||||
%mp_abort(iftrue=(&grant_type ne authorization_code and &grant_type ne password
|
||||
and &grant_type ne sas_services
|
||||
)
|
||||
,mac=&sysmacroname
|
||||
@@ -72,7 +71,7 @@ options noquotelenmax;
|
||||
|
||||
proc http method='GET' out=&fname1 &oauth_bearer
|
||||
url="&base_uri/identities/groups?limit=10000";
|
||||
headers
|
||||
headers
|
||||
%if &grant_type=authorization_code %then %do;
|
||||
"Authorization"="Bearer &&&access_token_var"
|
||||
%end;
|
||||
|
||||
@@ -3,14 +3,13 @@
|
||||
@brief deprecated - replaced by mv_tokenauth.sas
|
||||
|
||||
@version VIYA V.03.04
|
||||
@author Allan Bowe
|
||||
@source https://github.com/sasjs/core
|
||||
|
||||
@author Allan Bowe, source: https://github.com/sasjs/core
|
||||
|
||||
<h4> Dependencies </h4>
|
||||
@li mv_tokenauth.sas
|
||||
|
||||
**/
|
||||
|
||||
|
||||
%macro mv_getrefreshtoken(client_id=someclient
|
||||
,client_secret=somesecret
|
||||
,grant_type=authorization_code
|
||||
|
||||
@@ -1,24 +1,15 @@
|
||||
/**
|
||||
@file mv_getusergroups.sas
|
||||
@brief Creates a dataset with a list of groups for a particular user
|
||||
@details First, be sure you have an access token (which requires an app token).
|
||||
@details If using outside of Viya SPRE, then an access token is needed.
|
||||
|
||||
Using the macros here:
|
||||
Compile the macros here:
|
||||
|
||||
filename mc url
|
||||
"https://raw.githubusercontent.com/sasjs/core/main/all.sas";
|
||||
%inc mc;
|
||||
|
||||
An administrator needs to set you up with an access code:
|
||||
|
||||
%mv_registerclient(outds=client)
|
||||
|
||||
Navigate to the url from the log (opting in to the groups) and paste the
|
||||
access code below:
|
||||
|
||||
%mv_tokenauth(inds=client,code=wKDZYTEPK6)
|
||||
|
||||
Now we can run the macro!
|
||||
Then run the macro!
|
||||
|
||||
%mv_getusergroups(&sysuserid,outds=users)
|
||||
|
||||
@@ -29,8 +20,7 @@
|
||||
|
||||
|
||||
@version VIYA V.03.04
|
||||
@author Allan Bowe
|
||||
@source https://github.com/sasjs/core
|
||||
@author Allan Bowe, source: https://github.com/sasjs/core
|
||||
|
||||
<h4> Dependencies </h4>
|
||||
@li mp_abort.sas
|
||||
@@ -55,7 +45,7 @@
|
||||
%let &access_token_var=;
|
||||
%end;
|
||||
%put &sysmacroname: grant_type=&grant_type;
|
||||
%mp_abort(iftrue=(&grant_type ne authorization_code and &grant_type ne password
|
||||
%mp_abort(iftrue=(&grant_type ne authorization_code and &grant_type ne password
|
||||
and &grant_type ne sas_services
|
||||
)
|
||||
,mac=&sysmacroname
|
||||
@@ -73,7 +63,7 @@ options noquotelenmax;
|
||||
|
||||
proc http method='GET' out=&fname1 &oauth_bearer
|
||||
url="&base_uri/identities/users/&user/memberships?limit=10000";
|
||||
headers
|
||||
headers
|
||||
%if &grant_type=authorization_code %then %do;
|
||||
"Authorization"="Bearer &&&access_token_var"
|
||||
%end;
|
||||
|
||||
@@ -34,12 +34,12 @@
|
||||
creationTimeStamp char(24),
|
||||
modifiedTimeStamp char(24),
|
||||
state char(6)
|
||||
|
||||
|
||||
@param access_token_var= The global macro variable to contain the access token
|
||||
@param grant_type= valid values:
|
||||
* password
|
||||
* authorization_code
|
||||
* detect - will check if access_token exists, if not will use sas_services if
|
||||
* detect - will check if access_token exists, if not will use sas_services if
|
||||
a SASStudioV session else authorization_code. Default option.
|
||||
* sas_services - will use oauth_bearer=sas_services
|
||||
|
||||
@@ -47,8 +47,7 @@
|
||||
|
||||
|
||||
@version VIYA V.03.04
|
||||
@author Allan Bowe
|
||||
@source https://github.com/sasjs/core
|
||||
@author Allan Bowe, source: https://github.com/sasjs/core
|
||||
|
||||
<h4> Dependencies </h4>
|
||||
@li mp_abort.sas
|
||||
@@ -72,7 +71,7 @@
|
||||
%let &access_token_var=;
|
||||
%end;
|
||||
%put &sysmacroname: grant_type=&grant_type;
|
||||
%mp_abort(iftrue=(&grant_type ne authorization_code and &grant_type ne password
|
||||
%mp_abort(iftrue=(&grant_type ne authorization_code and &grant_type ne password
|
||||
and &grant_type ne sas_services
|
||||
)
|
||||
,mac=&sysmacroname
|
||||
|
||||
168
viya/mv_jobexecute.sas
Normal file
168
viya/mv_jobexecute.sas
Normal file
@@ -0,0 +1,168 @@
|
||||
/**
|
||||
@file
|
||||
@brief Executes a SAS Viya Job
|
||||
@details Triggers a SAS Viya Job, with optional URL parameters, using
|
||||
the JES web app.
|
||||
|
||||
First, compile the macros:
|
||||
|
||||
filename mc url
|
||||
"https://raw.githubusercontent.com/sasjs/core/main/all.sas";
|
||||
%inc mc;
|
||||
|
||||
Then, execute the job!
|
||||
|
||||
%mv_jobexecute(path=/Public/folder
|
||||
,name=somejob
|
||||
)
|
||||
|
||||
Example with parameters:
|
||||
|
||||
%mv_jobexecute(path=/Public/folder
|
||||
,name=somejob
|
||||
,paramstring=%str("macvarname":"macvarvalue","answer":42)
|
||||
)
|
||||
|
||||
@param access_token_var= The global macro variable to contain the access token
|
||||
@param grant_type= valid values:
|
||||
* password
|
||||
* authorization_code
|
||||
* detect - will check if access_token exists, if not will use sas_services if
|
||||
a SASStudioV session else authorization_code. Default option.
|
||||
* sas_services - will use oauth_bearer=sas_services
|
||||
|
||||
@param path= The SAS Drive path to the job being executed
|
||||
@param name= The name of the job to execute
|
||||
@param paramstring= A JSON fragment with name:value pairs, eg: `"name":"value"`
|
||||
or "name":"value","name2":42`. This will need to be wrapped in `%str()`.
|
||||
|
||||
@param contextName= Context name with which to run the job.
|
||||
Default = `SAS Job Execution compute context`
|
||||
|
||||
@param outds= The output dataset containing links (Default=work.mv_jobexecute)
|
||||
|
||||
|
||||
@version VIYA V.03.04
|
||||
@author Allan Bowe, source: https://github.com/sasjs/core
|
||||
|
||||
<h4> Dependencies </h4>
|
||||
@li mp_abort.sas
|
||||
@li mf_getplatform.sas
|
||||
@li mf_getuniquefileref.sas
|
||||
@li mf_getuniquelibref.sas
|
||||
@li mv_getfoldermembers.sas
|
||||
|
||||
**/
|
||||
|
||||
%macro mv_jobexecute(path=0
|
||||
,name=0
|
||||
,contextName=SAS Job Execution compute context
|
||||
,access_token_var=ACCESS_TOKEN
|
||||
,grant_type=sas_services
|
||||
,paramstring=0
|
||||
,outds=work.mv_jobexecute
|
||||
);
|
||||
%local oauth_bearer;
|
||||
%if &grant_type=detect %then %do;
|
||||
%if %symexist(&access_token_var) %then %let grant_type=authorization_code;
|
||||
%else %let grant_type=sas_services;
|
||||
%end;
|
||||
%if &grant_type=sas_services %then %do;
|
||||
%let oauth_bearer=oauth_bearer=sas_services;
|
||||
%let &access_token_var=;
|
||||
%end;
|
||||
%put &sysmacroname: grant_type=&grant_type;
|
||||
%mp_abort(iftrue=(&grant_type ne authorization_code and &grant_type ne password
|
||||
and &grant_type ne sas_services
|
||||
)
|
||||
,mac=&sysmacroname
|
||||
,msg=%str(Invalid value for grant_type: &grant_type)
|
||||
)
|
||||
|
||||
%mp_abort(iftrue=("&path"="0")
|
||||
,mac=&sysmacroname
|
||||
,msg=%str(Path not provided)
|
||||
)
|
||||
%mp_abort(iftrue=("&name"="0")
|
||||
,mac=&sysmacroname
|
||||
,msg=%str(Job Name not provided)
|
||||
)
|
||||
|
||||
options noquotelenmax;
|
||||
|
||||
%local base_uri; /* location of rest apis */
|
||||
%let base_uri=%mf_getplatform(VIYARESTAPI);
|
||||
|
||||
data;run;
|
||||
%local foldermembers;
|
||||
%let foldermembers=&syslast;
|
||||
%mv_getfoldermembers(root=&path
|
||||
,access_token_var=&access_token_var
|
||||
,grant_type=&grant_type
|
||||
,outds=&foldermembers
|
||||
)
|
||||
|
||||
%local joburi;
|
||||
%let joburi=0;
|
||||
data _null_;
|
||||
set &foldermembers;
|
||||
if name="&name" and uri=:'/jobDefinitions/definitions'
|
||||
then call symputx('joburi',uri);
|
||||
run;
|
||||
|
||||
%mp_abort(iftrue=("&joburi"="0")
|
||||
,mac=&sysmacroname
|
||||
,msg=%str(Job &path/&name not found)
|
||||
)
|
||||
|
||||
/* prepare request*/
|
||||
%local fname0 fname1;
|
||||
%let fname0=%mf_getuniquefileref();
|
||||
%let fname1=%mf_getuniquefileref();
|
||||
|
||||
data _null_;
|
||||
file &fname0;
|
||||
length joburi contextname $128 paramstring $32765;
|
||||
joburi=quote(trim(symget('joburi')));
|
||||
contextname=quote(trim(symget('contextname')));
|
||||
paramstring=symget('paramstring');
|
||||
put '{"jobDefinitionUri":' joburi ;
|
||||
put ' ,"arguments":{"_contextName":' contextname;
|
||||
if paramstring ne "0" then do;
|
||||
put ' ,' paramstring;
|
||||
end;
|
||||
put '}}';
|
||||
run;
|
||||
|
||||
proc http method='POST' in=&fname0 out=&fname1 &oauth_bearer
|
||||
url="&base_uri/jobExecution/jobs";
|
||||
headers "Content-Type"="application/vnd.sas.job.execution.job.request+json"
|
||||
"Accept"="application/vnd.sas.job.execution.job+json"
|
||||
%if &grant_type=authorization_code %then %do;
|
||||
"Authorization"="Bearer &&&access_token_var"
|
||||
%end;
|
||||
;
|
||||
run;
|
||||
%if &SYS_PROCHTTP_STATUS_CODE ne 200 and &SYS_PROCHTTP_STATUS_CODE ne 201 %then
|
||||
%do;
|
||||
data _null_;infile &fname0;input;putlog _infile_;run;
|
||||
data _null_;infile &fname1;input;putlog _infile_;run;
|
||||
%mp_abort(mac=&sysmacroname
|
||||
,msg=%str(&SYS_PROCHTTP_STATUS_CODE &SYS_PROCHTTP_STATUS_PHRASE)
|
||||
)
|
||||
%end;
|
||||
|
||||
%local libref;
|
||||
%let libref=%mf_getuniquelibref();
|
||||
libname &libref JSON fileref=&fname1;
|
||||
|
||||
data &outds;
|
||||
set &libref..links;
|
||||
run;
|
||||
|
||||
/* clear refs */
|
||||
filename &fname0 clear;
|
||||
filename &fname1 clear;
|
||||
libname &libref;
|
||||
|
||||
%mend;
|
||||
@@ -35,25 +35,24 @@
|
||||
@param scopes= list of space-seperated unquoted scopes (default is openid)
|
||||
@param grant_type= valid values are "password" or "authorization_code" (unquoted)
|
||||
@param outds= the dataset to contain the registered client id and secret
|
||||
@param access_token_validity= The duration of validity of the access token
|
||||
@param access_token_validity= The duration of validity of the access token
|
||||
in seconds. A value of DEFAULT will omit the entry (and use system default)
|
||||
@param refresh_token_validity= The duration of validity of the refresh token
|
||||
in seconds. A value of DEFAULT will omit the entry (and use system default)
|
||||
@param name= A human readable name for the client
|
||||
@param required_user_groups= A list of group names. If a user does not belong
|
||||
to all the required groups, the user will not be authenticated and no tokens
|
||||
are issued to this client for that user. If this field is not specified,
|
||||
@param required_user_groups= A list of group names. If a user does not belong
|
||||
to all the required groups, the user will not be authenticated and no tokens
|
||||
are issued to this client for that user. If this field is not specified,
|
||||
authentication and token issuance proceeds normally.
|
||||
@param autoapprove= During the auth step the user can choose which scope to
|
||||
@param autoapprove= During the auth step the user can choose which scope to
|
||||
apply. Setting this to true will autoapprove all the client scopes.
|
||||
@param use_session= If true, access tokens issued to this client will be
|
||||
@param use_session= If true, access tokens issued to this client will be
|
||||
associated with an HTTP session and revoked upon logout or time-out.
|
||||
@param outjson= A dataset containing the lines of JSON submitted. Useful
|
||||
for debugging. Default= _null_.
|
||||
|
||||
|
||||
@version VIYA V.03.04
|
||||
@author Allan Bowe
|
||||
@source https://github.com/sasjs/core
|
||||
@author Allan Bowe, source: https://github.com/sasjs/core
|
||||
|
||||
<h4> Dependencies </h4>
|
||||
@li mp_abort.sas
|
||||
@@ -143,7 +142,7 @@ data _null_;
|
||||
if not missing(autoapprove) then autoapprove=cats(',"autoapprove":',autoapprove);
|
||||
use_session=trim(symget('use_session'));
|
||||
if not missing(use_session) then use_session=cats(',"use_session":',use_session);
|
||||
|
||||
|
||||
put '{' clientid ;
|
||||
put clientsecret ;
|
||||
put clientname;
|
||||
@@ -187,7 +186,7 @@ run;
|
||||
%end;
|
||||
|
||||
/* prepare url */
|
||||
%if &grant_type=authorization_code %then %do;
|
||||
%if %index(%superq(grant_type),authorization_code) %then %do;
|
||||
data _null_;
|
||||
if symexist('_baseurl') then do;
|
||||
url=symget('_baseurl');
|
||||
@@ -206,7 +205,7 @@ run;
|
||||
%put CLIENT_SECRET=&client_secret;
|
||||
%put GRANT_TYPE=&grant_type;
|
||||
%put;
|
||||
%if &grant_type=authorization_code %then %do;
|
||||
%if %index(%superq(grant_type),authorization_code) %then %do;
|
||||
/* cannot use base_uri here as it includes the protocol which may be incorrect externally */
|
||||
%put NOTE: The developer must also register below and select 'openid' to get the grant code:;
|
||||
%put NOTE- ;
|
||||
|
||||
@@ -38,10 +38,10 @@
|
||||
@param pass= If grant_type=password then provide the password here
|
||||
@param access_token_var= The global macro variable to contain the access token
|
||||
@param refresh_token_var= The global macro variable to contain the refresh token
|
||||
@param base_uri= The Viya API server location
|
||||
|
||||
@version VIYA V.03.04
|
||||
@author Allan Bowe
|
||||
@source https://github.com/sasjs/core
|
||||
@author Allan Bowe, source: https://github.com/sasjs/core
|
||||
|
||||
<h4> Dependencies </h4>
|
||||
@li mp_abort.sas
|
||||
@@ -62,6 +62,7 @@
|
||||
,pass=
|
||||
,access_token_var=ACCESS_TOKEN
|
||||
,refresh_token_var=REFRESH_TOKEN
|
||||
,base_uri=#NOTSET#
|
||||
);
|
||||
%global &access_token_var &refresh_token_var;
|
||||
|
||||
@@ -108,8 +109,7 @@ run;
|
||||
/**
|
||||
* Request access token
|
||||
*/
|
||||
%local base_uri; /* location of rest apis */
|
||||
%let base_uri=%mf_getplatform(VIYARESTAPI);
|
||||
%if &base_uri=#NOTSET# %then %let base_uri=%mf_getplatform(VIYARESTAPI);
|
||||
|
||||
%let fref2=%mf_getuniquefileref();
|
||||
proc http method='POST' in=&grantstring out=&fref2
|
||||
|
||||
@@ -40,8 +40,7 @@
|
||||
@param refresh_token_var= The global macro variable containing the refresh token
|
||||
|
||||
@version VIYA V.03.04
|
||||
@author Allan Bowe
|
||||
@source https://github.com/sasjs/core
|
||||
@author Allan Bowe, source: https://github.com/sasjs/core
|
||||
|
||||
<h4> Dependencies </h4>
|
||||
@li mp_abort.sas
|
||||
|
||||
@@ -32,13 +32,13 @@
|
||||
@li mf_getuser.sas
|
||||
|
||||
@version Viya 3.3
|
||||
@author Allan Bowe
|
||||
@author Allan Bowe, source: https://github.com/sasjs/core
|
||||
|
||||
**/
|
||||
%macro mv_webout(action,ds,fref=_mvwtemp,dslabel=,fmt=Y);
|
||||
%global _webin_file_count _webin_fileuri _debug _omittextlog _webin_name
|
||||
sasjs_tables SYS_JES_JOB_URI;
|
||||
%if %index("&_debug",log) %then %let _debug=131;
|
||||
%if %index("&_debug",log) %then %let _debug=131;
|
||||
|
||||
%local i tempds;
|
||||
%let action=%upcase(&action);
|
||||
@@ -144,8 +144,8 @@
|
||||
filename _webout temp lrecl=999999 mod;
|
||||
%end;
|
||||
%else %do;
|
||||
filename _webout filesrvc parenturi="&SYS_JES_JOB_URI"
|
||||
name="_webout.json" lrecl=999999 mod;
|
||||
filename _webout filesrvc parenturi="&SYS_JES_JOB_URI"
|
||||
name="_webout.json" lrecl=999999 mod;
|
||||
%end;
|
||||
|
||||
/* setup temp ref */
|
||||
|
||||
Reference in New Issue
Block a user