mirror of
https://github.com/sasjs/core.git
synced 2025-12-16 16:44:37 +00:00
Compare commits
34 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
369c4412f3 | ||
|
|
7d7608f06c | ||
|
|
3791cb8a2c | ||
|
|
ff82f7d75c | ||
|
|
fdd566e8ce | ||
|
|
328f8c260b | ||
|
|
029169ac80 | ||
| 66ff1de7a9 | |||
| 053290c7df | |||
| af71a5e53b | |||
| ecdce86287 | |||
| ba1272aaf7 | |||
| d6056b9397 | |||
|
|
00511c72c2 | ||
|
|
1d6f04fd56 | ||
| af4dbb5632 | |||
|
|
f48c291dce | ||
|
|
18be74a1c2 | ||
|
|
456d10a90e | ||
|
|
a7fdb52231 | ||
|
|
066ed00e44 | ||
|
|
49fbc210ad | ||
|
|
951aa474f2 | ||
|
|
961dd54ee0 | ||
|
|
921354dac7 | ||
|
|
48212f8797 | ||
| cb8992dade | |||
| 7dec3120be | |||
| 9568b17f20 | |||
| 0a38056c69 | |||
|
|
096bf4fa11 | ||
|
|
030c4a4fc1 | ||
| 1b70205cab | |||
| 539447ed06 |
@@ -1,44 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
#
|
|
||||||
# A hook script to verify that no filenames with capital letters are committed.
|
|
||||||
# Called by "git commit" with no arguments. The hook should
|
|
||||||
# exit with non-zero status after issuing an appropriate message if
|
|
||||||
# it wants to stop the commit.
|
|
||||||
#
|
|
||||||
# Go through all the changed files (except for deleted and unmerged)
|
|
||||||
|
|
||||||
# Save exit code of last executed action
|
|
||||||
exit_code=0
|
|
||||||
|
|
||||||
# Check if file is one of SAS|DDL|CSV|SH and check for uppercase letters
|
|
||||||
mime_pattern="\.(sas|ddl|csv|sh)"
|
|
||||||
# Check for capital letters only in file names
|
|
||||||
extra_pattern="(^|/)[^/]*([A-Z]+)[^/]*\.[A-Za-z]{3}$"
|
|
||||||
# Grep git diff of files to commit
|
|
||||||
files=$( git diff --cached --find-copies --find-renames --name-only --diff-filter=ACMRTXBU |
|
|
||||||
grep -Ei "$mime_pattern" |
|
|
||||||
grep -E "$extra_pattern" )
|
|
||||||
echo "$files"
|
|
||||||
if [[ -n "$files" ]];
|
|
||||||
then
|
|
||||||
echo
|
|
||||||
echo "Found files that contain capital letters."
|
|
||||||
echo "Please rename the following files in lowercase, and commit again:"
|
|
||||||
|
|
||||||
for file in $files; do
|
|
||||||
echo -e '- \E[0;32m'"$file"'\033[0m'
|
|
||||||
done
|
|
||||||
# Abort commit
|
|
||||||
exit_code=1
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ "$exit_code" == "0" ]; then
|
|
||||||
echo
|
|
||||||
echo -e '\033[1m'"Pre-commit validation Passed"'\033[0m'
|
|
||||||
echo
|
|
||||||
else
|
|
||||||
echo
|
|
||||||
echo -e '\033[1m'"Commit Aborted!"'\033[0m'
|
|
||||||
echo
|
|
||||||
fi
|
|
||||||
exit $exit_code
|
|
||||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -7,3 +7,6 @@ sasjsbuild/
|
|||||||
|
|
||||||
# ignore the mc_* files - containing macros for individual libraries
|
# ignore the mc_* files - containing macros for individual libraries
|
||||||
mc_*
|
mc_*
|
||||||
|
|
||||||
|
# ignore .env files as they can contain sasjs access tokens
|
||||||
|
*.env*
|
||||||
@@ -1,10 +1,6 @@
|
|||||||
|
|
||||||
FROM gitpod/workspace-full
|
FROM gitpod/workspace-full
|
||||||
|
|
||||||
RUN sudo apt-get update \
|
RUN sudo apt-get update \
|
||||||
&& sudo apt-get install -y \
|
&& sudo apt-get install -y \
|
||||||
doxygen \
|
doxygen \
|
||||||
&& npm i -g npm@latest \
|
|
||||||
&& npm i -g @sasjs/cli \
|
|
||||||
&& npm i \
|
|
||||||
&& sudo rm -rf /var/lib/apt/lists/*
|
&& sudo rm -rf /var/lib/apt/lists/*
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
tasks:
|
tasks:
|
||||||
- init: npm i && clear
|
- init: npm i -g @sasjs/cli
|
||||||
|
|
||||||
image:
|
image:
|
||||||
file: .gitpod.dockerfile
|
file: .gitpod.dockerfile
|
||||||
vscode:
|
vscode:
|
||||||
extensions:
|
extensions:
|
||||||
- sasjs.sasjs-for-vscode@1.6.0:V4hJpMtbpekMcPRNhh4SXQ==
|
- sasjs.sasjs-for-vscode
|
||||||
|
|||||||
13
.sasjslint
Normal file
13
.sasjslint
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
"noTrailingSpaces": true,
|
||||||
|
"noEncodedPasswords": true,
|
||||||
|
"hasDoxygenHeader": true,
|
||||||
|
"hasMacroNameInMend": false,
|
||||||
|
"hasMacroParentheses": true,
|
||||||
|
"noNestedMacros": false,
|
||||||
|
"noSpacesInFileNames": true,
|
||||||
|
"maxLineLength": 135,
|
||||||
|
"lowerCaseFileNames": true,
|
||||||
|
"noTabIndentation": true,
|
||||||
|
"indentationMultiple": 2
|
||||||
|
}
|
||||||
5
.vscode/extensions.json
vendored
Normal file
5
.vscode/extensions.json
vendored
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"recommendations": [
|
||||||
|
"sasjs.sasjs-for-vscode"
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -118,7 +118,7 @@ All macros must be commented in the doxygen format, to enable the [online docume
|
|||||||
### Dependencies
|
### Dependencies
|
||||||
SAS code can contain one of two types of dependency - SAS Macros, and SAS Programs. When compiling projects using the [SASjs CLI](https://cli.sasjs.io) the doxygen header is scanned for ` @li` items under the following headers:
|
SAS code can contain one of two types of dependency - SAS Macros, and SAS Programs. When compiling projects using the [SASjs CLI](https://cli.sasjs.io) the doxygen header is scanned for ` @li` items under the following headers:
|
||||||
|
|
||||||
```
|
```sas
|
||||||
<h4> SAS Macros </h4>
|
<h4> SAS Macros </h4>
|
||||||
@li mf_nobs.sas
|
@li mf_nobs.sas
|
||||||
@li mm_assignlib.sas
|
@li mm_assignlib.sas
|
||||||
|
|||||||
@@ -49,7 +49,10 @@
|
|||||||
input; putlog _infile_;
|
input; putlog _infile_;
|
||||||
i=1;
|
i=1;
|
||||||
retain logonce 0;
|
retain logonce 0;
|
||||||
if (_infile_=:"%str(WARN)ING" or _infile_=:"%str(ERR)OR") and logonce=0 then do;
|
if (
|
||||||
|
_infile_=:"%str(WARN)ING" or _infile_=:"%str(ERR)OR"
|
||||||
|
) and logonce=0
|
||||||
|
then do;
|
||||||
call symputx('logline',_n_);
|
call symputx('logline',_n_);
|
||||||
logonce+1;
|
logonce+1;
|
||||||
end;
|
end;
|
||||||
@@ -112,7 +115,8 @@
|
|||||||
%let syscc=0;
|
%let syscc=0;
|
||||||
%if %symexist(SYS_JES_JOB_URI) %then %do;
|
%if %symexist(SYS_JES_JOB_URI) %then %do;
|
||||||
/* refer web service output to file service in one hit */
|
/* refer web service output to file service in one hit */
|
||||||
filename _webout filesrvc parenturi="&SYS_JES_JOB_URI" name="_webout.json";
|
filename _webout filesrvc parenturi="&SYS_JES_JOB_URI"
|
||||||
|
name="_webout.json";
|
||||||
%let rc=%sysfunc(fcopy(_web,_webout));
|
%let rc=%sysfunc(fcopy(_web,_webout));
|
||||||
%end;
|
%end;
|
||||||
%else %do;
|
%else %do;
|
||||||
|
|||||||
@@ -22,6 +22,9 @@
|
|||||||
@version 9.2
|
@version 9.2
|
||||||
@author Allan Bowe
|
@author Allan Bowe
|
||||||
|
|
||||||
|
<h4> Related Macros </h4>
|
||||||
|
@li mf_getxengine.sas
|
||||||
|
|
||||||
**/
|
**/
|
||||||
/** @cond */
|
/** @cond */
|
||||||
|
|
||||||
@@ -32,7 +35,9 @@
|
|||||||
/* in case the parameter is a libref.tablename, pull off just the libref */
|
/* in case the parameter is a libref.tablename, pull off just the libref */
|
||||||
%let libref = %upcase(%scan(&libref, 1, %str(.)));
|
%let libref = %upcase(%scan(&libref, 1, %str(.)));
|
||||||
|
|
||||||
%let dsid=%sysfunc(open(sashelp.vlibnam(where=(libname="%upcase(&libref)")),i));
|
%let dsid=%sysfunc(
|
||||||
|
open(sashelp.vlibnam(where=(libname="%upcase(&libref)")),i)
|
||||||
|
);
|
||||||
%if (&dsid ^= 0) %then %do;
|
%if (&dsid ^= 0) %then %do;
|
||||||
%let engnum=%sysfunc(varnum(&dsid,ENGINE));
|
%let engnum=%sysfunc(varnum(&dsid,ENGINE));
|
||||||
%let rc=%sysfunc(fetch(&dsid));
|
%let rc=%sysfunc(fetch(&dsid));
|
||||||
|
|||||||
@@ -27,21 +27,23 @@
|
|||||||
or "&sysprocessmode"= "SAS Compute Server" %then %do;
|
or "&sysprocessmode"= "SAS Compute Server" %then %do;
|
||||||
SASVIYA
|
SASVIYA
|
||||||
%end;
|
%end;
|
||||||
%else %if "&sysprocessmode"="SAS Stored Process Server" %then %do;
|
%else %if "&sysprocessmode"="SAS Stored Process Server"
|
||||||
|
or "&sysprocessmode"="SAS Workspace Server"
|
||||||
|
%then %do;
|
||||||
SASMETA
|
SASMETA
|
||||||
%return;
|
%return;
|
||||||
%end;
|
%end;
|
||||||
%else %do;
|
%else %do;
|
||||||
SAS
|
BASESAS
|
||||||
%return;
|
%return;
|
||||||
%end;
|
%end;
|
||||||
%end;
|
%end;
|
||||||
%else %if %symexist(_metaport) %then %do;
|
%else %if %symexist(_metaport) or %symexist(_metauser) %then %do;
|
||||||
SASMETA
|
SASMETA
|
||||||
%return;
|
%return;
|
||||||
%end;
|
%end;
|
||||||
%else %do;
|
%else %do;
|
||||||
SAS
|
BASESAS
|
||||||
%return;
|
%return;
|
||||||
%end;
|
%end;
|
||||||
%end;
|
%end;
|
||||||
|
|||||||
@@ -10,14 +10,17 @@
|
|||||||
returns:
|
returns:
|
||||||
> List of Variables=Name Sex Age Height Weight
|
> List of Variables=Name Sex Age Height Weight
|
||||||
|
|
||||||
|
For a seperated list of column values:
|
||||||
|
|
||||||
%put %mf_getvarlist(sashelp.class,dlm=%str(,),quote=double);
|
%put %mf_getvarlist(sashelp.class,dlm=%str(,),quote=double);
|
||||||
|
|
||||||
returns:
|
returns:
|
||||||
> "Name","Sex","Age","Height","Weight"
|
> "Name","Sex","Age","Height","Weight"
|
||||||
|
|
||||||
@param libds Two part dataset (or view) reference.
|
@param [in] libds Two part dataset (or view) reference.
|
||||||
@param dlm= provide a delimiter (eg comma or space) to separate the vars
|
@param [in] dlm= ( ) Provide a delimiter (eg comma or space) to separate the
|
||||||
@param quote= use either DOUBLE or SINGLE to quote the results
|
variables
|
||||||
|
@param [in] quote= (none) use either DOUBLE or SINGLE to quote the results
|
||||||
|
|
||||||
@version 9.2
|
@version 9.2
|
||||||
@author Allan Bowe
|
@author Allan Bowe
|
||||||
|
|||||||
43
base/mf_getxengine.sas
Normal file
43
base/mf_getxengine.sas
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
/**
|
||||||
|
@file
|
||||||
|
@brief Returns the engine type of a SAS fileref
|
||||||
|
@details Queries sashelp.vextfl to get the xengine value.
|
||||||
|
Usage:
|
||||||
|
|
||||||
|
filename feng temp;
|
||||||
|
%put %mf_getxengine(feng);
|
||||||
|
|
||||||
|
returns:
|
||||||
|
> TEMP
|
||||||
|
|
||||||
|
@param fref The fileref to check
|
||||||
|
|
||||||
|
@returns The XENGINE value in sashelp.vextfl or 0 if not found.
|
||||||
|
|
||||||
|
@version 9.2
|
||||||
|
@author Allan Bowe
|
||||||
|
|
||||||
|
<h4> Related Macros </h4>
|
||||||
|
@li mf_getengine.sas
|
||||||
|
|
||||||
|
**/
|
||||||
|
|
||||||
|
%macro mf_getxengine(fref
|
||||||
|
)/*/STORE SOURCE*/;
|
||||||
|
%local dsid engnum rc engine;
|
||||||
|
|
||||||
|
%let dsid=%sysfunc(
|
||||||
|
open(sashelp.vextfl(where=(fileref="%upcase(&fref)")),i)
|
||||||
|
);
|
||||||
|
%if (&dsid ^= 0) %then %do;
|
||||||
|
%let engnum=%sysfunc(varnum(&dsid,XENGINE));
|
||||||
|
%let rc=%sysfunc(fetch(&dsid));
|
||||||
|
%let engine=%sysfunc(getvarc(&dsid,&engnum));
|
||||||
|
%* put &fref. ENGINE is &engine.;
|
||||||
|
%let rc= %sysfunc(close(&dsid));
|
||||||
|
%end;
|
||||||
|
%else %let engine=0;
|
||||||
|
|
||||||
|
&engine
|
||||||
|
|
||||||
|
%mend;
|
||||||
@@ -9,7 +9,8 @@
|
|||||||
|
|
||||||
%put mf_isblank(&var);
|
%put mf_isblank(&var);
|
||||||
|
|
||||||
inspiration: https://support.sas.com/resources/papers/proceedings09/022-2009.pdf
|
inspiration:
|
||||||
|
https://support.sas.com/resources/papers/proceedings09/022-2009.pdf
|
||||||
|
|
||||||
@param param VALUE to be checked
|
@param param VALUE to be checked
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,8 @@
|
|||||||
|
|
||||||
%let isdir=%mf_isdir(/tmp);
|
%let isdir=%mf_isdir(/tmp);
|
||||||
|
|
||||||
With thanks and full credit to Andrea Defronzo - https://www.linkedin.com/in/andrea-defronzo-b1a47460/
|
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
|
@param path full path of the file/directory to be checked
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
/**
|
/**
|
||||||
@file mf_mval.sas
|
@file mf_mval.sas
|
||||||
@brief Returns a macro variable value if the variable exists
|
@brief Returns a macro variable value if the variable exists
|
||||||
@details Use this macro to avoid repetitive use of `%if %symexist(MACVAR) %then`
|
@details
|
||||||
|
Use this macro to avoid repetitive use of `%if %symexist(MACVAR) %then`
|
||||||
type logic.
|
type logic.
|
||||||
Usage:
|
Usage:
|
||||||
|
|
||||||
|
|||||||
@@ -12,7 +12,8 @@
|
|||||||
|
|
||||||
|
|
||||||
@param basestr The string to be modified
|
@param basestr The string to be modified
|
||||||
@param trimstr The string to be removed from the end of `basestr`, if it exists
|
@param trimstr The string to be removed from the end of `basestr`, if it
|
||||||
|
exists
|
||||||
|
|
||||||
@return output returns result with the value of `trimstr` removed from the end
|
@return output returns result with the value of `trimstr` removed from the end
|
||||||
|
|
||||||
|
|||||||
@@ -6,11 +6,11 @@
|
|||||||
results back to the client in an STP Web App context, or completely stop
|
results back to the client in an STP Web App context, or completely stop
|
||||||
in the case of a batch run.
|
in the case of a batch run.
|
||||||
|
|
||||||
Using SAS Abort Cancel mechanisms can cause hung sessions in some Stored Process
|
Using SAS Abort Cancel mechanisms can cause hung sessions in some Stored
|
||||||
environments. This macro takes a unique approach - we set the SAS syscc to 0,
|
Process environments. This macro takes a unique approach - we set the SAS
|
||||||
run `stpsrvset('program error', 0)` (if SAS 9) and then - we open a macro
|
syscc to 0, run `stpsrvset('program error', 0)` (if SAS 9) and then - we open
|
||||||
but don't close it! This provides a graceful abort for SAS web services in all
|
a macro but don't close it! This provides a graceful abort for SAS web
|
||||||
web enabled environments.
|
services in all web enabled environments.
|
||||||
|
|
||||||
@param mac= to contain the name of the calling macro
|
@param mac= to contain the name of the calling macro
|
||||||
@param msg= message to be returned
|
@param msg= message to be returned
|
||||||
@@ -48,7 +48,10 @@
|
|||||||
input; putlog _infile_;
|
input; putlog _infile_;
|
||||||
i=1;
|
i=1;
|
||||||
retain logonce 0;
|
retain logonce 0;
|
||||||
if (_infile_=:"%str(WARN)ING" or _infile_=:"%str(ERR)OR") and logonce=0 then do;
|
if (
|
||||||
|
_infile_=:"%str(WARN)ING" or _infile_=:"%str(ERR)OR"
|
||||||
|
) and logonce=0 then
|
||||||
|
do;
|
||||||
call symputx('logline',_n_);
|
call symputx('logline',_n_);
|
||||||
logonce+1;
|
logonce+1;
|
||||||
end;
|
end;
|
||||||
|
|||||||
87
base/mp_assertdsobs.sas
Normal file
87
base/mp_assertdsobs.sas
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
/**
|
||||||
|
@file
|
||||||
|
@brief Asserts the number of observations in a dataset
|
||||||
|
@details Useful in the context of writing sasjs tests. The results of the
|
||||||
|
test are _appended_ to the &outds. table.
|
||||||
|
|
||||||
|
Example usage:
|
||||||
|
|
||||||
|
%mp_assertdsobs(sashelp.class) %* tests if any observations are present;
|
||||||
|
|
||||||
|
<h4> SAS Macros </h4>
|
||||||
|
@li mf_nobs.sas
|
||||||
|
@li mp_abort.sas
|
||||||
|
|
||||||
|
|
||||||
|
@param [in] inds input dataset to test for presence of observations
|
||||||
|
@param [in] desc= (Testing observations) The user provided test description
|
||||||
|
@param [in] test= (HASOBS) The test to apply. Valid values are:
|
||||||
|
@li HASOBS - Test is a PASS if the input dataset has any observations
|
||||||
|
@li EMPTY - Test is a PASS if input dataset is empty
|
||||||
|
@li EQUALS [integer] - Test passes if obs count matches the provided integer
|
||||||
|
@param [out] outds= (work.test_results) The output dataset to contain the
|
||||||
|
results. If it does not exist, it will be created, with the following format:
|
||||||
|
|TEST_DESCRIPTION:$256|TEST_RESULT:$4|TEST_COMMENTS:$256|
|
||||||
|
|---|---|---|
|
||||||
|
|User Provided description|PASS|Dataset &inds has XX obs|
|
||||||
|
|
||||||
|
|
||||||
|
@version 9.2
|
||||||
|
@author Allan Bowe
|
||||||
|
|
||||||
|
**/
|
||||||
|
|
||||||
|
%macro mp_assertdsobs(inds,
|
||||||
|
test=HASOBS,
|
||||||
|
desc=Testing observations,
|
||||||
|
outds=work.test_results
|
||||||
|
)/*/STORE SOURCE*/;
|
||||||
|
|
||||||
|
%local nobs;
|
||||||
|
%let nobs=%mf_nobs(&inds);
|
||||||
|
%let test=%upcase(&test);
|
||||||
|
|
||||||
|
%if %substr(&test.xxxxx,1,6)=EQUALS %then %do;
|
||||||
|
%let val=%scan(&test,2,%str( ));
|
||||||
|
%mp_abort(iftrue= (%DATATYP(&val)=CHAR)
|
||||||
|
,mac=&sysmacroname
|
||||||
|
,msg=%str(Invalid test - &test, expected EQUALS [integer])
|
||||||
|
)
|
||||||
|
%let test=EQUALS;
|
||||||
|
%end;
|
||||||
|
%else %if &test ne HASOBS and &test ne EMPTY %then %do;
|
||||||
|
%mp_abort(
|
||||||
|
mac=&sysmacroname,
|
||||||
|
msg=%str(Invalid test - &test)
|
||||||
|
)
|
||||||
|
%end;
|
||||||
|
|
||||||
|
data;
|
||||||
|
length test_description $256 test_result $4 test_comments $256;
|
||||||
|
test_description=symget('desc');
|
||||||
|
test_result='FAIL';
|
||||||
|
test_comments="&sysmacroname: Dataset &inds has &nobs observations";
|
||||||
|
%if &test=HASOBS %then %do;
|
||||||
|
if &nobs>0 then test_result='PASS';
|
||||||
|
%end;
|
||||||
|
%else %if &test=EMPTY %then %do;
|
||||||
|
if &nobs=0 then test_result='PASS';
|
||||||
|
%end;
|
||||||
|
%else %if &test=EQUALS %then %do;
|
||||||
|
if &nobs=&val then test_result='PASS';
|
||||||
|
%end;
|
||||||
|
%else %do;
|
||||||
|
test_comments="&sysmacroname: Unsatisfied test condition - &test";
|
||||||
|
%end;
|
||||||
|
run;
|
||||||
|
|
||||||
|
%local ds;
|
||||||
|
%let ds=&syslast;
|
||||||
|
|
||||||
|
proc append base=&outds data=&ds;
|
||||||
|
run;
|
||||||
|
|
||||||
|
proc sql;
|
||||||
|
drop table &ds;
|
||||||
|
|
||||||
|
%mend;
|
||||||
@@ -4,7 +4,8 @@
|
|||||||
@details Reads in a file byte by byte and writes it back out. Is an
|
@details Reads in a file byte by byte and writes it back out. Is an
|
||||||
os-independent method to copy files. In case of naming collision, the
|
os-independent method to copy files. In case of naming collision, the
|
||||||
default filerefs can be modified.
|
default filerefs can be modified.
|
||||||
Based on http://stackoverflow.com/questions/13046116/using-sas-to-copy-a-text-file
|
Based on:
|
||||||
|
https://stackoverflow.com/questions/13046116/using-sas-to-copy-a-text-file
|
||||||
|
|
||||||
%mp_binarycopy(inloc="/home/me/blah.txt", outref=_webout)
|
%mp_binarycopy(inloc="/home/me/blah.txt", outref=_webout)
|
||||||
|
|
||||||
|
|||||||
@@ -23,7 +23,7 @@
|
|||||||
|
|
||||||
%macro mp_cleancsv(in=NOTPROVIDED,out=NOTPROVIDED,qchar='22'x);
|
%macro mp_cleancsv(in=NOTPROVIDED,out=NOTPROVIDED,qchar='22'x);
|
||||||
%if "&in"="NOTPROVIDED" or "&out"="NOTPROVIDED" %then %do;
|
%if "&in"="NOTPROVIDED" or "&out"="NOTPROVIDED" %then %do;
|
||||||
%put %str(ERR)OR: Please provide valid input (&in) and output (&out) locations;
|
%put %str(ERR)OR: Please provide valid input (&in) & output (&out) locations;
|
||||||
%return;
|
%return;
|
||||||
%end;
|
%end;
|
||||||
|
|
||||||
|
|||||||
@@ -57,8 +57,11 @@
|
|||||||
)/*/STORE SOURCE*/;
|
)/*/STORE SOURCE*/;
|
||||||
%let getattrs=%upcase(&getattrs)XX;
|
%let getattrs=%upcase(&getattrs)XX;
|
||||||
|
|
||||||
data &outds (compress=no keep=file_or_folder filepath filename ext msg directory);
|
data &outds(compress=no
|
||||||
length directory filepath $500 fref fref2 $8 file_or_folder $6 filename $80 ext $20 msg $200;
|
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;
|
%if &fref=0 %then %do;
|
||||||
rc = filename(fref, "&path");
|
rc = filename(fref, "&path");
|
||||||
%end;
|
%end;
|
||||||
|
|||||||
@@ -14,16 +14,18 @@
|
|||||||
- explicity setting a unix LF
|
- explicity setting a unix LF
|
||||||
- constraints / indexes etc
|
- constraints / indexes etc
|
||||||
|
|
||||||
@param [in] base_ds= Should be two level - eg work.blah. This is the table that
|
@param [in] base_ds= Should be two level - eg work.blah. This is the table
|
||||||
is converted to a cards file.
|
that is converted to a cards file.
|
||||||
@param [in] tgt_ds= Table that the generated cards file would create. Optional -
|
@param [in] tgt_ds= Table that the generated cards file would create.
|
||||||
if omitted, will be same as BASE_DS.
|
Optional - if omitted, will be same as BASE_DS.
|
||||||
@param [out] cards_file= Location in which to write the (.sas) cards file
|
@param [out] cards_file= Location in which to write the (.sas) cards file
|
||||||
@param [in] maxobs= to limit output to the first <code>maxobs</code> observations
|
@param [in] maxobs= to limit output to the first <code>maxobs</code>
|
||||||
@param [in] showlog= whether to show generated cards file in the SAS log (YES/NO)
|
observations
|
||||||
|
@param [in] showlog= whether to show generated cards file in the SAS log
|
||||||
|
(YES/NO)
|
||||||
@param [in] outencoding= provide encoding value for file statement (eg utf-8)
|
@param [in] outencoding= provide encoding value for file statement (eg utf-8)
|
||||||
@param [in] append= If NO then will rebuild the cards file if it already exists,
|
@param [in] append= If NO then will rebuild the cards file if it already
|
||||||
otherwise will append to it. Used by the mp_lib2cards.sas macro.
|
exists, otherwise will append to it. Used by the mp_lib2cards.sas macro.
|
||||||
|
|
||||||
|
|
||||||
@version 9.2
|
@version 9.2
|
||||||
@@ -64,9 +66,11 @@ select count(*) into: nvars from dictionary.columns
|
|||||||
%end;
|
%end;
|
||||||
|
|
||||||
/* get indexes */
|
/* get indexes */
|
||||||
proc sort data=sashelp.vindex
|
proc sort
|
||||||
(where=(upcase(libname)="%scan(%upcase(&base_ds),1)"
|
data=sashelp.vindex(
|
||||||
and upcase(memname)="%scan(%upcase(&base_ds),2)"))
|
where=(upcase(libname)="%scan(%upcase(&base_ds),1)"
|
||||||
|
and upcase(memname)="%scan(%upcase(&base_ds),2)")
|
||||||
|
)
|
||||||
out=_data_;
|
out=_data_;
|
||||||
by indxname indxpos;
|
by indxname indxpos;
|
||||||
run;
|
run;
|
||||||
|
|||||||
157
base/mp_filtercheck.sas
Normal file
157
base/mp_filtercheck.sas
Normal file
@@ -0,0 +1,157 @@
|
|||||||
|
/**
|
||||||
|
@file
|
||||||
|
@brief Checks an input filter table for validity
|
||||||
|
@details Performs checks on the input table to ensure it arrives in the
|
||||||
|
correct format. This is necessary to prevent code injection. Will update
|
||||||
|
SYSCC to 1008 if bad records are found, and call mp_abort.sas for a
|
||||||
|
graceful service exit (configurable).
|
||||||
|
|
||||||
|
Used for dynamic filtering in [Data Controller for SAS®](https://datacontroller.io).
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
|
||||||
|
%mp_filtercheck(work.filter,targetds=sashelp.class,outds=work.badrecords)
|
||||||
|
|
||||||
|
The input table should have the following format:
|
||||||
|
|
||||||
|
|GROUP_LOGIC:$3|SUBGROUP_LOGIC:$3|SUBGROUP_ID:8.|VARIABLE_NM:$32|OPERATOR_NM:$10|RAW_VALUE:$32767|
|
||||||
|
|---|---|---|---|---|---|
|
||||||
|
|AND|AND|1|AGE|=|12|
|
||||||
|
|AND|AND|1|SEX|<=|'M'|
|
||||||
|
|AND|OR|2|Name|NOT IN|('Jane','Alfred')|
|
||||||
|
|AND|OR|2|Weight|>=|7|
|
||||||
|
|
||||||
|
Rules applied:
|
||||||
|
|
||||||
|
@li GROUP_LOGIC - only AND/OR
|
||||||
|
@li SUBGROUP_LOGIC - only AND/OR
|
||||||
|
@li SUBGROUP_ID - only integers
|
||||||
|
@li VARIABLE_NM - must be in the target table
|
||||||
|
@li OPERATOR_NM - only =/>/</<=/>=/BETWEEN/IN/NOT IN/NOT EQUAL/CONTAINS
|
||||||
|
@li RAW_VALUE - no unquoted values except integers, commas and spaces.
|
||||||
|
|
||||||
|
@returns The &outds table containing any bad rows, plus a REASON_CD column.
|
||||||
|
|
||||||
|
@param [in] inds The table to be checked, with the format above
|
||||||
|
@param [in] targetds= The target dataset against which to verify VARIABLE_NM
|
||||||
|
@param [out] abort= (YES) If YES will call mp_abort.sas on any exceptions
|
||||||
|
@param [out] outds= The output table, which is a copy of the &inds. table
|
||||||
|
plus a REASON_CD column, containing only bad records. If bad records found,
|
||||||
|
the SYSCC value will be set to 1008 (general data problem). Downstream
|
||||||
|
processes should check this table (and return code) before continuing.
|
||||||
|
|
||||||
|
<h4> SAS Macros </h4>
|
||||||
|
@li mp_abort.sas
|
||||||
|
@li mf_getvarlist.sas
|
||||||
|
@li mf_nobs.sas
|
||||||
|
|
||||||
|
<h4> Related Macros </h4>
|
||||||
|
@li mp_filtergenerate.sas
|
||||||
|
|
||||||
|
@version 9.3
|
||||||
|
@author Allan Bowe
|
||||||
|
|
||||||
|
@todo Support date / hex / name literals and exponents in RAW_VALUE field
|
||||||
|
**/
|
||||||
|
|
||||||
|
%macro mp_filtercheck(inds,targetds=,outds=work.badrecords,abort=YES);
|
||||||
|
|
||||||
|
%mp_abort(iftrue= (&syscc ne 0)
|
||||||
|
,mac=&sysmacroname
|
||||||
|
,msg=%str(syscc=&syscc - on macro entry)
|
||||||
|
)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sanitise the values based on valid value lists, then strip out
|
||||||
|
* quotes, commas, periods and spaces.
|
||||||
|
* Only numeric values should remain
|
||||||
|
*/
|
||||||
|
|
||||||
|
data &outds;
|
||||||
|
set &inds;
|
||||||
|
length reason_cd $32;
|
||||||
|
|
||||||
|
/* closed list checks */
|
||||||
|
if GROUP_LOGIC not in ('AND','OR') then do;
|
||||||
|
REASON_CD='GROUP_LOGIC should be either AND or OR';
|
||||||
|
putlog REASON_CD= GROUP_LOGIC=;
|
||||||
|
output;
|
||||||
|
end;
|
||||||
|
if SUBGROUP_LOGIC not in ('AND','OR') then do;
|
||||||
|
REASON_CD='SUBGROUP_LOGIC should be either AND or OR';
|
||||||
|
putlog REASON_CD= SUBGROUP_LOGIC=;
|
||||||
|
output;
|
||||||
|
end;
|
||||||
|
if mod(SUBGROUP_ID,1) ne 0 then do;
|
||||||
|
REASON_CD='SUBGROUP_ID should be integer';
|
||||||
|
putlog REASON_CD= SUBGROUP_ID=;
|
||||||
|
output;
|
||||||
|
end;
|
||||||
|
if upcase(VARIABLE_NM) not in
|
||||||
|
(%upcase(%mf_getvarlist(&targetds,dlm=%str(,),quote=SINGLE)))
|
||||||
|
then do;
|
||||||
|
REASON_CD="VARIABLE_NM not in &targetds";
|
||||||
|
putlog REASON_CD= VARIABLE_NM=;
|
||||||
|
output;
|
||||||
|
end;
|
||||||
|
if OPERATOR_NM not in
|
||||||
|
('=','>','<','<=','>=','BETWEEN','IN','NOT IN','NE','CONTAINS')
|
||||||
|
then do;
|
||||||
|
REASON_CD='Invalid OPERATOR_NM';
|
||||||
|
putlog REASON_CD= OPERATOR_NM=;
|
||||||
|
output;
|
||||||
|
end;
|
||||||
|
|
||||||
|
/* special logic */
|
||||||
|
if OPERATOR_NM='BETWEEN' then raw_value1=tranwrd(raw_value,' AND ','');
|
||||||
|
else if OPERATOR_NM in ('IN','NOT IN') then do;
|
||||||
|
if substr(raw_value,1,1) ne '('
|
||||||
|
or substr(cats(reverse(raw_value)),1,1) ne ')'
|
||||||
|
then do;
|
||||||
|
REASON_CD='Missing brackets in RAW_VALUE';
|
||||||
|
putlog REASON_CD= OPERATOR_NM= raw_value= raw_value1= ;
|
||||||
|
output;
|
||||||
|
end;
|
||||||
|
else raw_value1=substr(raw_value,2,max(length(raw_value)-2,0));
|
||||||
|
end;
|
||||||
|
else raw_value1=raw_value;
|
||||||
|
|
||||||
|
/* remove nested literals eg '' */
|
||||||
|
raw_value1=tranwrd(raw_value1,"''",'');
|
||||||
|
|
||||||
|
/* now match string literals (always single quotes) */
|
||||||
|
raw_value2=raw_value1;
|
||||||
|
regex = prxparse("s/(\').*?(\')//");
|
||||||
|
call prxchange(regex,-1,raw_value2);
|
||||||
|
|
||||||
|
/* remove commas and periods*/
|
||||||
|
raw_value3=compress(raw_value2,',.');
|
||||||
|
|
||||||
|
/* output records that contain values other than digits and spaces */
|
||||||
|
if notdigit(compress(raw_value3,' '))>0 then do;
|
||||||
|
putlog raw_value3= $hex32.;
|
||||||
|
REASON_CD='Invalid RAW_VALUE';
|
||||||
|
putlog REASON_CD= raw_value= raw_value1= raw_value2= raw_value3=;
|
||||||
|
output;
|
||||||
|
end;
|
||||||
|
|
||||||
|
run;
|
||||||
|
|
||||||
|
%if %mf_nobs(&outds)>0 %then %do;
|
||||||
|
%if &abort=YES %then %do;
|
||||||
|
data _null_;
|
||||||
|
set &outds;
|
||||||
|
call symputx('REASON_CD',reason_cd,'l');
|
||||||
|
stop;
|
||||||
|
run;
|
||||||
|
%mp_abort(
|
||||||
|
mac=&sysmacroname,
|
||||||
|
msg=%str(Filter issues in &inds, first was &reason_cd, details in &outds)
|
||||||
|
)
|
||||||
|
%end;
|
||||||
|
%let syscc=1008;
|
||||||
|
%end;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
%mend;
|
||||||
100
base/mp_filtergenerate.sas
Normal file
100
base/mp_filtergenerate.sas
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
/**
|
||||||
|
@file
|
||||||
|
@brief Generates a filter clause from an input table, to a fileref
|
||||||
|
@details Uses the input table to generate an output filter clause.
|
||||||
|
This feature is used to create dynamic dropdowns in [Data Controller for SAS®](
|
||||||
|
https://datacontroller.io). The input table should be in the format below:
|
||||||
|
|
||||||
|
|GROUP_LOGIC:$3|SUBGROUP_LOGIC:$3|SUBGROUP_ID:8.|VARIABLE_NM:$32|OPERATOR_NM:$10|RAW_VALUE:$32767|
|
||||||
|
|---|---|---|---|---|---|
|
||||||
|
|AND|AND|1|AGE|=|12|
|
||||||
|
|AND|AND|1|SEX|<=|'M'|
|
||||||
|
|AND|OR|2|Name|NOT IN|('Jane','Alfred')|
|
||||||
|
|AND|OR|2|Weight|>=|7|
|
||||||
|
|
||||||
|
Note - if the above table is received from an external client, the values
|
||||||
|
should first be validated using the mp_filtercheck.sas macro to avoid risk
|
||||||
|
of SQL injection.
|
||||||
|
|
||||||
|
To generate the filter, run the following code:
|
||||||
|
|
||||||
|
data work.filtertable;
|
||||||
|
infile datalines4 dsd;
|
||||||
|
input GROUP_LOGIC:$3. SUBGROUP_LOGIC:$3. SUBGROUP_ID:8. VARIABLE_NM:$32.
|
||||||
|
OPERATOR_NM:$10. RAW_VALUE:$32767.;
|
||||||
|
datalines4;
|
||||||
|
AND,AND,1,AGE,=,12
|
||||||
|
AND,AND,1,SEX,<=,"'M'"
|
||||||
|
AND,OR,2,Name,NOT IN,"('Jane','Alfred')"
|
||||||
|
AND,OR,2,Weight,>=,7
|
||||||
|
;;;;
|
||||||
|
run;
|
||||||
|
|
||||||
|
%mp_filtergenerate(work.filtertable,outref=myfilter)
|
||||||
|
|
||||||
|
data _null_;
|
||||||
|
infile myfilter;
|
||||||
|
input;
|
||||||
|
put _infile_;
|
||||||
|
run;
|
||||||
|
|
||||||
|
Will write the following query to the log:
|
||||||
|
|
||||||
|
> (
|
||||||
|
> AGE = 12
|
||||||
|
> AND
|
||||||
|
> SEX <= 'M'
|
||||||
|
> ) AND (
|
||||||
|
> Name NOT IN ('Jane','Alfred')
|
||||||
|
> OR
|
||||||
|
> Weight >= 7
|
||||||
|
> )
|
||||||
|
|
||||||
|
@param [in] inds The input table with query values
|
||||||
|
@param [out] outref= The output fileref to contain the filter clause. Will
|
||||||
|
be created (or replaced).
|
||||||
|
|
||||||
|
<h4> Related Macros </h4>
|
||||||
|
@li mp_filtercheck.sas
|
||||||
|
|
||||||
|
<h4> SAS Macros </h4>
|
||||||
|
@li mp_abort.sas
|
||||||
|
@li mf_nobs.sas
|
||||||
|
|
||||||
|
@version 9.3
|
||||||
|
@author Allan Bowe
|
||||||
|
|
||||||
|
**/
|
||||||
|
|
||||||
|
%macro mp_filtergenerate(inds,outref=filter);
|
||||||
|
|
||||||
|
%mp_abort(iftrue= (&syscc ne 0)
|
||||||
|
,mac=&sysmacroname
|
||||||
|
,msg=%str(syscc=&syscc - on macro entry)
|
||||||
|
)
|
||||||
|
|
||||||
|
filename &outref temp;
|
||||||
|
|
||||||
|
%if %mf_nobs(&inds)=0 %then %do;
|
||||||
|
/* ensure we have a default filter */
|
||||||
|
data _null_;
|
||||||
|
file &outref;
|
||||||
|
put '1=1';
|
||||||
|
run;
|
||||||
|
%end;
|
||||||
|
%else %do;
|
||||||
|
data _null_;
|
||||||
|
file &outref lrecl=32800;
|
||||||
|
set &inds end=last;
|
||||||
|
by SUBGROUP_ID;
|
||||||
|
if _n_=1 then put '(';
|
||||||
|
else if first.SUBGROUP_ID then put +1 GROUP_LOGIC '(';
|
||||||
|
else put +2 SUBGROUP_LOGIC;
|
||||||
|
|
||||||
|
put +4 VARIABLE_NM OPERATOR_NM RAW_VALUE;
|
||||||
|
|
||||||
|
if last.SUBGROUP_ID then put ')'@;
|
||||||
|
run;
|
||||||
|
%end;
|
||||||
|
|
||||||
|
%mend;
|
||||||
@@ -133,7 +133,9 @@ run;
|
|||||||
|
|
||||||
if notnull='yes' then notnul=' not null';
|
if notnull='yes' then notnul=' not null';
|
||||||
if notnull='no' and missing(label) then put ' ' name typ;
|
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='yes' and missing(label) then do;
|
||||||
|
put ' ' name typ '[' notnul ']';
|
||||||
|
end;
|
||||||
else if notnull='no' then put ' ' name typ '[' lab ']';
|
else if notnull='no' then put ' ' name typ '[' lab ']';
|
||||||
else put ' ' name typ '[' notnul ',' lab ']';
|
else put ' ' name typ '[' notnul ',' lab ']';
|
||||||
|
|
||||||
@@ -166,7 +168,7 @@ run;
|
|||||||
call symputx('constcheck',1);
|
call symputx('constcheck',1);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
if last then call symputx('constraints_used',cats(upcase(constraints_used)));
|
if last then call symput('constraints_used',cats(upcase(constraints_used)));
|
||||||
|
|
||||||
length curds const col $39;
|
length curds const col $39;
|
||||||
curds="&curds";
|
curds="&curds";
|
||||||
@@ -176,7 +178,8 @@ run;
|
|||||||
|
|
||||||
proc append base=&pkds data=&syslast;run;
|
proc append base=&pkds data=&syslast;run;
|
||||||
|
|
||||||
/* Create Unique Indexes, but only if they were not already defined within the Constraints section. */
|
/* Create Unique Indexes, but only if they were not already defined within
|
||||||
|
the Constraints section. */
|
||||||
data _data_(keep=curds const col);
|
data _data_(keep=curds const col);
|
||||||
set &idxinfo (where=(
|
set &idxinfo (where=(
|
||||||
libname="%scan(&curds,1,.)"
|
libname="%scan(&curds,1,.)"
|
||||||
@@ -187,7 +190,7 @@ run;
|
|||||||
file &outref mod;
|
file &outref mod;
|
||||||
by idxusage indxname;
|
by idxusage indxname;
|
||||||
name=upcase(name);
|
name=upcase(name);
|
||||||
if &constcheck=1 then stop; /* in fact we only care about PKs so stop if we have */
|
if &constcheck=1 then stop; /* we only care about PKs so stop if we have */
|
||||||
if _n_=1 and &constcheck=0 then put / ' indexes {';
|
if _n_=1 and &constcheck=0 then put / ' indexes {';
|
||||||
|
|
||||||
length cols $5000;
|
length cols $5000;
|
||||||
@@ -261,7 +264,11 @@ run;
|
|||||||
line='Ref: "'!!"&curds"
|
line='Ref: "'!!"&curds"
|
||||||
!!cats('".(',"%mf_getquotedstr(&pkcols,dlm=%str(,),quote=%str( ))",')')
|
!!cats('".(',"%mf_getquotedstr(&pkcols,dlm=%str(,),quote=%str( ))",')')
|
||||||
!!' - '
|
!!' - '
|
||||||
!!cats(quote(trim(curds)),'.(',"%mf_getquotedstr(&pkcols,dlm=%str(,),quote=%str( ))",')');
|
!!cats(quote(trim(curds))
|
||||||
|
,'.('
|
||||||
|
,"%mf_getquotedstr(&pkcols,dlm=%str(,),quote=%str( ))"
|
||||||
|
,')'
|
||||||
|
);
|
||||||
put line;
|
put line;
|
||||||
run;
|
run;
|
||||||
|
|
||||||
@@ -282,7 +289,9 @@ run;
|
|||||||
create table &pkds.5b as
|
create table &pkds.5b as
|
||||||
select curds,count(*) as cnt
|
select curds,count(*) as cnt
|
||||||
from &pkds.5a
|
from &pkds.5a
|
||||||
where curds not in (select curds from &pkds.2 where cols="&pkcols") /* not a one to one match */
|
where curds not in (
|
||||||
|
select curds from &pkds.2 where cols="&pkcols"
|
||||||
|
) /* not a one to one match */
|
||||||
and curds ne "&curds" /* exclude self */
|
and curds ne "&curds" /* exclude self */
|
||||||
group by 1;
|
group by 1;
|
||||||
create table &pkds.6 as
|
create table &pkds.6 as
|
||||||
|
|||||||
@@ -86,7 +86,9 @@ create table _data_ as
|
|||||||
%global constraints_used;
|
%global constraints_used;
|
||||||
data _null_;
|
data _null_;
|
||||||
length ctype $11 constraint_name_orig $256 constraints_used $5000;
|
length ctype $11 constraint_name_orig $256 constraints_used $5000;
|
||||||
set &colconst (where=(table_name="&curds" and constraint_type in ('PRIMARY','UNIQUE'))) end=last;
|
set &colconst(
|
||||||
|
where=(table_name="&curds" and constraint_type in ('PRIMARY','UNIQUE'))
|
||||||
|
) end=last;
|
||||||
file &fref mod;
|
file &fref mod;
|
||||||
by constraint_type constraint_name;
|
by constraint_type constraint_name;
|
||||||
retain constraints_used;
|
retain constraints_used;
|
||||||
@@ -161,10 +163,19 @@ run;
|
|||||||
put ');';
|
put ');';
|
||||||
run;
|
run;
|
||||||
|
|
||||||
/* Create Unique Indexes, but only if they were not already defined within the Constraints section. */
|
/* Create Unique Indexes, but only if they were not already defined within
|
||||||
|
the Constraints section. */
|
||||||
data _null_;
|
data _null_;
|
||||||
*length ds $128;
|
*length ds $128;
|
||||||
set &idxinfo (where=(memname="&curds" and unique='yes' and indxname not in (%sysfunc(tranwrd("&constraints_used",%str( ),%str(","))))));
|
set &idxinfo(
|
||||||
|
where=(
|
||||||
|
memname="&curds"
|
||||||
|
and unique='yes'
|
||||||
|
and indxname not in (
|
||||||
|
%sysfunc(tranwrd("&constraints_used",%str( ),%str(",")))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
file &fref mod;
|
file &fref mod;
|
||||||
by idxusage indxname;
|
by idxusage indxname;
|
||||||
/* ds=cats(libname,'.',memname); */
|
/* ds=cats(libname,'.',memname); */
|
||||||
@@ -228,10 +239,19 @@ run;
|
|||||||
/* Extra step for data constraints */
|
/* Extra step for data constraints */
|
||||||
%addConst()
|
%addConst()
|
||||||
|
|
||||||
/* Create Unique Indexes, but only if they were not already defined within the Constraints section. */
|
/* Create Unique Indexes, but only if they were not already defined within
|
||||||
|
the Constraints section. */
|
||||||
data _null_;
|
data _null_;
|
||||||
*length ds $128;
|
*length ds $128;
|
||||||
set &idxinfo (where=(memname="&curds" and unique='yes' and indxname not in (%sysfunc(tranwrd("&constraints_used",%str( ),%str(","))))));
|
set &idxinfo(
|
||||||
|
where=(
|
||||||
|
memname="&curds"
|
||||||
|
and unique='yes'
|
||||||
|
and indxname not in (
|
||||||
|
%sysfunc(tranwrd("&constraints_used",%str( ),%str(",")))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
file &fref mod;
|
file &fref mod;
|
||||||
by idxusage indxname;
|
by idxusage indxname;
|
||||||
*ds=cats(libname,'.',memname);
|
*ds=cats(libname,'.',memname);
|
||||||
@@ -320,10 +340,19 @@ run;
|
|||||||
put ');';
|
put ');';
|
||||||
run;
|
run;
|
||||||
|
|
||||||
/* Create Unique Indexes, but only if they were not already defined within the Constraints section. */
|
/* Create Unique Indexes, but only if they were not already defined within
|
||||||
|
the Constraints section. */
|
||||||
data _null_;
|
data _null_;
|
||||||
*length ds $128;
|
*length ds $128;
|
||||||
set &idxinfo (where=(memname="&curds" and unique='yes' and indxname not in (%sysfunc(tranwrd("&constraints_used",%str( ),%str(","))))));
|
set &idxinfo(
|
||||||
|
where=(
|
||||||
|
memname="&curds"
|
||||||
|
and unique='yes'
|
||||||
|
and indxname not in (
|
||||||
|
%sysfunc(tranwrd("&constraints_used",%str( ),%str(",")))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
file &fref mod;
|
file &fref mod;
|
||||||
by idxusage indxname;
|
by idxusage indxname;
|
||||||
/* ds=cats(libname,'.',memname); */
|
/* ds=cats(libname,'.',memname); */
|
||||||
|
|||||||
@@ -8,10 +8,11 @@
|
|||||||
- NAME Name of the base dataset column
|
- NAME Name of the base dataset column
|
||||||
- MAXLEN Maximum length of the data contained therein.
|
- MAXLEN Maximum length of the data contained therein.
|
||||||
|
|
||||||
Character fields may be allocated very large widths (eg 32000) of which the maximum
|
Character fields may be allocated very large widths (eg 32000) of which the
|
||||||
value is likely to be much narrower. This macro was designed to enable a HTML
|
maximum value is likely to be much narrower. This macro was designed to
|
||||||
table to be appropriately sized however this could be used as part of a data
|
enable a HTML table to be appropriately sized however this could be used as
|
||||||
audit to ensure we aren't over-sizing our tables in relation to the data therein.
|
part of a data audit to ensure we aren't over-sizing our tables in relation to
|
||||||
|
the data therein.
|
||||||
|
|
||||||
Numeric fields are converted using the relevant format to determine the width.
|
Numeric fields are converted using the relevant format to determine the width.
|
||||||
Usage:
|
Usage:
|
||||||
|
|||||||
75
base/mp_hashdataset.sas
Normal file
75
base/mp_hashdataset.sas
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
/**
|
||||||
|
@file
|
||||||
|
@brief Returns a unique hash for a dataset
|
||||||
|
@details Ignores metadata attributes, used only to hash values. Compared
|
||||||
|
datasets must be in the same order.
|
||||||
|
|
||||||
|
%mp_hashdataset(sashelp.class,outds=myhash)
|
||||||
|
|
||||||
|
data _null_;
|
||||||
|
set work.myhash;
|
||||||
|
put hashkey=;
|
||||||
|
run;
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
<h4> SAS Macros </h4>
|
||||||
|
@li mf_getattrn.sas
|
||||||
|
@li mf_getuniquename.sas
|
||||||
|
@li mf_getvarlist.sas
|
||||||
|
@li mf_getvartype.sas
|
||||||
|
|
||||||
|
@param [in] libds dataset to hash
|
||||||
|
@param [out] outds= (work.mf_hashdataset) The output dataset to create. This
|
||||||
|
will contain one column (hashkey) with one observation (a hex32.
|
||||||
|
representation of the input hash)
|
||||||
|
|hashkey:$32.|
|
||||||
|
|---|
|
||||||
|
|28ABC74ABFC45F50794237BA5566E6CA|
|
||||||
|
|
||||||
|
@version 9.2
|
||||||
|
@author Allan Bowe
|
||||||
|
**/
|
||||||
|
|
||||||
|
%macro mp_hashdataset(
|
||||||
|
libds,
|
||||||
|
outds=
|
||||||
|
)/*/STORE SOURCE*/;
|
||||||
|
%if %mf_getattrn(&libds,NLOBS)=0 %then %do;
|
||||||
|
%put %str(WARN)ING: Dataset &libds is empty;, or is not a dataset;
|
||||||
|
%end;
|
||||||
|
%else %if %mf_getattrn(&libds,NLOBS)<0 %then %do;
|
||||||
|
%put %str(ERR)OR: Dataset &libds is not a dataset;
|
||||||
|
%end;
|
||||||
|
%else %do;
|
||||||
|
%local keyvar /* roll up the md5 */
|
||||||
|
prevkeyvar /* retain prev record md5 */
|
||||||
|
lastvar /* last var in input ds */
|
||||||
|
varlist var i;
|
||||||
|
/* avoid naming conflict for hash key vars */
|
||||||
|
%let keyvar=%mf_getuniquename();
|
||||||
|
%let prevkeyvar=%mf_getuniquename();
|
||||||
|
%let lastvar=%mf_getuniquename();
|
||||||
|
%let varlist=%mf_getvarlist(&libds);
|
||||||
|
data &outds(rename=(&keyvar=hashkey) keep=&keyvar);
|
||||||
|
length &prevkeyvar &keyvar $32;
|
||||||
|
retain &prevkeyvar;
|
||||||
|
set &libds end=&lastvar;
|
||||||
|
/* hash should include previous row */
|
||||||
|
if _n_>1 then &keyvar=put(md5(&prevkeyvar
|
||||||
|
/* loop every column, hashing every individual value */
|
||||||
|
%do i=1 %to %sysfunc(countw(&varlist));
|
||||||
|
%let var=%scan(&varlist,&i,%str( ));
|
||||||
|
%if %mf_getvartype(&libds,&var)=C %then %do;
|
||||||
|
!!put(md5(trim(&var)),$hex32.)
|
||||||
|
%end;
|
||||||
|
%else %do;
|
||||||
|
!!put(md5(trim(put(&var*1,binary64.))),$hex32.)
|
||||||
|
%end;
|
||||||
|
%end;
|
||||||
|
),$hex32.);
|
||||||
|
&prevkeyvar=&keyvar;
|
||||||
|
if &lastvar then output;
|
||||||
|
run;
|
||||||
|
%end;
|
||||||
|
%mend;
|
||||||
@@ -4,7 +4,10 @@
|
|||||||
@details PROC JSON is faster but will produce errs like the ones below if
|
@details PROC JSON is faster but will produce errs like the ones below if
|
||||||
special chars are encountered.
|
special chars are encountered.
|
||||||
|
|
||||||
|
> ERROR: Some code points did not transcode.
|
||||||
|
|
||||||
> An object or array close is not valid at this point in the JSON text.
|
> An object or array close is not valid at this point in the JSON text.
|
||||||
|
|
||||||
> Date value out of range
|
> Date value out of range
|
||||||
|
|
||||||
If this happens, try running with ENGINE=DATASTEP.
|
If this happens, try running with ENGINE=DATASTEP.
|
||||||
@@ -14,7 +17,9 @@
|
|||||||
filename tmp temp;
|
filename tmp temp;
|
||||||
data class; set sashelp.class;run;
|
data class; set sashelp.class;run;
|
||||||
|
|
||||||
|
%mp_jsonout(OPEN,jref=tmp)
|
||||||
%mp_jsonout(OBJ,class,jref=tmp)
|
%mp_jsonout(OBJ,class,jref=tmp)
|
||||||
|
%mp_jsonout(CLOSE,jref=tmp)
|
||||||
|
|
||||||
data _null_;
|
data _null_;
|
||||||
infile tmp;
|
infile tmp;
|
||||||
@@ -27,18 +32,18 @@
|
|||||||
For more information see https://sasjs.io
|
For more information see https://sasjs.io
|
||||||
|
|
||||||
@param action Valid values:
|
@param action Valid values:
|
||||||
* OPEN - opens the JSON
|
@li OPEN - opens the JSON
|
||||||
* OBJ - sends a table with each row as an object
|
@li OBJ - sends a table with each row as an object
|
||||||
* ARR - sends a table with each row in an array
|
@li ARR - sends a table with each row in an array
|
||||||
* CLOSE - closes the JSON
|
@li CLOSE - closes the JSON
|
||||||
|
|
||||||
@param ds the dataset to send. Must be a work table.
|
@param ds the dataset to send. Must be a work table.
|
||||||
@param jref= the fileref to which to send the JSON
|
@param jref= the fileref to which to send the JSON
|
||||||
@param dslabel= the name to give the table in the exported JSON
|
@param dslabel= the name to give the table in the exported JSON
|
||||||
@param fmt= Whether to keep or strip formats from the table
|
@param fmt= Whether to keep or strip formats from the table
|
||||||
@param engine= Which engine to use to send the JSON, options are:
|
@param engine= Which engine to use to send the JSON, valid options are:
|
||||||
* PROCJSON (default)
|
@li PROCJSON (default)
|
||||||
* DATASTEP
|
@li DATASTEP (more reliable when data has non standard characters)
|
||||||
|
|
||||||
@param dbg= DEPRECATED - was used to conditionally add PRETTY to
|
@param dbg= DEPRECATED - was used to conditionally add PRETTY to
|
||||||
proc json but this can cause line truncation in large files.
|
proc json but this can cause line truncation in large files.
|
||||||
@@ -83,7 +88,8 @@
|
|||||||
%end;
|
%end;
|
||||||
data _null_;file &jref mod ;
|
data _null_;file &jref mod ;
|
||||||
put "["; call symputx('cols',0,'l');
|
put "["; call symputx('cols',0,'l');
|
||||||
proc sort data=sashelp.vcolumn(where=(libname='WORK' & memname="%upcase(&ds)"))
|
proc sort
|
||||||
|
data=sashelp.vcolumn(where=(libname='WORK' & memname="%upcase(&ds)"))
|
||||||
out=_data_;
|
out=_data_;
|
||||||
by varnum;
|
by varnum;
|
||||||
|
|
||||||
@@ -122,7 +128,8 @@
|
|||||||
%end;
|
%end;
|
||||||
%end;
|
%end;
|
||||||
run;
|
run;
|
||||||
/* write to temp loc to avoid _webout truncation - https://support.sas.com/kb/49/325.html */
|
/* write to temp loc to avoid _webout truncation
|
||||||
|
- https://support.sas.com/kb/49/325.html */
|
||||||
filename _sjs temp lrecl=131068 encoding='utf-8';
|
filename _sjs temp lrecl=131068 encoding='utf-8';
|
||||||
data _null_; file _sjs lrecl=131068 encoding='utf-8' mod;
|
data _null_; file _sjs lrecl=131068 encoding='utf-8' mod;
|
||||||
set &tempds;
|
set &tempds;
|
||||||
@@ -158,7 +165,7 @@
|
|||||||
%end;
|
%end;
|
||||||
|
|
||||||
%else %if &action=CLOSE %then %do;
|
%else %if &action=CLOSE %then %do;
|
||||||
data _null_;file &jref encoding='utf-8';
|
data _null_;file &jref encoding='utf-8' mod;
|
||||||
put "}";
|
put "}";
|
||||||
run;
|
run;
|
||||||
%end;
|
%end;
|
||||||
|
|||||||
98
base/mp_mdtablewrite.sas
Normal file
98
base/mp_mdtablewrite.sas
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
/**
|
||||||
|
@file
|
||||||
|
@brief Create a Markdown Table from a dataset
|
||||||
|
@details A markdown table is a simple table representation for use in
|
||||||
|
documents written in markdown format.
|
||||||
|
|
||||||
|
An online generator is available here:
|
||||||
|
https://www.tablesgenerator.com/markdown_tables
|
||||||
|
|
||||||
|
This structure is also used by the Macro Core library for documenting input/
|
||||||
|
output datasets, as well as the sasjs/cli tool for documenting inputs/outputs
|
||||||
|
for web services.
|
||||||
|
|
||||||
|
We take the standard definition one step further by embedding the informat
|
||||||
|
in the table header row, like so:
|
||||||
|
|
||||||
|
|var1:$|var2:best.|var3:date9.|
|
||||||
|
|---|---|---|
|
||||||
|
|some text|42|01JAN1960|
|
||||||
|
|blah|1|31DEC1999|
|
||||||
|
|
||||||
|
Which resolves to:
|
||||||
|
|
||||||
|
|var1:$|var2:best.|var3:date9.|
|
||||||
|
|---|---|---|
|
||||||
|
|some text|42|01JAN1960|
|
||||||
|
|blah|1|31DEC1999|
|
||||||
|
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
|
||||||
|
%mp_mdtablewrite(libds=sashelp.class,showlog=YES)
|
||||||
|
|
||||||
|
|
||||||
|
<h4> SAS Macros </h4>
|
||||||
|
@li mf_getvarlist.sas
|
||||||
|
@li mf_getvarformat.sas
|
||||||
|
|
||||||
|
@param [in] libds= the library / dataset to create or read from.
|
||||||
|
@param [out] fref= Fileref to contain the markdown. Default=mdtable.
|
||||||
|
@param [out] showlog= set to YES to show the markdown in the log. Default=NO.
|
||||||
|
|
||||||
|
@version 9.3
|
||||||
|
@author Allan Bowe
|
||||||
|
**/
|
||||||
|
|
||||||
|
%macro mp_mdtablewrite(
|
||||||
|
libds=,
|
||||||
|
fref=mdtable,
|
||||||
|
showlog=NO
|
||||||
|
)/*/STORE SOURCE*/;
|
||||||
|
|
||||||
|
/* check fileref is assigned */
|
||||||
|
%if %sysfunc(fileref(&fref)) > 0 %then %do;
|
||||||
|
filename &fref temp;
|
||||||
|
%end;
|
||||||
|
|
||||||
|
%local vars;
|
||||||
|
%let vars=%mf_getvarlist(&libds);
|
||||||
|
|
||||||
|
/* create the header row */
|
||||||
|
data _null_;
|
||||||
|
file &fref;
|
||||||
|
length line $32767;
|
||||||
|
put '|'
|
||||||
|
%local i var fmt;
|
||||||
|
%do i=1 %to %sysfunc(countw(&vars));
|
||||||
|
%let var=%scan(&vars,&i);
|
||||||
|
%let fmt=%mf_getvarformat(&libds,&var,force=1);
|
||||||
|
"&var:&fmt|"
|
||||||
|
%end;
|
||||||
|
;
|
||||||
|
put '|'
|
||||||
|
%do i=1 %to %sysfunc(countw(&vars));
|
||||||
|
"---|"
|
||||||
|
%end;
|
||||||
|
;
|
||||||
|
run;
|
||||||
|
|
||||||
|
/* write out the data */
|
||||||
|
data _null_;
|
||||||
|
file &fref mod dlm='|' lrecl=32767;
|
||||||
|
set &libds ;
|
||||||
|
length line $32767;
|
||||||
|
line=cats('|',%mf_getvarlist(&libds,dlm=%str(,'|',)),'|');
|
||||||
|
put line;
|
||||||
|
run;
|
||||||
|
|
||||||
|
%if %upcase(&showlog)=YES %then %do;
|
||||||
|
options ps=max;
|
||||||
|
data _null_;
|
||||||
|
infile &fref;
|
||||||
|
input;
|
||||||
|
putlog _infile_;
|
||||||
|
run;
|
||||||
|
%end;
|
||||||
|
|
||||||
|
%mend mp_mdtablewrite;
|
||||||
@@ -1,7 +1,8 @@
|
|||||||
/**
|
/**
|
||||||
@file
|
@file
|
||||||
@brief Reset an option to original value
|
@brief Reset an option to original value
|
||||||
@details Inspired by the SAS Jedi - https://blogs.sas.com/content/sastraining/2012/08/14/jedi-sas-tricks-reset-sas-system-options/
|
@details Inspired by the SAS Jedi -
|
||||||
|
https://blogs.sas.com/content/sastraining/2012/08/14/jedi-sas-tricks-reset-sas-system-options
|
||||||
Called as follows:
|
Called as follows:
|
||||||
|
|
||||||
options obs=30;
|
options obs=30;
|
||||||
|
|||||||
@@ -20,9 +20,10 @@
|
|||||||
@param ds= the dataset to search (leave blank to search entire library)
|
@param ds= the dataset to search (leave blank to search entire library)
|
||||||
@param string= the string value to search
|
@param string= the string value to search
|
||||||
@param numval= the numeric value to search (must be exact)
|
@param numval= the numeric value to search (must be exact)
|
||||||
@param outloc= the directory in which to create the output datasets with matching
|
@param outloc= the directory in which to create the output datasets with
|
||||||
rows. Will default to a subfolder in the WORK library.
|
matching rows. Will default to a subfolder in the WORK library.
|
||||||
@param outobs= set to a positive integer to restrict the number of observations
|
@param outobs= set to a positive integer to restrict the number of
|
||||||
|
observations
|
||||||
@param filter_text= add a (valid) filter clause to further filter the results
|
@param filter_text= add a (valid) filter clause to further filter the results
|
||||||
|
|
||||||
<h4> SAS Macros </h4>
|
<h4> SAS Macros </h4>
|
||||||
@@ -44,7 +45,8 @@
|
|||||||
,filter_text=%str(1=1)
|
,filter_text=%str(1=1)
|
||||||
)/*/STORE SOURCE*/;
|
)/*/STORE SOURCE*/;
|
||||||
|
|
||||||
%local table_list table table_num table colnum col start_tm check_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.);
|
%put process began at %sysfunc(datetime(),datetime19.);
|
||||||
|
|
||||||
%if &syscc ge 4 %then %do;
|
%if &syscc ge 4 %then %do;
|
||||||
@@ -101,7 +103,8 @@ proc sql
|
|||||||
%end;
|
%end;
|
||||||
%end;
|
%end;
|
||||||
);
|
);
|
||||||
%put Search query for &table took %sysevalf(%sysfunc(datetime())-&check_tm) seconds;
|
%put Search query for &table took
|
||||||
|
%sysevalf(%sysfunc(datetime())-&check_tm) seconds;
|
||||||
%if &sqlrc ne 0 %then %do;
|
%if &sqlrc ne 0 %then %do;
|
||||||
%put %str(WAR)NING: SQLRC=&sqlrc when processing &table;
|
%put %str(WAR)NING: SQLRC=&sqlrc when processing &table;
|
||||||
%return;
|
%return;
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
/**
|
/**
|
||||||
@file
|
@file
|
||||||
@brief Capture session start / finish times and request details
|
@brief Capture session start / finish times and request details
|
||||||
@details For details, see http://www.rawsas.com/2015/09/logging-of-stored-process-server.html.
|
@details For details, see
|
||||||
|
https://rawsas.com/event-logging-of-stored-process-server-sessions.
|
||||||
Requires a base table in the following structure (name can be changed):
|
Requires a base table in the following structure (name can be changed):
|
||||||
|
|
||||||
proc sql;
|
proc sql;
|
||||||
|
|||||||
@@ -6,7 +6,8 @@
|
|||||||
|
|
||||||
Usage:
|
Usage:
|
||||||
|
|
||||||
filename mc url "https://raw.githubusercontent.com/sasjs/core/main/all.sas";
|
filename mc url
|
||||||
|
"https://raw.githubusercontent.com/sasjs/core/main/all.sas";
|
||||||
%inc mc;
|
%inc mc;
|
||||||
|
|
||||||
%mp_streamfile(contenttype=csv,inloc=/some/where.txt,outname=myfile.txt)
|
%mp_streamfile(contenttype=csv,inloc=/some/where.txt,outname=myfile.txt)
|
||||||
@@ -65,13 +66,15 @@
|
|||||||
%else %if &contentype=XLSX %then %do;
|
%else %if &contentype=XLSX %then %do;
|
||||||
%if &platform=SASMETA %then %do;
|
%if &platform=SASMETA %then %do;
|
||||||
data _null_;
|
data _null_;
|
||||||
rc=stpsrv_header('Content-type','application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
|
rc=stpsrv_header('Content-type',
|
||||||
|
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
|
||||||
rc=stpsrv_header('Content-disposition',"attachment; filename=&outname");
|
rc=stpsrv_header('Content-disposition',"attachment; filename=&outname");
|
||||||
run;
|
run;
|
||||||
%end;
|
%end;
|
||||||
%else %if &platform=SASVIYA %then %do;
|
%else %if &platform=SASVIYA %then %do;
|
||||||
filename _webout filesrvc parenturi="&SYS_JES_JOB_URI" name='_webout.xls'
|
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";
|
contentdisp="attachment; filename=&outname";
|
||||||
%end;
|
%end;
|
||||||
%end;
|
%end;
|
||||||
|
|||||||
@@ -8,8 +8,11 @@
|
|||||||
|
|
||||||
Credits:
|
Credits:
|
||||||
|
|
||||||
* Roger Deangelis, https://communities.sas.com/t5/SAS-Programming/listing-all-files-within-a-directory-and-subdirectories/m-p/332616/highlight/true#M74887
|
Roger Deangelis:
|
||||||
* Tom, https://communities.sas.com/t5/SAS-Programming/listing-all-files-of-all-types-from-all-subdirectories/m-p/334113/highlight/true#M75419
|
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 dir= Directory to be scanned (default=/tmp)
|
||||||
|
|||||||
@@ -117,7 +117,8 @@ run;
|
|||||||
%end;
|
%end;
|
||||||
%else %if &engine=REMOTE %then %do;
|
%else %if &engine=REMOTE %then %do;
|
||||||
data x;
|
data x;
|
||||||
length rcCon rcProp rc k 3 uriCon uriProp PropertyValue PropertyName Delimiter $256 properties $2048;
|
length rcCon rcProp rc k 3 uriCon uriProp PropertyValue PropertyName
|
||||||
|
Delimiter $256 properties $2048;
|
||||||
retain properties;
|
retain properties;
|
||||||
rcCon = metadata_getnasn("&liburi", "LibraryConnection", 1, uriCon);
|
rcCon = metadata_getnasn("&liburi", "LibraryConnection", 1, uriCon);
|
||||||
|
|
||||||
@@ -129,7 +130,8 @@ run;
|
|||||||
rc = metadata_getattr(uriProp , "DefaultValue",PropertyValue);
|
rc = metadata_getattr(uriProp , "DefaultValue",PropertyValue);
|
||||||
rc = metadata_getattr(uriProp , "PropertyName",PropertyName);
|
rc = metadata_getattr(uriProp , "PropertyName",PropertyName);
|
||||||
rc = metadata_getattr(uriProp , "Delimiter",Delimiter);
|
rc = metadata_getattr(uriProp , "Delimiter",Delimiter);
|
||||||
properties = trim(properties) !! " " !! trim(PropertyName) !! trim(Delimiter) !! trim(PropertyValue);
|
properties = trim(properties) !! " " !! trim(PropertyName)
|
||||||
|
!! trim(Delimiter) !! trim(PropertyValue);
|
||||||
output;
|
output;
|
||||||
k+1;
|
k+1;
|
||||||
rcProp = metadata_getnasn(uriCon, "Properties", k, uriProp);
|
rcProp = metadata_getnasn(uriCon, "Properties", k, uriProp);
|
||||||
@@ -170,7 +172,8 @@ run;
|
|||||||
else if value='Connection.OLE.Property.PROVIDER.Name.xmlKey.txt' then do;
|
else if value='Connection.OLE.Property.PROVIDER.Name.xmlKey.txt' then do;
|
||||||
rc4=metadata_getattr(conprop_uri,'DefaultValue',provider);
|
rc4=metadata_getattr(conprop_uri,'DefaultValue',provider);
|
||||||
end;
|
end;
|
||||||
else if value='Connection.OLE.Property.PROPERTIES.Name.xmlKey.txt' then do;
|
else if value='Connection.OLE.Property.PROPERTIES.Name.xmlKey.txt' then
|
||||||
|
do;
|
||||||
rc5=metadata_getattr(conprop_uri,'DefaultValue',properties);
|
rc5=metadata_getattr(conprop_uri,'DefaultValue',properties);
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
@@ -357,7 +360,8 @@ run;
|
|||||||
call symputx('authdomain',authdomain,'l');
|
call symputx('authdomain',authdomain,'l');
|
||||||
|
|
||||||
/* path */
|
/* path */
|
||||||
rc=metadata_getprop(assocuri1,'Connection.Oracle.Property.PATH.Name.xmlKey.txt',path);
|
rc=metadata_getprop(assocuri1,
|
||||||
|
'Connection.Oracle.Property.PATH.Name.xmlKey.txt',path);
|
||||||
call symputx('path',path,'l');
|
call symputx('path',path,'l');
|
||||||
|
|
||||||
/* schema */
|
/* schema */
|
||||||
@@ -366,14 +370,16 @@ run;
|
|||||||
call symputx('schema',schema,'l');
|
call symputx('schema',schema,'l');
|
||||||
run;
|
run;
|
||||||
%put NOTE: Executing the following:/; %put NOTE-;
|
%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;
|
||||||
|
%put NOTE- authdomain=&authdomain;
|
||||||
%put NOTE-;
|
%put NOTE-;
|
||||||
libname &libref ORACLE path=&path schema=&schema authdomain=&authdomain;
|
libname &libref ORACLE path=&path schema=&schema authdomain=&authdomain;
|
||||||
%end;
|
%end;
|
||||||
%else %if &engine=SQLSVR %then %do;
|
%else %if &engine=SQLSVR %then %do;
|
||||||
%put NOTE: Obtaining &engine library details;
|
%put NOTE: Obtaining &engine library details;
|
||||||
data _null;
|
data _null;
|
||||||
length assocuri1 assocuri2 assocuri3 authdomain path schema userid passwd $256;
|
length assocuri1 assocuri2 assocuri3 authdomain path schema userid
|
||||||
|
passwd $256;
|
||||||
call missing (of _all_);
|
call missing (of _all_);
|
||||||
|
|
||||||
rc=metadata_getnasn("&liburi",'DefaultLogin',1,assocuri1);
|
rc=metadata_getnasn("&liburi",'DefaultLogin',1,assocuri1);
|
||||||
@@ -384,7 +390,8 @@ run;
|
|||||||
|
|
||||||
/* path */
|
/* path */
|
||||||
rc=metadata_getnasn("&liburi",'LibraryConnection',1,assocuri2);
|
rc=metadata_getnasn("&liburi",'LibraryConnection',1,assocuri2);
|
||||||
rc=metadata_getprop(assocuri2,'Connection.SQL.Property.Datasrc.Name.xmlKey.txt',path);
|
rc=metadata_getprop(assocuri2,
|
||||||
|
'Connection.SQL.Property.Datasrc.Name.xmlKey.txt',path);
|
||||||
call symputx('path',path,'l');
|
call symputx('path',path,'l');
|
||||||
|
|
||||||
/* schema */
|
/* schema */
|
||||||
@@ -394,7 +401,8 @@ run;
|
|||||||
run;
|
run;
|
||||||
|
|
||||||
%put NOTE: Executing the following:/; %put NOTE-;
|
%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 ;
|
||||||
|
%put NOTE- user="&user" pass="XXX";
|
||||||
%put NOTE-;
|
%put NOTE-;
|
||||||
|
|
||||||
libname &libref SQLSVR datasrc=&path schema=&schema user="&user" pass="&pass";
|
libname &libref SQLSVR datasrc=&path schema=&schema user="&user" pass="&pass";
|
||||||
@@ -402,7 +410,8 @@ run;
|
|||||||
%else %if &engine=TERADATA %then %do;
|
%else %if &engine=TERADATA %then %do;
|
||||||
%put NOTE: Obtaining &engine library details;
|
%put NOTE: Obtaining &engine library details;
|
||||||
data _null;
|
data _null;
|
||||||
length assocuri1 assocuri2 assocuri3 authdomain path schema userid passwd $256;
|
length assocuri1 assocuri2 assocuri3 authdomain path schema userid
|
||||||
|
passwd $256;
|
||||||
call missing (of _all_);
|
call missing (of _all_);
|
||||||
|
|
||||||
/* get auth domain */
|
/* get auth domain */
|
||||||
@@ -421,7 +430,8 @@ run;
|
|||||||
|
|
||||||
/* path */
|
/* path */
|
||||||
rc=metadata_getnasn("&liburi",'LibraryConnection',1,assocuri2);
|
rc=metadata_getnasn("&liburi",'LibraryConnection',1,assocuri2);
|
||||||
rc=metadata_getprop(assocuri2,'Connection.Teradata.Property.SERVER.Name.xmlKey.txt',path);
|
rc=metadata_getprop(assocuri2,
|
||||||
|
'Connection.Teradata.Property.SERVER.Name.xmlKey.txt',path);
|
||||||
call symputx('path',path,'l');
|
call symputx('path',path,'l');
|
||||||
|
|
||||||
/* schema */
|
/* schema */
|
||||||
@@ -431,7 +441,8 @@ run;
|
|||||||
run;
|
run;
|
||||||
|
|
||||||
%put NOTE: Executing the following:/; %put NOTE-;
|
%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 ;
|
||||||
|
%put NOTe- authdomain=&authdomain;
|
||||||
%put NOTE-;
|
%put NOTE-;
|
||||||
|
|
||||||
libname &libref TERADATA server=&path schema=&schema authdomain=&authdomain;
|
libname &libref TERADATA server=&path schema=&schema authdomain=&authdomain;
|
||||||
|
|||||||
@@ -14,7 +14,8 @@
|
|||||||
@li mp_abort.sas
|
@li mp_abort.sas
|
||||||
|
|
||||||
@param libref the libref (not name) of the metadata library
|
@param libref the libref (not name) of the metadata library
|
||||||
@param mAbort= If not assigned, HARD will call %mp_abort(), SOFT will silently return
|
@param mAbort= If not assigned, HARD will call %mp_abort(), SOFT will
|
||||||
|
silently return
|
||||||
|
|
||||||
@returns libname statement
|
@returns libname statement
|
||||||
|
|
||||||
|
|||||||
@@ -13,7 +13,9 @@
|
|||||||
,params= name1=value1
name2=value2
emptyvalue=
|
,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).
|
@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> SAS Macros </h4>
|
<h4> SAS Macros </h4>
|
||||||
@li mp_abort.sas
|
@li mp_abort.sas
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ data _null_;
|
|||||||
|
|
||||||
* must have a starting slash ;
|
* must have a starting slash ;
|
||||||
if ( substr(folderPath,1,1) ne '/' ) then do;
|
if ( substr(folderPath,1,1) ne '/' ) then do;
|
||||||
put "%str(ERR)OR: &sysmacroname PATH parameter value must have starting slash";
|
put "%str(ERR)OR: &sysmacroname PATH param value must have starting slash";
|
||||||
stop;
|
stop;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
@@ -123,9 +123,10 @@ run;
|
|||||||
%local inmeta;
|
%local inmeta;
|
||||||
%put creating: &path;
|
%put creating: &path;
|
||||||
%let inmeta=<AddMetadata><Reposid>$METAREPOSITORY</Reposid><Metadata>
|
%let inmeta=<AddMetadata><Reposid>$METAREPOSITORY</Reposid><Metadata>
|
||||||
<Tree Name='&child' PublicType='Folder' TreeType='BIP Folder' UsageVersion='1000000'>
|
<Tree Name='&child' PublicType='Folder' TreeType='BIP Folder'
|
||||||
<ParentTree><Tree ObjRef='&parentFolderObjId'/></ParentTree></Tree></Metadata>
|
UsageVersion='1000000'><ParentTree><Tree ObjRef='&parentFolderObjId'/>
|
||||||
<NS>SAS</NS><Flags>268435456</Flags></AddMetadata>;
|
</ParentTree></Tree></Metadata><NS>SAS</NS><Flags>268435456</Flags>
|
||||||
|
</AddMetadata>;
|
||||||
|
|
||||||
proc metadata in="&inmeta" out=__newdir verbose;
|
proc metadata in="&inmeta" out=__newdir verbose;
|
||||||
run ;
|
run ;
|
||||||
|
|||||||
@@ -166,7 +166,7 @@ filename &frefout temp;
|
|||||||
putlog (_all_)(=);
|
putlog (_all_)(=);
|
||||||
run;
|
run;
|
||||||
%if &checktype ne Prototype %then %do;
|
%if &checktype ne Prototype %then %do;
|
||||||
%put %str(ERR)OR: Prototype (Library.SAS.Prototype.Name.xmlKey.txt) not found!;
|
%put %str(ERR)OR: Prototype Library.SAS.Prototype.Name.xmlKey.txt not found;
|
||||||
%return;
|
%return;
|
||||||
%end;
|
%end;
|
||||||
|
|
||||||
|
|||||||
@@ -210,7 +210,8 @@ run;
|
|||||||
rc3=METADATA_SETATTR(prompturi, 'GroupType','2');
|
rc3=METADATA_SETATTR(prompturi, 'GroupType','2');
|
||||||
rc4=METADATA_SETATTR(prompturi, 'Name','Parameters');
|
rc4=METADATA_SETATTR(prompturi, 'Name','Parameters');
|
||||||
rc5=METADATA_SETATTR(prompturi, 'PublicType','Embedded:PromptGroup');
|
rc5=METADATA_SETATTR(prompturi, 'PublicType','Embedded:PromptGroup');
|
||||||
GroupInfo="<PromptGroup promptId='PromptGroup_%sysfunc(datetime())_&sysprocessid'"
|
GroupInfo=
|
||||||
|
"<PromptGroup promptId='PromptGroup_%sysfunc(datetime())_&sysprocessid'"
|
||||||
!!" version='1.0'><Label><Text xml:lang='en-GB'>Parameters</Text>"
|
!!" version='1.0'><Label><Text xml:lang='en-GB'>Parameters</Text>"
|
||||||
!!"</Label></PromptGroup>";
|
!!"</Label></PromptGroup>";
|
||||||
rc6 = METADATA_SETATTR(prompturi, 'GroupInfo',groupinfo);
|
rc6 = METADATA_SETATTR(prompturi, 'GroupInfo',groupinfo);
|
||||||
@@ -375,8 +376,6 @@ run;
|
|||||||
*/
|
*/
|
||||||
%mm_updatestpsourcecode(stp=&tree/&stpname
|
%mm_updatestpsourcecode(stp=&tree/&stpname
|
||||||
,stpcode="&directory/&filename"
|
,stpcode="&directory/&filename"
|
||||||
,frefin=&frefin.
|
|
||||||
,frefout=&frefout.
|
|
||||||
,mdebug=&mdebug
|
,mdebug=&mdebug
|
||||||
,minify=&minify)
|
,minify=&minify)
|
||||||
|
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ Usage:
|
|||||||
%webout(OBJ,example2) * Object format, easier to work with ;
|
%webout(OBJ,example2) * Object format, easier to work with ;
|
||||||
%webout(CLOSE)
|
%webout(CLOSE)
|
||||||
;;;;
|
;;;;
|
||||||
%mm_createwebservice(path=/Public/app/common,name=appInit,code=ft15f001,replace=YES)
|
%mm_createwebservice(path=/Public/app/common,name=appInit)
|
||||||
|
|
||||||
<h4> SAS Macros </h4>
|
<h4> SAS Macros </h4>
|
||||||
@li mm_createstp.sas
|
@li mm_createstp.sas
|
||||||
@@ -119,7 +119,8 @@ data _null_;
|
|||||||
put ' %end; ';
|
put ' %end; ';
|
||||||
put ' data _null_;file &jref mod ; ';
|
put ' data _null_;file &jref mod ; ';
|
||||||
put ' put "["; call symputx(''cols'',0,''l''); ';
|
put ' put "["; call symputx(''cols'',0,''l''); ';
|
||||||
put ' proc sort data=sashelp.vcolumn(where=(libname=''WORK'' & memname="%upcase(&ds)")) ';
|
put ' proc sort ';
|
||||||
|
put ' data=sashelp.vcolumn(where=(libname=''WORK'' & memname="%upcase(&ds)")) ';
|
||||||
put ' out=_data_; ';
|
put ' out=_data_; ';
|
||||||
put ' by varnum; ';
|
put ' by varnum; ';
|
||||||
put ' ';
|
put ' ';
|
||||||
@@ -158,7 +159,8 @@ data _null_;
|
|||||||
put ' %end; ';
|
put ' %end; ';
|
||||||
put ' %end; ';
|
put ' %end; ';
|
||||||
put ' run; ';
|
put ' run; ';
|
||||||
put ' /* write to temp loc to avoid _webout truncation - https://support.sas.com/kb/49/325.html */ ';
|
put ' /* write to temp loc to avoid _webout truncation ';
|
||||||
|
put ' - https://support.sas.com/kb/49/325.html */ ';
|
||||||
put ' filename _sjs temp lrecl=131068 encoding=''utf-8''; ';
|
put ' filename _sjs temp lrecl=131068 encoding=''utf-8''; ';
|
||||||
put ' data _null_; file _sjs lrecl=131068 encoding=''utf-8'' mod; ';
|
put ' data _null_; file _sjs lrecl=131068 encoding=''utf-8'' mod; ';
|
||||||
put ' set &tempds; ';
|
put ' set &tempds; ';
|
||||||
@@ -194,7 +196,7 @@ data _null_;
|
|||||||
put '%end; ';
|
put '%end; ';
|
||||||
put ' ';
|
put ' ';
|
||||||
put '%else %if &action=CLOSE %then %do; ';
|
put '%else %if &action=CLOSE %then %do; ';
|
||||||
put ' data _null_;file &jref encoding=''utf-8''; ';
|
put ' data _null_;file &jref encoding=''utf-8'' mod; ';
|
||||||
put ' put "}"; ';
|
put ' put "}"; ';
|
||||||
put ' run; ';
|
put ' run; ';
|
||||||
put '%end; ';
|
put '%end; ';
|
||||||
@@ -235,8 +237,16 @@ data _null_;
|
|||||||
put '%else %if &action=OPEN %then %do; ';
|
put '%else %if &action=OPEN %then %do; ';
|
||||||
put ' /* fix encoding */ ';
|
put ' /* fix encoding */ ';
|
||||||
put ' OPTIONS NOBOMFILE; ';
|
put ' OPTIONS NOBOMFILE; ';
|
||||||
|
put ' ';
|
||||||
|
put ' /** ';
|
||||||
|
put ' * check engine type to avoid the below err message: ';
|
||||||
|
put ' * > Function is only valid for filerefs using the CACHE access method. ';
|
||||||
|
put ' */ ';
|
||||||
put ' data _null_; ';
|
put ' data _null_; ';
|
||||||
|
put ' set sashelp.vextfl(where=(fileref="_WEBOUT")); ';
|
||||||
|
put ' if xengine=''STREAM'' then do; ';
|
||||||
put ' rc=stpsrv_header(''Content-type'',"text/html; encoding=utf-8"); ';
|
put ' rc=stpsrv_header(''Content-type'',"text/html; encoding=utf-8"); ';
|
||||||
|
put ' end; ';
|
||||||
put ' run; ';
|
put ' run; ';
|
||||||
put ' ';
|
put ' ';
|
||||||
put ' /* setup json */ ';
|
put ' /* setup json */ ';
|
||||||
@@ -250,17 +260,10 @@ data _null_;
|
|||||||
put '%end; ';
|
put '%end; ';
|
||||||
put ' ';
|
put ' ';
|
||||||
put '%else %if &action=ARR or &action=OBJ %then %do; ';
|
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 ' %mp_jsonout(&action,&ds,dslabel=&dslabel,fmt=&fmt ';
|
||||||
put ' ,engine=DATASTEP,dbg=%str(&_debug) ';
|
put ' ,engine=DATASTEP,dbg=%str(&_debug) ';
|
||||||
put ' ) ';
|
put ' ) ';
|
||||||
put '%end; ';
|
put '%end; ';
|
||||||
put '%end; ';
|
|
||||||
put '%else %if &action=CLOSE %then %do; ';
|
put '%else %if &action=CLOSE %then %do; ';
|
||||||
put ' %if %str(&_debug) ge 131 %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 ' /* if debug mode, send back first 10 records of each work table also */ ';
|
||||||
@@ -275,14 +278,14 @@ data _null_;
|
|||||||
put ' i+1; ';
|
put ' i+1; ';
|
||||||
put ' call symputx(''wt''!!left(i),name,''l''); ';
|
put ' call symputx(''wt''!!left(i),name,''l''); ';
|
||||||
put ' call symputx(''wtcnt'',i,''l''); ';
|
put ' call symputx(''wtcnt'',i,''l''); ';
|
||||||
put ' data _null_; file &fref encoding=''utf-8''; ';
|
put ' data _null_; file &fref mod encoding=''utf-8''; ';
|
||||||
put ' put ",""WORK"":{"; ';
|
put ' put ",""WORK"":{"; ';
|
||||||
put ' %do i=1 %to &wtcnt; ';
|
put ' %do i=1 %to &wtcnt; ';
|
||||||
put ' %let wt=&&wt&i; ';
|
put ' %let wt=&&wt&i; ';
|
||||||
put ' proc contents noprint data=&wt ';
|
put ' proc contents noprint data=&wt ';
|
||||||
put ' out=_data_ (keep=name type length format:); ';
|
put ' out=_data_ (keep=name type length format:); ';
|
||||||
put ' run;%let tempds=%scan(&syslast,2,.); ';
|
put ' run;%let tempds=%scan(&syslast,2,.); ';
|
||||||
put ' data _null_; file &fref encoding=''utf-8''; ';
|
put ' data _null_; file &fref mod encoding=''utf-8''; ';
|
||||||
put ' dsid=open("WORK.&wt",''is''); ';
|
put ' dsid=open("WORK.&wt",''is''); ';
|
||||||
put ' nlobs=attrn(dsid,''NLOBS''); ';
|
put ' nlobs=attrn(dsid,''NLOBS''); ';
|
||||||
put ' nvars=attrn(dsid,''NVARS''); ';
|
put ' nvars=attrn(dsid,''NVARS''); ';
|
||||||
@@ -293,10 +296,10 @@ data _null_;
|
|||||||
put ' put '',"nvars":'' nvars; ';
|
put ' put '',"nvars":'' nvars; ';
|
||||||
put ' %mp_jsonout(OBJ,&tempds,jref=&fref,dslabel=colattrs,engine=DATASTEP) ';
|
put ' %mp_jsonout(OBJ,&tempds,jref=&fref,dslabel=colattrs,engine=DATASTEP) ';
|
||||||
put ' %mp_jsonout(OBJ,&wt,jref=&fref,dslabel=first10rows,engine=DATASTEP) ';
|
put ' %mp_jsonout(OBJ,&wt,jref=&fref,dslabel=first10rows,engine=DATASTEP) ';
|
||||||
put ' data _null_; file &fref encoding=''utf-8''; ';
|
put ' data _null_; file &fref mod encoding=''utf-8''; ';
|
||||||
put ' put "}"; ';
|
put ' put "}"; ';
|
||||||
put ' %end; ';
|
put ' %end; ';
|
||||||
put ' data _null_; file &fref encoding=''utf-8''; ';
|
put ' data _null_; file &fref mod encoding=''utf-8''; ';
|
||||||
put ' put "}"; ';
|
put ' put "}"; ';
|
||||||
put ' run; ';
|
put ' run; ';
|
||||||
put ' %end; ';
|
put ' %end; ';
|
||||||
|
|||||||
@@ -21,8 +21,8 @@
|
|||||||
)/*/STORE SOURCE*/;
|
)/*/STORE SOURCE*/;
|
||||||
|
|
||||||
%if %length(&outds)>30 %then %do;
|
%if %length(&outds)>30 %then %do;
|
||||||
%put %str(ERR)OR: Temp tables are created with the &outds prefix, which therefore
|
%put %str(ERR)OR: Temp tables are created with the &outds prefix, which
|
||||||
needs to be 30 characters or less;
|
therefore needs to be 30 characters or less;
|
||||||
%return;
|
%return;
|
||||||
%end;
|
%end;
|
||||||
%if %index(&outds,'.')>0 %then %do;
|
%if %index(&outds,'.')>0 %then %do;
|
||||||
|
|||||||
@@ -39,8 +39,10 @@ data &outds (keep=directoryuri name directoryname directorydesc );
|
|||||||
do while
|
do while
|
||||||
(metadata_getnobj("omsobj:Directory?@Id contains '.'",__i,directoryuri)>0);
|
(metadata_getnobj("omsobj:Directory?@Id contains '.'",__i,directoryuri)>0);
|
||||||
%end; %else %do;
|
%end; %else %do;
|
||||||
do while
|
do while(
|
||||||
(metadata_getnobj("omsobj:Directory?@DirectoryName='&path'",__i,directoryuri)>0);
|
metadata_getnobj("omsobj:Directory?@DirectoryName='&path'",__i,directoryuri)
|
||||||
|
>0
|
||||||
|
);
|
||||||
%end;
|
%end;
|
||||||
__rc1=metadata_getattr(directoryuri, "Name", name);
|
__rc1=metadata_getattr(directoryuri, "Name", name);
|
||||||
__rc2=metadata_getattr(directoryuri, "DirectoryName", directoryname);
|
__rc2=metadata_getattr(directoryuri, "DirectoryName", directoryname);
|
||||||
|
|||||||
@@ -11,7 +11,8 @@
|
|||||||
%mm_getfoldermembers(root=/User Folders/&sysuserid, outds=usercontent)
|
%mm_getfoldermembers(root=/User Folders/&sysuserid, outds=usercontent)
|
||||||
|
|
||||||
@param [in] root= the parent folder under which to return all contents
|
@param [in] root= the parent folder under which to return all contents
|
||||||
@param [out] outds= the dataset to create that contains the list of directories
|
@param [out] outds= the dataset to create that contains the list of
|
||||||
|
directories
|
||||||
@param [in] mDebug= set to 1 to show debug messages in the log
|
@param [in] mDebug= set to 1 to show debug messages in the log
|
||||||
|
|
||||||
<h4> Data Outputs </h4>
|
<h4> Data Outputs </h4>
|
||||||
|
|||||||
@@ -13,7 +13,8 @@
|
|||||||
options notes source;
|
options notes source;
|
||||||
|
|
||||||
@param [in] root= the parent folder under which to return all contents
|
@param [in] root= the parent folder under which to return all contents
|
||||||
@param [out] outds= the dataset to create that contains the list of directories
|
@param [out] outds= the dataset to create that contains the list of
|
||||||
|
directories
|
||||||
@param [in] mDebug= set to 1 to show debug messages in the log
|
@param [in] mDebug= set to 1 to show debug messages in the log
|
||||||
|
|
||||||
<h4> SAS Macros </h4>
|
<h4> SAS Macros </h4>
|
||||||
|
|||||||
@@ -11,7 +11,8 @@
|
|||||||
|
|
||||||
@param [in] user= the metadata user to return groups for. Leave blank for all
|
@param [in] user= the metadata user to return groups for. Leave blank for all
|
||||||
groups.
|
groups.
|
||||||
@param [in] repo= the metadata repository that contains the user/group information
|
@param [in] repo= the metadata repository that contains the user/group
|
||||||
|
information
|
||||||
@param [in] mDebug= set to 1 to show debug messages in the log
|
@param [in] mDebug= set to 1 to show debug messages in the log
|
||||||
@param [out] outds= the dataset to create that contains the list of groups
|
@param [out] outds= the dataset to create that contains the list of groups
|
||||||
|
|
||||||
@@ -39,7 +40,8 @@
|
|||||||
%&mD.put Executing mm_getGroups.sas;
|
%&mD.put Executing mm_getGroups.sas;
|
||||||
%&mD.put _local_;
|
%&mD.put _local_;
|
||||||
|
|
||||||
/* on some sites, user / group info is in a different metadata repo to the default */
|
/* on some sites, user / group info is in a different metadata repo to the
|
||||||
|
default */
|
||||||
%if &oldrepo ne &repo %then %do;
|
%if &oldrepo ne &repo %then %do;
|
||||||
options metarepository=&repo;
|
options metarepository=&repo;
|
||||||
%end;
|
%end;
|
||||||
|
|||||||
129
meta/mm_getlibmetadiffs.sas
Normal file
129
meta/mm_getlibmetadiffs.sas
Normal file
@@ -0,0 +1,129 @@
|
|||||||
|
/**
|
||||||
|
@file
|
||||||
|
@brief Compares the metadata of a library with the physical tables
|
||||||
|
@details Creates a series of output tables that show the differences between
|
||||||
|
metadata and physical tables.
|
||||||
|
Each output can be created with an optional prefix.
|
||||||
|
|
||||||
|
Credit - Paul Homes
|
||||||
|
https://platformadmin.com/blogs/paul/2012/11/sas-proc-metalib-ods-output
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
|
||||||
|
%* create (and assign) a library for testing purposes ;
|
||||||
|
%mm_createlibrary(
|
||||||
|
libname=My Temp Library,
|
||||||
|
libref=XXTEMPXX,
|
||||||
|
tree=/User Folders/&sysuserid,
|
||||||
|
directory=%sysfunc(pathname(work))
|
||||||
|
)
|
||||||
|
|
||||||
|
%* create some tables;
|
||||||
|
data work.table1 table2 table3;
|
||||||
|
a=1;b='two';c=3;
|
||||||
|
run;
|
||||||
|
|
||||||
|
%* register the tables;
|
||||||
|
proc metalib;
|
||||||
|
omr=(library="My Temp Library");
|
||||||
|
report(type=detail);
|
||||||
|
update_rule (delete);
|
||||||
|
run;
|
||||||
|
|
||||||
|
%* modify the tables;
|
||||||
|
proc sql;
|
||||||
|
drop table table3;
|
||||||
|
alter table table2 drop c;
|
||||||
|
alter table table2 add d num;
|
||||||
|
|
||||||
|
%* run the macro;
|
||||||
|
%mm_getlibmetadiffs(libname=My Temp Library)
|
||||||
|
|
||||||
|
%* delete the library ;
|
||||||
|
%mm_deletelibrary(name=My Temp Library)
|
||||||
|
|
||||||
|
The program will create four output tables, with the following structure (and
|
||||||
|
example data):
|
||||||
|
|
||||||
|
#### &prefix.added
|
||||||
|
|name:$32.|metaID:$17.|SAStabName:$32.|
|
||||||
|
|---|---|---|
|
||||||
|
|||DATA1|
|
||||||
|
|
||||||
|
#### &prefix.deleted
|
||||||
|
|name:$32.|metaID:$17.|SAStabName:$32.|
|
||||||
|
|---|---|---|
|
||||||
|
|TABLE3|A5XLSNXI.BK0001HO|TABLE3|
|
||||||
|
|
||||||
|
#### &prefix.updated
|
||||||
|
|tabName:$32.|tabMetaID:$17.|SAStabName:$32.|metaName:$32.|metaID:$17.|sasname:$32.|metaType:$16.|change:$64.|
|
||||||
|
|---|---|---|---|---|---|---|---|
|
||||||
|
|TABLE2|A5XLSNXI.BK0001HN|TABLE2|c|A5XLSNXI.BM000MA9|c|Column|Deleted|
|
||||||
|
||||d||d|Column|Added|
|
||||||
|
|
||||||
|
#### &prefix.meta
|
||||||
|
|Label1:$28.|cValue1:$1.|nValue1:D12.3|
|
||||||
|
|---|---|---|
|
||||||
|
|Total tables analyzed|4|4|
|
||||||
|
|Tables to be Updated|1|1|
|
||||||
|
|Tables to be Deleted|1|1|
|
||||||
|
|Tables to be Added|1|1|
|
||||||
|
|Tables matching data source|1|1|
|
||||||
|
|Tables not processed|0|0|
|
||||||
|
|
||||||
|
If you are interested in more functionality like this (checking the health of
|
||||||
|
SAS metadata and your SAS 9 environment) then do contact [Allan Bowe](
|
||||||
|
https://www.linkedin.com/in/allanbowe) for details of our SAS 9 Health Check
|
||||||
|
service.
|
||||||
|
|
||||||
|
Our system scan will perform hundreds of checks to identify common issues,
|
||||||
|
such as dangling metadata, embedded passwords, security issues and more.
|
||||||
|
|
||||||
|
@param [in] libname= the metadata name of the library to be compared
|
||||||
|
@param [out] outlib= The output library in which to store the output tables.
|
||||||
|
Default=WORK.
|
||||||
|
@param [out] prefix The prefix for the four tables created. Default=metadiff.
|
||||||
|
|
||||||
|
@version 9.3
|
||||||
|
@author Allan Bowe
|
||||||
|
|
||||||
|
**/
|
||||||
|
|
||||||
|
%macro mm_getlibmetadiffs(
|
||||||
|
libname= ,
|
||||||
|
prefix=metadiff,
|
||||||
|
outlib=work
|
||||||
|
)/*/STORE SOURCE*/;
|
||||||
|
|
||||||
|
/* create tempds */
|
||||||
|
data;run;
|
||||||
|
%local tempds;
|
||||||
|
%let tempds=&syslast;
|
||||||
|
|
||||||
|
/* save options */
|
||||||
|
proc optsave out=&tempds;
|
||||||
|
run;
|
||||||
|
|
||||||
|
options VALIDVARNAME=ANY VALIDMEMNAME=EXTEND;
|
||||||
|
|
||||||
|
ods output
|
||||||
|
factoid1=&outlib..&prefix.meta
|
||||||
|
updtab=&outlib..&prefix.updated
|
||||||
|
addtab=&outlib..&prefix.added
|
||||||
|
deltab=&outlib..&prefix.deleted
|
||||||
|
;
|
||||||
|
|
||||||
|
proc metalib;
|
||||||
|
omr=(library="&libname");
|
||||||
|
noexec;
|
||||||
|
report(type=detail);
|
||||||
|
update_rule (delete);
|
||||||
|
run;
|
||||||
|
|
||||||
|
ods output close;
|
||||||
|
|
||||||
|
/* restore options */
|
||||||
|
proc optload data=&tempds;
|
||||||
|
run;
|
||||||
|
|
||||||
|
%mend mm_getlibmetadiffs;
|
||||||
@@ -46,7 +46,8 @@ filename sxlemap temp;
|
|||||||
data _null_;
|
data _null_;
|
||||||
file sxlemap;
|
file sxlemap;
|
||||||
put '<SXLEMAP version="1.2" name="SASObjects"><TABLE name="SASObjects">';
|
put '<SXLEMAP version="1.2" name="SASObjects"><TABLE name="SASObjects">';
|
||||||
put "<TABLE-PATH syntax='XPath'>/GetMetadataObjects/Objects/&type</TABLE-PATH>";
|
put "<TABLE-PATH syntax='XPath'>/GetMetadataObjects/Objects/&type";
|
||||||
|
put "</TABLE-PATH>";
|
||||||
put '<COLUMN name="id">';
|
put '<COLUMN name="id">';
|
||||||
put "<PATH syntax='XPath'>/GetMetadataObjects/Objects/&type/@Id</PATH>";
|
put "<PATH syntax='XPath'>/GetMetadataObjects/Objects/&type/@Id</PATH>";
|
||||||
put "<TYPE>character</TYPE><DATATYPE>string</DATATYPE><LENGTH>200</LENGTH>";
|
put "<TYPE>character</TYPE><DATATYPE>string</DATATYPE><LENGTH>200</LENGTH>";
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
/**
|
/**
|
||||||
@file mm_getpublictypes.sas
|
@file mm_getpublictypes.sas
|
||||||
@brief Creates a dataset with all deployable public types
|
@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
|
@details More info:
|
||||||
|
https://support.sas.com/documentation/cdl/en/bisag/65422/HTML/default/viewer.htm#n1nkrdzsq5iunln18bk2236istkb.htm
|
||||||
|
|
||||||
Usage:
|
Usage:
|
||||||
|
|
||||||
|
|||||||
@@ -44,61 +44,76 @@ filename sxlemap temp;
|
|||||||
data _null_;
|
data _null_;
|
||||||
file sxlemap;
|
file sxlemap;
|
||||||
put '<SXLEMAP version="1.2" name="SASRepos"><TABLE name="SASRepos">';
|
put '<SXLEMAP version="1.2" name="SASRepos"><TABLE name="SASRepos">';
|
||||||
put "<TABLE-PATH syntax='XPath'>/GetRepositories/Repositories/Repository</TABLE-PATH>";
|
put "<TABLE-PATH syntax='XPath'>/GetRepositories/Repositories/Repository";
|
||||||
|
put "</TABLE-PATH>";
|
||||||
put '<COLUMN name="id">';
|
put '<COLUMN name="id">';
|
||||||
put "<PATH syntax='XPath'>/GetRepositories/Repositories/Repository/@Id</PATH>";
|
put "<PATH syntax='XPath'>/GetRepositories/Repositories/Repository/@Id";
|
||||||
|
put "</PATH>";
|
||||||
put "<TYPE>character</TYPE><DATATYPE>string</DATATYPE><LENGTH>200</LENGTH>";
|
put "<TYPE>character</TYPE><DATATYPE>string</DATATYPE><LENGTH>200</LENGTH>";
|
||||||
put '</COLUMN>';
|
put '</COLUMN>';
|
||||||
put '<COLUMN name="name">';
|
put '<COLUMN name="name">';
|
||||||
put "<PATH syntax='XPath'>/GetRepositories/Repositories/Repository/@Name</PATH>";
|
put "<PATH syntax='XPath'>/GetRepositories/Repositories/Repository/@Name";
|
||||||
|
put "</PATH>";
|
||||||
put "<TYPE>character</TYPE><DATATYPE>string</DATATYPE><LENGTH>200</LENGTH>";
|
put "<TYPE>character</TYPE><DATATYPE>string</DATATYPE><LENGTH>200</LENGTH>";
|
||||||
put '</COLUMN>';
|
put '</COLUMN>';
|
||||||
put '<COLUMN name="desc">';
|
put '<COLUMN name="desc">';
|
||||||
put "<PATH syntax='XPath'>/GetRepositories/Repositories/Repository/@Desc</PATH>";
|
put "<PATH syntax='XPath'>/GetRepositories/Repositories/Repository/@Desc";
|
||||||
|
put "</PATH>";
|
||||||
put "<TYPE>character</TYPE><DATATYPE>string</DATATYPE><LENGTH>200</LENGTH>";
|
put "<TYPE>character</TYPE><DATATYPE>string</DATATYPE><LENGTH>200</LENGTH>";
|
||||||
put '</COLUMN>';
|
put '</COLUMN>';
|
||||||
put '<COLUMN name="DefaultNS">';
|
put '<COLUMN name="DefaultNS">';
|
||||||
put "<PATH syntax='XPath'>/GetRepositories/Repositories/Repository/@DefaultNS</PATH>";
|
put "<PATH syntax='XPath'>";
|
||||||
|
put "/GetRepositories/Repositories/Repository/@DefaultNS</PATH>";
|
||||||
put "<TYPE>character</TYPE><DATATYPE>string</DATATYPE><LENGTH>200</LENGTH>";
|
put "<TYPE>character</TYPE><DATATYPE>string</DATATYPE><LENGTH>200</LENGTH>";
|
||||||
put '</COLUMN>';
|
put '</COLUMN>';
|
||||||
put '<COLUMN name="RepositoryType">';
|
put '<COLUMN name="RepositoryType">';
|
||||||
put "<PATH syntax='XPath'>/GetRepositories/Repositories/Repository/@RepositoryType</PATH>";
|
put "<PATH syntax='XPath'>";
|
||||||
|
put "/GetRepositories/Repositories/Repository/@RepositoryType</PATH>";
|
||||||
put "<TYPE>character</TYPE><DATATYPE>string</DATATYPE><LENGTH>20</LENGTH>";
|
put "<TYPE>character</TYPE><DATATYPE>string</DATATYPE><LENGTH>20</LENGTH>";
|
||||||
put '</COLUMN>';
|
put '</COLUMN>';
|
||||||
put '<COLUMN name="RepositoryFormat">';
|
put '<COLUMN name="RepositoryFormat">';
|
||||||
put "<PATH syntax='XPath'>/GetRepositories/Repositories/Repository/@RepositoryFormat</PATH>";
|
put "<PATH syntax='XPath'>";
|
||||||
|
put "/GetRepositories/Repositories/Repository/@RepositoryFormat</PATH>";
|
||||||
put "<TYPE>character</TYPE><DATATYPE>string</DATATYPE><LENGTH>10</LENGTH>";
|
put "<TYPE>character</TYPE><DATATYPE>string</DATATYPE><LENGTH>10</LENGTH>";
|
||||||
put '</COLUMN>';
|
put '</COLUMN>';
|
||||||
put '<COLUMN name="Access">';
|
put '<COLUMN name="Access">';
|
||||||
put "<PATH syntax='XPath'>/GetRepositories/Repositories/Repository/@Access</PATH>";
|
put "<PATH syntax='XPath'>";
|
||||||
|
put "/GetRepositories/Repositories/Repository/@Access</PATH>";
|
||||||
put "<TYPE>character</TYPE><DATATYPE>string</DATATYPE><LENGTH>16</LENGTH>";
|
put "<TYPE>character</TYPE><DATATYPE>string</DATATYPE><LENGTH>16</LENGTH>";
|
||||||
put '</COLUMN>';
|
put '</COLUMN>';
|
||||||
put '<COLUMN name="CurrentAccess">';
|
put '<COLUMN name="CurrentAccess">';
|
||||||
put "<PATH syntax='XPath'>/GetRepositories/Repositories/Repository/@CurrentAccess</PATH>";
|
put "<PATH syntax='XPath'>";
|
||||||
|
put "/GetRepositories/Repositories/Repository/@CurrentAccess</PATH>";
|
||||||
put "<TYPE>character</TYPE><DATATYPE>string</DATATYPE><LENGTH>16</LENGTH>";
|
put "<TYPE>character</TYPE><DATATYPE>string</DATATYPE><LENGTH>16</LENGTH>";
|
||||||
put '</COLUMN>';
|
put '</COLUMN>';
|
||||||
put '<COLUMN name="PauseState">';
|
put '<COLUMN name="PauseState">';
|
||||||
put "<PATH syntax='XPath'>/GetRepositories/Repositories/Repository/@PauseState</PATH>";
|
put "<PATH syntax='XPath'>";
|
||||||
|
put "/GetRepositories/Repositories/Repository/@PauseState</PATH>";
|
||||||
put "<TYPE>character</TYPE><DATATYPE>string</DATATYPE><LENGTH>16</LENGTH>";
|
put "<TYPE>character</TYPE><DATATYPE>string</DATATYPE><LENGTH>16</LENGTH>";
|
||||||
put '</COLUMN>';
|
put '</COLUMN>';
|
||||||
put '<COLUMN name="Path">';
|
put '<COLUMN name="Path">';
|
||||||
put "<PATH syntax='XPath'>/GetRepositories/Repositories/Repository/@Path</PATH>";
|
put "<PATH syntax='XPath'>/GetRepositories/Repositories/Repository/@Path";
|
||||||
|
put "</PATH>";
|
||||||
put "<TYPE>character</TYPE><DATATYPE>string</DATATYPE><LENGTH>256</LENGTH>";
|
put "<TYPE>character</TYPE><DATATYPE>string</DATATYPE><LENGTH>256</LENGTH>";
|
||||||
put '</COLUMN>';
|
put '</COLUMN>';
|
||||||
put '<COLUMN name="Engine">';
|
put '<COLUMN name="Engine">';
|
||||||
put "<PATH syntax='XPath'>/GetRepositories/Repositories/Repository/@Engine</PATH>";
|
put "<PATH syntax='XPath'>/GetRepositories/Repositories/Repository/@Engine";
|
||||||
|
put "</PATH>";
|
||||||
put "<TYPE>character</TYPE><DATATYPE>string</DATATYPE><LENGTH>8</LENGTH>";
|
put "<TYPE>character</TYPE><DATATYPE>string</DATATYPE><LENGTH>8</LENGTH>";
|
||||||
put '</COLUMN>';
|
put '</COLUMN>';
|
||||||
put '<COLUMN name="Options">';
|
put '<COLUMN name="Options">';
|
||||||
put "<PATH syntax='XPath'>/GetRepositories/Repositories/Repository/@Options</PATH>";
|
put "<PATH syntax='XPath'>/GetRepositories/Repositories/Repository/@Options";
|
||||||
|
put "</PATH>";
|
||||||
put "<TYPE>character</TYPE><DATATYPE>string</DATATYPE><LENGTH>32</LENGTH>";
|
put "<TYPE>character</TYPE><DATATYPE>string</DATATYPE><LENGTH>32</LENGTH>";
|
||||||
put '</COLUMN>';
|
put '</COLUMN>';
|
||||||
put '<COLUMN name="MetadataCreated">';
|
put '<COLUMN name="MetadataCreated">';
|
||||||
put "<PATH syntax='XPath'>/GetRepositories/Repositories/Repository/@MetadataCreated</PATH>";
|
put "<PATH syntax='XPath'>";
|
||||||
|
put "/GetRepositories/Repositories/Repository/@MetadataCreated</PATH>";
|
||||||
put "<TYPE>character</TYPE><DATATYPE>string</DATATYPE><LENGTH>24</LENGTH>";
|
put "<TYPE>character</TYPE><DATATYPE>string</DATATYPE><LENGTH>24</LENGTH>";
|
||||||
put '</COLUMN>';
|
put '</COLUMN>';
|
||||||
put '<COLUMN name="MetadataUpdated">';
|
put '<COLUMN name="MetadataUpdated">';
|
||||||
put "<PATH syntax='XPath'>/GetRepositories/Repositories/Repository/@MetadataUpdated</PATH>";
|
put "<PATH syntax='XPath'>";
|
||||||
|
put "/GetRepositories/Repositories/Repository/@MetadataUpdated</PATH>";
|
||||||
put "<TYPE>character</TYPE><DATATYPE>string</DATATYPE><LENGTH>24</LENGTH>";
|
put "<TYPE>character</TYPE><DATATYPE>string</DATATYPE><LENGTH>24</LENGTH>";
|
||||||
put '</COLUMN>';
|
put '</COLUMN>';
|
||||||
put '</TABLE></SXLEMAP>';
|
put '</TABLE></SXLEMAP>';
|
||||||
|
|||||||
@@ -44,15 +44,19 @@ filename sxlemap temp;
|
|||||||
data _null_;
|
data _null_;
|
||||||
file sxlemap;
|
file sxlemap;
|
||||||
put '<SXLEMAP version="1.2" name="roles"><TABLE name="roles">';
|
put '<SXLEMAP version="1.2" name="roles"><TABLE name="roles">';
|
||||||
put "<TABLE-PATH syntax='XPath'>/GetMetadataObjects/Objects/IdentityGroup</TABLE-PATH>";
|
put "<TABLE-PATH syntax='XPath'>/GetMetadataObjects/Objects/IdentityGroup";
|
||||||
|
put "</TABLE-PATH>";
|
||||||
put '<COLUMN name="roleuri">';
|
put '<COLUMN name="roleuri">';
|
||||||
put "<PATH syntax='XPath'>/GetMetadataObjects/Objects/IdentityGroup/@Id</PATH>";
|
put "<PATH syntax='XPath'>/GetMetadataObjects/Objects/IdentityGroup/@Id";
|
||||||
|
put "</PATH>";
|
||||||
put "<TYPE>character</TYPE><DATATYPE>string</DATATYPE><LENGTH>32</LENGTH>";
|
put "<TYPE>character</TYPE><DATATYPE>string</DATATYPE><LENGTH>32</LENGTH>";
|
||||||
put '</COLUMN><COLUMN name="rolename">';
|
put '</COLUMN><COLUMN name="rolename">';
|
||||||
put "<PATH syntax='XPath'>/GetMetadataObjects/Objects/IdentityGroup/@Name</PATH>";
|
put "<PATH syntax='XPath'>/GetMetadataObjects/Objects/IdentityGroup/@Name";
|
||||||
|
put "</PATH>";
|
||||||
put "<TYPE>character</TYPE><DATATYPE>string</DATATYPE><LENGTH>256</LENGTH>";
|
put "<TYPE>character</TYPE><DATATYPE>string</DATATYPE><LENGTH>256</LENGTH>";
|
||||||
put '</COLUMN><COLUMN name="roledesc">';
|
put '</COLUMN><COLUMN name="roledesc">';
|
||||||
put "<PATH syntax='XPath'>/GetMetadataObjects/Objects/IdentityGroup/@Desc</PATH>";
|
put "<PATH syntax='XPath'>/GetMetadataObjects/Objects/IdentityGroup/@Desc";
|
||||||
|
put "</PATH>";
|
||||||
put "<TYPE>character</TYPE><DATATYPE>string</DATATYPE><LENGTH>500</LENGTH>";
|
put "<TYPE>character</TYPE><DATATYPE>string</DATATYPE><LENGTH>500</LENGTH>";
|
||||||
put '</COLUMN></TABLE></SXLEMAP>';
|
put '</COLUMN></TABLE></SXLEMAP>';
|
||||||
run;
|
run;
|
||||||
|
|||||||
@@ -23,9 +23,10 @@
|
|||||||
combine with the <code>tree=</code> parameter.
|
combine with the <code>tree=</code> parameter.
|
||||||
@param outds= the dataset to create that contains the list of stps.
|
@param outds= the dataset to create that contains the list of stps.
|
||||||
@param mDebug= set to 1 to show debug messages in the log
|
@param mDebug= set to 1 to show debug messages in the log
|
||||||
@param showDesc= provide a non blank value to return stored process descriptions
|
@param showDesc= provide a non blank value to return stored process
|
||||||
@param showUsageVersion= provide a non blank value to return the UsageVersion. This
|
descriptions
|
||||||
is either 1000000 (type 1, 9.2) or 2000000 (type2, 9.3 onwards).
|
@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
|
@returns outds dataset containing the following columns
|
||||||
- stpuri
|
- stpuri
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
/**
|
/**
|
||||||
@file mm_gettableid.sas
|
@file mm_gettableid.sas
|
||||||
@brief Get the metadata id for a particular table
|
@brief Get the metadata id for a particular table
|
||||||
@details Provide a libref and table name to return the corresponding metadata id
|
@details Provide a libref and table name to return the corresponding metadata
|
||||||
in an output datataset.
|
in an output datataset.
|
||||||
|
|
||||||
Usage:
|
Usage:
|
||||||
|
|||||||
@@ -48,7 +48,8 @@ filename sxlemap temp;
|
|||||||
data _null_;
|
data _null_;
|
||||||
file sxlemap;
|
file sxlemap;
|
||||||
put '<SXLEMAP version="1.2" name="SASObjects"><TABLE name="SASObjects">';
|
put '<SXLEMAP version="1.2" name="SASObjects"><TABLE name="SASObjects">';
|
||||||
put "<TABLE-PATH syntax='XPath'>/GetMetadataObjects/Objects/Person</TABLE-PATH>";
|
put "<TABLE-PATH syntax='XPath'>/GetMetadataObjects/Objects/Person";
|
||||||
|
put "</TABLE-PATH>";
|
||||||
put '<COLUMN name="uri">';
|
put '<COLUMN name="uri">';
|
||||||
put "<PATH syntax='XPath'>/GetMetadataObjects/Objects/Person/@Id</PATH>";
|
put "<PATH syntax='XPath'>/GetMetadataObjects/Objects/Person/@Id</PATH>";
|
||||||
put "<TYPE>character</TYPE><DATATYPE>string</DATATYPE><LENGTH>32</LENGTH>";
|
put "<TYPE>character</TYPE><DATATYPE>string</DATATYPE><LENGTH>32</LENGTH>";
|
||||||
|
|||||||
@@ -12,7 +12,8 @@
|
|||||||
Usage:
|
Usage:
|
||||||
|
|
||||||
%* import the macros (or make them available some other way);
|
%* import the macros (or make them available some other way);
|
||||||
filename mc url "https://raw.githubusercontent.com/sasjs/core/main/all.sas";
|
filename mc url
|
||||||
|
"https://raw.githubusercontent.com/sasjs/core/main/all.sas";
|
||||||
%inc mc;
|
%inc mc;
|
||||||
|
|
||||||
%* create sample text file as input to the macro;
|
%* create sample text file as input to the macro;
|
||||||
@@ -92,7 +93,8 @@
|
|||||||
%let port=%sysfunc(getoption(metaport));
|
%let port=%sysfunc(getoption(metaport));
|
||||||
%let platform_object_path=%mf_loc(POF);
|
%let platform_object_path=%mf_loc(POF);
|
||||||
|
|
||||||
%let connx_string=%str(-host &host -port &port -user &mmxuser -password &mmxpass);
|
%let connx_string=%str(-host &host -port &port -user &mmxuser %trim(
|
||||||
|
)-password &mmxpass);
|
||||||
|
|
||||||
%mm_tree(root=%str(&metaloc) ,types=EXPORTABLE ,outds=exportable)
|
%mm_tree(root=%str(&metaloc) ,types=EXPORTABLE ,outds=exportable)
|
||||||
|
|
||||||
|
|||||||
@@ -15,7 +15,8 @@
|
|||||||
Usage:
|
Usage:
|
||||||
|
|
||||||
%* load macros;
|
%* load macros;
|
||||||
filename mc url "https://raw.githubusercontent.com/sasjs/core/main/all.sas";
|
filename mc url
|
||||||
|
"https://raw.githubusercontent.com/sasjs/core/main/all.sas";
|
||||||
%inc mc;
|
%inc mc;
|
||||||
|
|
||||||
%* export everything;
|
%* export everything;
|
||||||
@@ -104,7 +105,8 @@ filename sxlemap temp;
|
|||||||
data _null_;
|
data _null_;
|
||||||
file sxlemap;
|
file sxlemap;
|
||||||
put '<SXLEMAP version="1.2" name="SASObjects"><TABLE name="SASObjects">';
|
put '<SXLEMAP version="1.2" name="SASObjects"><TABLE name="SASObjects">';
|
||||||
put "<TABLE-PATH syntax='XPath'>/GetMetadataObjects/Objects/Tree</TABLE-PATH>";
|
put "<TABLE-PATH syntax='XPath'>/GetMetadataObjects/Objects/Tree";
|
||||||
|
put "</TABLE-PATH>";
|
||||||
put '<COLUMN name="pathuri">';
|
put '<COLUMN name="pathuri">';
|
||||||
put "<PATH syntax='XPath'>/GetMetadataObjects/Objects/Tree/@Id</PATH>";
|
put "<PATH syntax='XPath'>/GetMetadataObjects/Objects/Tree/@Id</PATH>";
|
||||||
put "<TYPE>character</TYPE><DATATYPE>string</DATATYPE><LENGTH>64</LENGTH>";
|
put "<TYPE>character</TYPE><DATATYPE>string</DATATYPE><LENGTH>64</LENGTH>";
|
||||||
|
|||||||
@@ -10,8 +10,8 @@
|
|||||||
|
|
||||||
|
|
||||||
@param target= full path to the STP being deleted
|
@param target= full path to the STP being deleted
|
||||||
@param type= Either WKS or STP depending on whether Workspace or Stored Process
|
@param type= Either WKS or STP depending on whether Workspace or
|
||||||
type required
|
Stored Process type required
|
||||||
|
|
||||||
@version 9.4
|
@version 9.4
|
||||||
@author Allan Bowe
|
@author Allan Bowe
|
||||||
@@ -51,7 +51,8 @@ data _null_;
|
|||||||
n+1;
|
n+1;
|
||||||
rc=metadata_getattr(uri,"Name",name);
|
rc=metadata_getattr(uri,"Name",name);
|
||||||
if name='Stored Process' then do;
|
if name='Stored Process' then do;
|
||||||
rc = METADATA_SETATTR(uri,'StoredText','<?xml version="1.0" encoding="UTF-8"?>'
|
rc = METADATA_SETATTR(uri,'StoredText'
|
||||||
|
,'<?xml version="1.0" encoding="UTF-8"?>'
|
||||||
!!'<StoredProcess><ServerContext LogicalServerType="'!!"&newtype"
|
!!'<StoredProcess><ServerContext LogicalServerType="'!!"&newtype"
|
||||||
!!'" OtherAllowed="false"/><ResultCapabilities Package="false" '
|
!!'" OtherAllowed="false"/><ResultCapabilities Package="false" '
|
||||||
!!' Streaming="true"/><OutputParameters/></StoredProcess>');
|
!!' Streaming="true"/><OutputParameters/></StoredProcess>');
|
||||||
|
|||||||
@@ -68,8 +68,16 @@
|
|||||||
%else %if &action=OPEN %then %do;
|
%else %if &action=OPEN %then %do;
|
||||||
/* fix encoding */
|
/* fix encoding */
|
||||||
OPTIONS NOBOMFILE;
|
OPTIONS NOBOMFILE;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* check engine type to avoid the below err message:
|
||||||
|
* > Function is only valid for filerefs using the CACHE access method.
|
||||||
|
*/
|
||||||
data _null_;
|
data _null_;
|
||||||
|
set sashelp.vextfl(where=(fileref="_WEBOUT"));
|
||||||
|
if xengine='STREAM' then do;
|
||||||
rc=stpsrv_header('Content-type',"text/html; encoding=utf-8");
|
rc=stpsrv_header('Content-type',"text/html; encoding=utf-8");
|
||||||
|
end;
|
||||||
run;
|
run;
|
||||||
|
|
||||||
/* setup json */
|
/* setup json */
|
||||||
@@ -83,17 +91,10 @@
|
|||||||
%end;
|
%end;
|
||||||
|
|
||||||
%else %if &action=ARR or &action=OBJ %then %do;
|
%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
|
%mp_jsonout(&action,&ds,dslabel=&dslabel,fmt=&fmt
|
||||||
,engine=DATASTEP,dbg=%str(&_debug)
|
,engine=DATASTEP,dbg=%str(&_debug)
|
||||||
)
|
)
|
||||||
%end;
|
%end;
|
||||||
%end;
|
|
||||||
%else %if &action=CLOSE %then %do;
|
%else %if &action=CLOSE %then %do;
|
||||||
%if %str(&_debug) ge 131 %then %do;
|
%if %str(&_debug) ge 131 %then %do;
|
||||||
/* if debug mode, send back first 10 records of each work table also */
|
/* if debug mode, send back first 10 records of each work table also */
|
||||||
@@ -108,14 +109,14 @@
|
|||||||
i+1;
|
i+1;
|
||||||
call symputx('wt'!!left(i),name,'l');
|
call symputx('wt'!!left(i),name,'l');
|
||||||
call symputx('wtcnt',i,'l');
|
call symputx('wtcnt',i,'l');
|
||||||
data _null_; file &fref encoding='utf-8';
|
data _null_; file &fref mod encoding='utf-8';
|
||||||
put ",""WORK"":{";
|
put ",""WORK"":{";
|
||||||
%do i=1 %to &wtcnt;
|
%do i=1 %to &wtcnt;
|
||||||
%let wt=&&wt&i;
|
%let wt=&&wt&i;
|
||||||
proc contents noprint data=&wt
|
proc contents noprint data=&wt
|
||||||
out=_data_ (keep=name type length format:);
|
out=_data_ (keep=name type length format:);
|
||||||
run;%let tempds=%scan(&syslast,2,.);
|
run;%let tempds=%scan(&syslast,2,.);
|
||||||
data _null_; file &fref encoding='utf-8';
|
data _null_; file &fref mod encoding='utf-8';
|
||||||
dsid=open("WORK.&wt",'is');
|
dsid=open("WORK.&wt",'is');
|
||||||
nlobs=attrn(dsid,'NLOBS');
|
nlobs=attrn(dsid,'NLOBS');
|
||||||
nvars=attrn(dsid,'NVARS');
|
nvars=attrn(dsid,'NVARS');
|
||||||
@@ -126,10 +127,10 @@
|
|||||||
put ',"nvars":' nvars;
|
put ',"nvars":' nvars;
|
||||||
%mp_jsonout(OBJ,&tempds,jref=&fref,dslabel=colattrs,engine=DATASTEP)
|
%mp_jsonout(OBJ,&tempds,jref=&fref,dslabel=colattrs,engine=DATASTEP)
|
||||||
%mp_jsonout(OBJ,&wt,jref=&fref,dslabel=first10rows,engine=DATASTEP)
|
%mp_jsonout(OBJ,&wt,jref=&fref,dslabel=first10rows,engine=DATASTEP)
|
||||||
data _null_; file &fref encoding='utf-8';
|
data _null_; file &fref mod encoding='utf-8';
|
||||||
put "}";
|
put "}";
|
||||||
%end;
|
%end;
|
||||||
data _null_; file &fref encoding='utf-8';
|
data _null_; file &fref mod encoding='utf-8';
|
||||||
put "}";
|
put "}";
|
||||||
run;
|
run;
|
||||||
%end;
|
%end;
|
||||||
|
|||||||
@@ -20,7 +20,8 @@ Usage:
|
|||||||
run;
|
run;
|
||||||
|
|
||||||
filename outref "%sysfunc(pathname(work))";
|
filename outref "%sysfunc(pathname(work))";
|
||||||
%mmx_spkexport(metaloc=%str(/30.Projects/3001.Internal/300115.DataController/dc1)
|
%mmx_spkexport(
|
||||||
|
metaloc=%str(/30.Projects/3001.Internal/300115.DataController/dc1)
|
||||||
,secureref=tmp
|
,secureref=tmp
|
||||||
,outspkpath=%str(/tmp)
|
,outspkpath=%str(/tmp)
|
||||||
)
|
)
|
||||||
@@ -56,7 +57,8 @@ Usage:
|
|||||||
/* get creds */
|
/* get creds */
|
||||||
%inc &secureref/nosource;
|
%inc &secureref/nosource;
|
||||||
|
|
||||||
%let connx_string=%str(-host &host -port &port -user '&mmxuser' -password '&mmxpass');
|
%let connx_string=
|
||||||
|
%str(-host &host -port &port -user '&mmxuser' -password '&mmxpass');
|
||||||
|
|
||||||
%mm_tree(root=%str(&metaloc) ,types=EXPORTABLE ,outds=exportable)
|
%mm_tree(root=%str(&metaloc) ,types=EXPORTABLE ,outds=exportable)
|
||||||
|
|
||||||
|
|||||||
1688
package-lock.json
generated
1688
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
13
package.json
13
package.json
@@ -28,10 +28,15 @@
|
|||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"test": "echo \"Error: no test specified\" && exit 1",
|
"test": "echo \"Error: no test specified\" && exit 1",
|
||||||
"docs": "sasjs doc && ./sasjs/utils/build.sh",
|
"docs": "sasjs doc && ./sasjs/utils/build.sh"
|
||||||
"postinstall": "node-git-hooks"
|
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"devDependencies": {
|
||||||
"node-git-hooks": "^1.0.5"
|
"@sasjs/cli": "^2.14.2",
|
||||||
|
"ghooks": "^2.0.4"
|
||||||
|
},
|
||||||
|
"config": {
|
||||||
|
"ghooks": {
|
||||||
|
"pre-commit": "sasjs lint"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,12 @@
|
|||||||
{
|
{
|
||||||
"$schema": "https://cli.sasjs.io/sasjsconfig-schema.json",
|
"$schema": "https://cli.sasjs.io/sasjsconfig-schema.json",
|
||||||
"macroFolders": ["base", "meta", "metax", "viya", "lua"],
|
"macroFolders": [
|
||||||
|
"base",
|
||||||
|
"meta",
|
||||||
|
"metax",
|
||||||
|
"viya",
|
||||||
|
"lua"
|
||||||
|
],
|
||||||
"docConfig": {
|
"docConfig": {
|
||||||
"displayMacroCore": false,
|
"displayMacroCore": false,
|
||||||
"enableLineage": false,
|
"enableLineage": false,
|
||||||
@@ -9,5 +15,30 @@
|
|||||||
"logo": "Macro_core_website_1.png",
|
"logo": "Macro_core_website_1.png",
|
||||||
"readMe": "../../README.md"
|
"readMe": "../../README.md"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"serviceConfig": {
|
||||||
|
"initProgram": "tests/testinit.sas"
|
||||||
|
},
|
||||||
|
"defaultTarget": "viya",
|
||||||
|
"targets": [
|
||||||
|
{
|
||||||
|
"name": "viya",
|
||||||
|
"serverUrl": "https://sas.analytium.co.uk",
|
||||||
|
"serverType": "SASVIYA",
|
||||||
|
"appLoc": "/Public/temp/macrocore",
|
||||||
|
"serviceConfig": {
|
||||||
|
"serviceFolders": [
|
||||||
|
"tests/base",
|
||||||
|
"tests/viya"
|
||||||
|
],
|
||||||
|
"macroVars": {
|
||||||
|
"mcTestAppLoc": "/Public/temp/macrocore"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"deployConfig": {
|
||||||
|
"deployServicePack": true
|
||||||
|
},
|
||||||
|
"contextName": "SAS Job Execution compute context"
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
132
tests/base/mp_filtercheck.test.sas
Normal file
132
tests/base/mp_filtercheck.test.sas
Normal file
@@ -0,0 +1,132 @@
|
|||||||
|
/**
|
||||||
|
@file
|
||||||
|
@brief Testing mp_filtercheck macro
|
||||||
|
|
||||||
|
<h4> SAS Macros </h4>
|
||||||
|
@li mp_filtercheck.sas
|
||||||
|
@li mp_assertdsobs.sas
|
||||||
|
|
||||||
|
**/
|
||||||
|
|
||||||
|
|
||||||
|
/* valid filter */
|
||||||
|
data work.inds;
|
||||||
|
infile datalines4 dsd;
|
||||||
|
input GROUP_LOGIC:$3. SUBGROUP_LOGIC:$3. SUBGROUP_ID:8. VARIABLE_NM:$32.
|
||||||
|
OPERATOR_NM:$10. RAW_VALUE:$32767.;
|
||||||
|
datalines4;
|
||||||
|
AND,AND,1,AGE,=,12
|
||||||
|
AND,AND,1,SEX,<=,"'M'"
|
||||||
|
AND,OR,2,Name,NOT IN,"('Jane','Alfred')"
|
||||||
|
AND,OR,2,Weight,>=,77.7
|
||||||
|
AND,OR,2,Weight,NE,77.7
|
||||||
|
;;;;
|
||||||
|
run;
|
||||||
|
|
||||||
|
%mp_filtercheck(work.inds,
|
||||||
|
targetds=sashelp.class,
|
||||||
|
outds=work.badrecords,
|
||||||
|
abort=NO
|
||||||
|
)
|
||||||
|
%let syscc=0;
|
||||||
|
%mp_assertdsobs(work.badrecords,
|
||||||
|
desc=Valid filter query,
|
||||||
|
test=EMPTY,
|
||||||
|
outds=work.test_results
|
||||||
|
)
|
||||||
|
|
||||||
|
/* invalid column */
|
||||||
|
data work.inds;
|
||||||
|
infile datalines4 dsd;
|
||||||
|
input GROUP_LOGIC:$3. SUBGROUP_LOGIC:$3. SUBGROUP_ID:8. VARIABLE_NM:$32.
|
||||||
|
OPERATOR_NM:$10. RAW_VALUE:$32767.;
|
||||||
|
datalines4;
|
||||||
|
AND,AND,1,invalid,=,12
|
||||||
|
AND,AND,1,SEX,<=,"'M'"
|
||||||
|
AND,OR,2,Name,NOT IN,"('Jane','Alfred')"
|
||||||
|
AND,OR,2,Weight,>=,7
|
||||||
|
;;;;
|
||||||
|
run;
|
||||||
|
%mp_filtercheck(work.inds,
|
||||||
|
targetds=sashelp.class,
|
||||||
|
outds=work.badrecords,
|
||||||
|
abort=NO
|
||||||
|
)
|
||||||
|
%let syscc=0;
|
||||||
|
%mp_assertdsobs(work.badrecords,
|
||||||
|
desc=Invalid column name,
|
||||||
|
test=HASOBS,
|
||||||
|
outds=work.test_results
|
||||||
|
)
|
||||||
|
|
||||||
|
/* invalid raw value */
|
||||||
|
data work.inds;
|
||||||
|
infile datalines4 dsd;
|
||||||
|
input GROUP_LOGIC:$3. SUBGROUP_LOGIC:$3. SUBGROUP_ID:8. VARIABLE_NM:$32.
|
||||||
|
OPERATOR_NM:$10. RAW_VALUE:$32767.;
|
||||||
|
datalines4;
|
||||||
|
AND,OR,2,Name,NOT IN,"(''''Jane','Alfred')"
|
||||||
|
;;;;
|
||||||
|
run;
|
||||||
|
|
||||||
|
%mp_filtercheck(work.inds,
|
||||||
|
targetds=sashelp.class,
|
||||||
|
outds=work.badrecords,
|
||||||
|
abort=NO
|
||||||
|
)
|
||||||
|
%let syscc=0;
|
||||||
|
%mp_assertdsobs(work.badrecords,
|
||||||
|
desc=Invalid raw value,
|
||||||
|
test=HASOBS,
|
||||||
|
outds=work.test_results
|
||||||
|
)
|
||||||
|
|
||||||
|
/* Code injection - column name */
|
||||||
|
data work.inds;
|
||||||
|
infile datalines4 dsd;
|
||||||
|
input GROUP_LOGIC:$3. SUBGROUP_LOGIC:$3. SUBGROUP_ID:8. VARIABLE_NM:$32.
|
||||||
|
OPERATOR_NM:$10. RAW_VALUE:$32767.;
|
||||||
|
datalines4;
|
||||||
|
AND,AND,1,%abort,=,12
|
||||||
|
AND,OR,2,Weight,>=,7
|
||||||
|
;;;;
|
||||||
|
run;
|
||||||
|
|
||||||
|
%mp_filtercheck(work.inds,
|
||||||
|
targetds=sashelp.class,
|
||||||
|
outds=work.badrecords,
|
||||||
|
abort=NO
|
||||||
|
)
|
||||||
|
%let syscc=0;
|
||||||
|
%mp_assertdsobs(work.badrecords,
|
||||||
|
desc=Code injection - column name,
|
||||||
|
test=HASOBS,
|
||||||
|
outds=work.test_results
|
||||||
|
)
|
||||||
|
|
||||||
|
/* Code injection - raw values*/
|
||||||
|
data work.inds;
|
||||||
|
infile datalines4 dsd;
|
||||||
|
input GROUP_LOGIC:$3. SUBGROUP_LOGIC:$3. SUBGROUP_ID:8. VARIABLE_NM:$32.
|
||||||
|
OPERATOR_NM:$10. RAW_VALUE:$32767.;
|
||||||
|
datalines4;
|
||||||
|
AND,AND,1,age,=,;;%abort
|
||||||
|
;;;;
|
||||||
|
run;
|
||||||
|
%mp_filtercheck(work.inds,
|
||||||
|
targetds=sashelp.class,
|
||||||
|
outds=work.badrecords,
|
||||||
|
abort=NO
|
||||||
|
)
|
||||||
|
%let syscc=0;
|
||||||
|
%mp_assertdsobs(work.badrecords,
|
||||||
|
desc=Code injection - raw value abort,
|
||||||
|
test=HASOBS,
|
||||||
|
outds=work.test_results
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
%webout(OPEN)
|
||||||
|
%webout(OBJ, TEST_RESULTS)
|
||||||
|
%webout(CLOSE)
|
||||||
126
tests/base/mp_filtergenerate.test.sas
Normal file
126
tests/base/mp_filtergenerate.test.sas
Normal file
@@ -0,0 +1,126 @@
|
|||||||
|
/**
|
||||||
|
@file
|
||||||
|
@brief Testing mp_filtergenerate macro
|
||||||
|
|
||||||
|
<h4> SAS Macros </h4>
|
||||||
|
@li mp_filtergenerate.sas
|
||||||
|
@li mp_filtercheck.sas
|
||||||
|
@li mp_assertdsobs.sas
|
||||||
|
|
||||||
|
**/
|
||||||
|
|
||||||
|
options source2;
|
||||||
|
|
||||||
|
/* valid filter */
|
||||||
|
data work.inds;
|
||||||
|
infile datalines4 dsd;
|
||||||
|
input GROUP_LOGIC:$3. SUBGROUP_LOGIC:$3. SUBGROUP_ID:8. VARIABLE_NM:$32.
|
||||||
|
OPERATOR_NM:$10. RAW_VALUE:$32767.;
|
||||||
|
datalines4;
|
||||||
|
AND,AND,1,AGE,>,5
|
||||||
|
AND,AND,1,SEX,NE,"'M'"
|
||||||
|
AND,OR,2,Name,NOT IN,"('Jane','Janet')"
|
||||||
|
AND,OR,2,Weight,>=,84.6
|
||||||
|
;;;;
|
||||||
|
run;
|
||||||
|
%mp_filtercheck(work.inds,targetds=sashelp.class)
|
||||||
|
%mp_filtergenerate(work.inds,outref=myfilter)
|
||||||
|
data work.test;
|
||||||
|
set sashelp.class;
|
||||||
|
where %inc myfilter;;
|
||||||
|
run;
|
||||||
|
%mp_assertdsobs(work.test,
|
||||||
|
desc=Valid filter,
|
||||||
|
test=EQUALS 8,
|
||||||
|
outds=work.test_results
|
||||||
|
)
|
||||||
|
|
||||||
|
/* empty filter (return all records) */
|
||||||
|
data work.inds;
|
||||||
|
infile datalines4 dsd;
|
||||||
|
input GROUP_LOGIC:$3. SUBGROUP_LOGIC:$3. SUBGROUP_ID:8. VARIABLE_NM:$32.
|
||||||
|
OPERATOR_NM:$10. RAW_VALUE:$32767.;
|
||||||
|
datalines4;
|
||||||
|
;;;;
|
||||||
|
run;
|
||||||
|
%mp_filtercheck(work.inds,targetds=sashelp.class)
|
||||||
|
%mp_filtergenerate(work.inds,outref=myfilter)
|
||||||
|
data work.test;
|
||||||
|
set sashelp.class;
|
||||||
|
where %inc myfilter;;
|
||||||
|
run;
|
||||||
|
%mp_assertdsobs(work.test,
|
||||||
|
desc=Empty filter (return all records) ,
|
||||||
|
test=EQUALS 19,
|
||||||
|
outds=work.test_results
|
||||||
|
)
|
||||||
|
|
||||||
|
/* single line filter */
|
||||||
|
data work.inds;
|
||||||
|
infile datalines4 dsd;
|
||||||
|
input GROUP_LOGIC:$3. SUBGROUP_LOGIC:$3. SUBGROUP_ID:8. VARIABLE_NM:$32.
|
||||||
|
OPERATOR_NM:$10. RAW_VALUE:$32767.;
|
||||||
|
datalines4;
|
||||||
|
AND,OR,2,Name,IN,"('Jane','Janet')"
|
||||||
|
;;;;
|
||||||
|
run;
|
||||||
|
%mp_filtercheck(work.inds,targetds=sashelp.class)
|
||||||
|
%mp_filtergenerate(work.inds,outref=myfilter)
|
||||||
|
data work.test;
|
||||||
|
set sashelp.class;
|
||||||
|
where %inc myfilter;;
|
||||||
|
run;
|
||||||
|
%mp_assertdsobs(work.test,
|
||||||
|
desc=Single line filter ,
|
||||||
|
test=EQUALS 2,
|
||||||
|
outds=work.test_results
|
||||||
|
)
|
||||||
|
|
||||||
|
/* single line 2 group filter */
|
||||||
|
data work.inds;
|
||||||
|
infile datalines4 dsd;
|
||||||
|
input GROUP_LOGIC:$3. SUBGROUP_LOGIC:$3. SUBGROUP_ID:8. VARIABLE_NM:$32.
|
||||||
|
OPERATOR_NM:$10. RAW_VALUE:$32767.;
|
||||||
|
datalines4;
|
||||||
|
OR,OR,2,Name,IN,"('Jane','Janet')"
|
||||||
|
OR,OR,3,Name,IN,"('James')"
|
||||||
|
;;;;
|
||||||
|
run;
|
||||||
|
%mp_filtercheck(work.inds,targetds=sashelp.class)
|
||||||
|
%mp_filtergenerate(work.inds,outref=myfilter)
|
||||||
|
data work.test;
|
||||||
|
set sashelp.class;
|
||||||
|
where %inc myfilter;;
|
||||||
|
run;
|
||||||
|
%mp_assertdsobs(work.test,
|
||||||
|
desc=Single line 2 group filter ,
|
||||||
|
test=EQUALS 3,
|
||||||
|
outds=work.test_results
|
||||||
|
)
|
||||||
|
|
||||||
|
/* filter with nothing returned */
|
||||||
|
data work.inds;
|
||||||
|
infile datalines4 dsd;
|
||||||
|
input GROUP_LOGIC:$3. SUBGROUP_LOGIC:$3. SUBGROUP_ID:8. VARIABLE_NM:$32.
|
||||||
|
OPERATOR_NM:$10. RAW_VALUE:$32767.;
|
||||||
|
datalines4;
|
||||||
|
AND,OR,2,Name,IN,"('Jane','Janet')"
|
||||||
|
AND,OR,3,Name,IN,"('James')"
|
||||||
|
;;;;
|
||||||
|
run;
|
||||||
|
%mp_filtercheck(work.inds,targetds=sashelp.class)
|
||||||
|
%mp_filtergenerate(work.inds,outref=myfilter)
|
||||||
|
data work.test;
|
||||||
|
set sashelp.class;
|
||||||
|
where %inc myfilter;;
|
||||||
|
run;
|
||||||
|
%mp_assertdsobs(work.test,
|
||||||
|
desc=Filter with nothing returned,
|
||||||
|
test=EQUALS 0,
|
||||||
|
outds=work.test_results
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
%webout(OPEN)
|
||||||
|
%webout(OBJ, TEST_RESULTS)
|
||||||
|
%webout(CLOSE)
|
||||||
8
tests/testinit.sas
Normal file
8
tests/testinit.sas
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
/**
|
||||||
|
@file
|
||||||
|
@brief init file for tests
|
||||||
|
|
||||||
|
**/
|
||||||
|
|
||||||
|
/* location in metadata or SAS Drive for temporary files */
|
||||||
|
%let mcTestAppLoc=/Public/temp/test;
|
||||||
24
tests/viya/mv_createwebservice.test.sas
Normal file
24
tests/viya/mv_createwebservice.test.sas
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
/**
|
||||||
|
@file
|
||||||
|
@brief Testing mv_createwebservice macro
|
||||||
|
|
||||||
|
<h4> SAS Macros </h4>
|
||||||
|
@li mv_createwebservice.sas
|
||||||
|
|
||||||
|
**/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test Case 1
|
||||||
|
* Send special char in a service
|
||||||
|
*/
|
||||||
|
|
||||||
|
filename testref temp;
|
||||||
|
data _null_;
|
||||||
|
file testref;
|
||||||
|
put '01'x;
|
||||||
|
run;
|
||||||
|
%mv_createwebservice(
|
||||||
|
path=&mcTestAppLoc/tests/macros,
|
||||||
|
code=testref,
|
||||||
|
name=mv_createwebservice
|
||||||
|
)
|
||||||
@@ -9,8 +9,8 @@
|
|||||||
|
|
||||||
@param path= The full path of the folder to be created
|
@param path= The full path of the folder to be created
|
||||||
@param access_token_var= The global macro variable to contain the access token
|
@param access_token_var= The global macro variable to contain the access token
|
||||||
@param grant_type= valid values are "password" or "authorization_code" (unquoted).
|
@param grant_type= (authorization_code) Valid values are "password" or
|
||||||
The default is authorization_code.
|
"authorization_code" (unquoted).
|
||||||
|
|
||||||
|
|
||||||
@version VIYA V.03.04
|
@version VIYA V.03.04
|
||||||
@@ -39,7 +39,6 @@
|
|||||||
%let &access_token_var=;
|
%let &access_token_var=;
|
||||||
%end;
|
%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
|
and &grant_type ne sas_services
|
||||||
)
|
)
|
||||||
@@ -85,12 +84,15 @@ options noquotelenmax;
|
|||||||
%local libref1;
|
%local libref1;
|
||||||
%let libref1=%mf_getuniquelibref();
|
%let libref1=%mf_getuniquelibref();
|
||||||
libname &libref1 JSON fileref=&fname1;
|
libname &libref1 JSON fileref=&fname1;
|
||||||
%mp_abort(iftrue=(&SYS_PROCHTTP_STATUS_CODE ne 200 and &SYS_PROCHTTP_STATUS_CODE ne 404)
|
%mp_abort(
|
||||||
|
iftrue=(
|
||||||
|
&SYS_PROCHTTP_STATUS_CODE ne 200 and &SYS_PROCHTTP_STATUS_CODE ne 404
|
||||||
|
)
|
||||||
,mac=&sysmacroname
|
,mac=&sysmacroname
|
||||||
,msg=%str(&SYS_PROCHTTP_STATUS_CODE &SYS_PROCHTTP_STATUS_PHRASE)
|
,msg=%str(&SYS_PROCHTTP_STATUS_CODE &SYS_PROCHTTP_STATUS_PHRASE)
|
||||||
)
|
)
|
||||||
%if &SYS_PROCHTTP_STATUS_CODE=200 %then %do;
|
%if &SYS_PROCHTTP_STATUS_CODE=200 %then %do;
|
||||||
%put &sysmacroname &newpath exists so grab the follow on link ;
|
%*put &sysmacroname &newpath exists so grab the follow on link ;
|
||||||
data _null_;
|
data _null_;
|
||||||
set &libref1..links;
|
set &libref1..links;
|
||||||
if rel='createChild' then
|
if rel='createChild' then
|
||||||
|
|||||||
@@ -5,7 +5,8 @@
|
|||||||
Code is passed in as one or more filerefs.
|
Code is passed in as one or more filerefs.
|
||||||
|
|
||||||
%* Step 1 - compile macros ;
|
%* Step 1 - compile macros ;
|
||||||
filename mc url "https://raw.githubusercontent.com/sasjs/core/main/all.sas";
|
filename mc url
|
||||||
|
"https://raw.githubusercontent.com/sasjs/core/main/all.sas";
|
||||||
%inc mc;
|
%inc mc;
|
||||||
|
|
||||||
%* Step 2 - Create some SAS code and add it to a job;
|
%* Step 2 - Create some SAS code and add it to a job;
|
||||||
@@ -38,8 +39,8 @@
|
|||||||
needs to be attached to the beginning of the job
|
needs to be attached to the beginning of the job
|
||||||
@param code= Fileref(s) of the actual code to be added
|
@param code= Fileref(s) of the actual code to be added
|
||||||
@param access_token_var= The global macro variable to contain the access token
|
@param access_token_var= The global macro variable to contain the access token
|
||||||
@param grant_type= valid values are "password" or "authorization_code" (unquoted).
|
@param grant_type= valid values are "password" or "authorization_code"
|
||||||
The default is authorization_code.
|
(unquoted). The default is authorization_code.
|
||||||
@param replace= select NO to avoid replacing any existing job in that location
|
@param replace= select NO to avoid replacing any existing job in that location
|
||||||
@param contextname= Choose a specific context on which to run the Job. Leave
|
@param contextname= Choose a specific context on which to run the Job. Leave
|
||||||
blank to use the default context. From Viya 3.5 it is possible to configure
|
blank to use the default context. From Viya 3.5 it is possible to configure
|
||||||
@@ -71,7 +72,6 @@
|
|||||||
%let oauth_bearer=oauth_bearer=sas_services;
|
%let oauth_bearer=oauth_bearer=sas_services;
|
||||||
%let &access_token_var=;
|
%let &access_token_var=;
|
||||||
%end;
|
%end;
|
||||||
%put &sysmacroname: grant_type=&grant_type;
|
|
||||||
|
|
||||||
/* initial validation checking */
|
/* initial validation checking */
|
||||||
%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
|
||||||
|
|||||||
@@ -5,7 +5,8 @@
|
|||||||
Code is passed in as one or more filerefs.
|
Code is passed in as one or more filerefs.
|
||||||
|
|
||||||
%* Step 1 - compile macros ;
|
%* Step 1 - compile macros ;
|
||||||
filename mc url "https://raw.githubusercontent.com/sasjs/core/main/all.sas";
|
filename mc url
|
||||||
|
"https://raw.githubusercontent.com/sasjs/core/main/all.sas";
|
||||||
%inc mc;
|
%inc mc;
|
||||||
|
|
||||||
%* Step 2 - Create some code and add it to a web service;
|
%* Step 2 - Create some code and add it to a web service;
|
||||||
@@ -45,14 +46,16 @@
|
|||||||
needs to be attached to the beginning of the service
|
needs to be attached to the beginning of the service
|
||||||
@param code= Fileref(s) of the actual code to be added
|
@param code= Fileref(s) of the actual code to be added
|
||||||
@param access_token_var= The global macro variable to contain the access token
|
@param access_token_var= The global macro variable to contain the access token
|
||||||
@param grant_type= valid values are "password" or "authorization_code" (unquoted).
|
@param grant_type= valid values are "password" or "authorization_code"
|
||||||
The default is authorization_code.
|
(unquoted). The default is authorization_code.
|
||||||
@param replace= select NO to avoid replacing any existing service in that location
|
@param replace= select NO to avoid replacing any existing service in that
|
||||||
|
location
|
||||||
@param adapter= the macro uses the sasjs adapter by default. To use another
|
@param adapter= the macro uses the sasjs adapter by default. To use another
|
||||||
adapter, add a (different) fileref here.
|
adapter, add a (different) fileref here.
|
||||||
@param contextname= Choose a specific context on which to run the Job. Leave
|
@param contextname= Choose a specific context on which to run the Job. Leave
|
||||||
blank to use the default context. From Viya 3.5 it is possible to configure
|
blank to use the default context. From Viya 3.5 it is possible to configure
|
||||||
a shared context - see https://go.documentation.sas.com/?docsetId=calcontexts&docsetTarget=n1hjn8eobk5pyhn1wg3ja0drdl6h.htm&docsetVersion=3.5&locale=en
|
a shared context - see
|
||||||
|
https://go.documentation.sas.com/?docsetId=calcontexts&docsetTarget=n1hjn8eobk5pyhn1wg3ja0drdl6h.htm&docsetVersion=3.5&locale=en
|
||||||
|
|
||||||
@version VIYA V.03.04
|
@version VIYA V.03.04
|
||||||
@author Allan Bowe, source: https://github.com/sasjs/core
|
@author Allan Bowe, source: https://github.com/sasjs/core
|
||||||
@@ -80,7 +83,6 @@
|
|||||||
%let oauth_bearer=oauth_bearer=sas_services;
|
%let oauth_bearer=oauth_bearer=sas_services;
|
||||||
%let &access_token_var=;
|
%let &access_token_var=;
|
||||||
%end;
|
%end;
|
||||||
%put &sysmacroname: grant_type=&grant_type;
|
|
||||||
|
|
||||||
/* initial validation checking */
|
/* initial validation checking */
|
||||||
%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
|
||||||
@@ -143,7 +145,8 @@ libname &libref1 JSON fileref=&fname1;
|
|||||||
|
|
||||||
data _null_;
|
data _null_;
|
||||||
set &libref1..links;
|
set &libref1..links;
|
||||||
if rel='members' then call symputx('membercheck',quote("&base_uri"!!trim(href)),'l');
|
if rel='members' then
|
||||||
|
call symputx('membercheck',quote("&base_uri"!!trim(href)),'l');
|
||||||
else if rel='self' then call symputx('parentFolderUri',href,'l');
|
else if rel='self' then call symputx('parentFolderUri',href,'l');
|
||||||
run;
|
run;
|
||||||
data _null_;
|
data _null_;
|
||||||
@@ -256,7 +259,8 @@ data _null_;
|
|||||||
put ' %end; ';
|
put ' %end; ';
|
||||||
put ' data _null_;file &jref mod ; ';
|
put ' data _null_;file &jref mod ; ';
|
||||||
put ' put "["; call symputx(''cols'',0,''l''); ';
|
put ' put "["; call symputx(''cols'',0,''l''); ';
|
||||||
put ' proc sort data=sashelp.vcolumn(where=(libname=''WORK'' & memname="%upcase(&ds)")) ';
|
put ' proc sort ';
|
||||||
|
put ' data=sashelp.vcolumn(where=(libname=''WORK'' & memname="%upcase(&ds)")) ';
|
||||||
put ' out=_data_; ';
|
put ' out=_data_; ';
|
||||||
put ' by varnum; ';
|
put ' by varnum; ';
|
||||||
put ' ';
|
put ' ';
|
||||||
@@ -295,7 +299,8 @@ data _null_;
|
|||||||
put ' %end; ';
|
put ' %end; ';
|
||||||
put ' %end; ';
|
put ' %end; ';
|
||||||
put ' run; ';
|
put ' run; ';
|
||||||
put ' /* write to temp loc to avoid _webout truncation - https://support.sas.com/kb/49/325.html */ ';
|
put ' /* write to temp loc to avoid _webout truncation ';
|
||||||
|
put ' - https://support.sas.com/kb/49/325.html */ ';
|
||||||
put ' filename _sjs temp lrecl=131068 encoding=''utf-8''; ';
|
put ' filename _sjs temp lrecl=131068 encoding=''utf-8''; ';
|
||||||
put ' data _null_; file _sjs lrecl=131068 encoding=''utf-8'' mod; ';
|
put ' data _null_; file _sjs lrecl=131068 encoding=''utf-8'' mod; ';
|
||||||
put ' set &tempds; ';
|
put ' set &tempds; ';
|
||||||
@@ -331,7 +336,7 @@ data _null_;
|
|||||||
put '%end; ';
|
put '%end; ';
|
||||||
put ' ';
|
put ' ';
|
||||||
put '%else %if &action=CLOSE %then %do; ';
|
put '%else %if &action=CLOSE %then %do; ';
|
||||||
put ' data _null_;file &jref encoding=''utf-8''; ';
|
put ' data _null_;file &jref encoding=''utf-8'' mod; ';
|
||||||
put ' put "}"; ';
|
put ' put "}"; ';
|
||||||
put ' run; ';
|
put ' run; ';
|
||||||
put '%end; ';
|
put '%end; ';
|
||||||
@@ -410,7 +415,8 @@ data _null_;
|
|||||||
put ' if _n_=1 then call symputx(''input_statement'',_infile_); ';
|
put ' if _n_=1 then call symputx(''input_statement'',_infile_); ';
|
||||||
put ' list; ';
|
put ' list; ';
|
||||||
put ' data &table; ';
|
put ' data &table; ';
|
||||||
put ' infile "%sysfunc(pathname(work))/&table..csv" firstobs=2 dsd termstr=crlf; ';
|
put ' infile "%sysfunc(pathname(work))/&table..csv" firstobs=2 dsd ';
|
||||||
|
put ' termstr=crlf; ';
|
||||||
put ' input &input_statement; ';
|
put ' input &input_statement; ';
|
||||||
put ' run; ';
|
put ' run; ';
|
||||||
put ' %end; ';
|
put ' %end; ';
|
||||||
@@ -451,7 +457,8 @@ data _null_;
|
|||||||
put ' ';
|
put ' ';
|
||||||
put ' /* setup temp ref */ ';
|
put ' /* setup temp ref */ ';
|
||||||
put ' %if %upcase(&fref) ne _WEBOUT %then %do; ';
|
put ' %if %upcase(&fref) ne _WEBOUT %then %do; ';
|
||||||
put ' filename &fref temp lrecl=999999 permission=''A::u::rwx,A::g::rw-,A::o::---'' mod; ';
|
put ' filename &fref temp lrecl=999999 permission=''A::u::rwx,A::g::rw-,A::o::---'' ';
|
||||||
|
put ' mod; ';
|
||||||
put ' %end; ';
|
put ' %end; ';
|
||||||
put ' ';
|
put ' ';
|
||||||
put ' /* setup json */ ';
|
put ' /* setup json */ ';
|
||||||
@@ -461,7 +468,7 @@ data _null_;
|
|||||||
put '%end; ';
|
put '%end; ';
|
||||||
put '%else %if &action=ARR or &action=OBJ %then %do; ';
|
put '%else %if &action=ARR or &action=OBJ %then %do; ';
|
||||||
put ' %mp_jsonout(&action,&ds,dslabel=&dslabel,fmt=&fmt ';
|
put ' %mp_jsonout(&action,&ds,dslabel=&dslabel,fmt=&fmt ';
|
||||||
put ' ,jref=&fref,engine=PROCJSON,dbg=%str(&_debug) ';
|
put ' ,jref=&fref,engine=DATASTEP,dbg=%str(&_debug) ';
|
||||||
put ' ) ';
|
put ' ) ';
|
||||||
put '%end; ';
|
put '%end; ';
|
||||||
put '%else %if &action=CLOSE %then %do; ';
|
put '%else %if &action=CLOSE %then %do; ';
|
||||||
@@ -586,6 +593,14 @@ run;
|
|||||||
rc =fput(fileid,'\');rc =fwrite(fileid);
|
rc =fput(fileid,'\');rc =fwrite(fileid);
|
||||||
rc =fput(fileid,'\');rc =fwrite(fileid);
|
rc =fput(fileid,'\');rc =fwrite(fileid);
|
||||||
end;
|
end;
|
||||||
|
else if rec='01'x then do; /* Unprintable */
|
||||||
|
rc =fput(fileid,'\');rc =fwrite(fileid);
|
||||||
|
rc =fput(fileid,'u');rc =fwrite(fileid);
|
||||||
|
rc =fput(fileid,'0');rc =fwrite(fileid);
|
||||||
|
rc =fput(fileid,'0');rc =fwrite(fileid);
|
||||||
|
rc =fput(fileid,'0');rc =fwrite(fileid);
|
||||||
|
rc =fput(fileid,'1');rc =fwrite(fileid);
|
||||||
|
end;
|
||||||
else do;
|
else do;
|
||||||
rc =fput(fileid,rec);
|
rc =fput(fileid,rec);
|
||||||
rc =fwrite(fileid);
|
rc =fwrite(fileid);
|
||||||
|
|||||||
@@ -44,7 +44,7 @@
|
|||||||
%let oauth_bearer=oauth_bearer=sas_services;
|
%let oauth_bearer=oauth_bearer=sas_services;
|
||||||
%let &access_token_var=;
|
%let &access_token_var=;
|
||||||
%end;
|
%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
|
and &grant_type ne sas_services
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -39,7 +39,7 @@
|
|||||||
%let oauth_bearer=oauth_bearer=sas_services;
|
%let oauth_bearer=oauth_bearer=sas_services;
|
||||||
%let &access_token_var=;
|
%let &access_token_var=;
|
||||||
%end;
|
%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
|
and &grant_type ne sas_services
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -54,7 +54,7 @@
|
|||||||
%let oauth_bearer=oauth_bearer=sas_services;
|
%let oauth_bearer=oauth_bearer=sas_services;
|
||||||
%let &access_token_var=;
|
%let &access_token_var=;
|
||||||
%end;
|
%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
|
and &grant_type ne sas_services
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -10,7 +10,8 @@
|
|||||||
|
|
||||||
First, compile the macros:
|
First, compile the macros:
|
||||||
|
|
||||||
filename mc url "https://raw.githubusercontent.com/sasjs/core/main/all.sas";
|
filename mc url
|
||||||
|
"https://raw.githubusercontent.com/sasjs/core/main/all.sas";
|
||||||
%inc mc;
|
%inc mc;
|
||||||
|
|
||||||
Next, create a job (in this case, a web service):
|
Next, create a job (in this case, a web service):
|
||||||
|
|||||||
@@ -13,7 +13,7 @@
|
|||||||
@li FLOW_ID - Numeric value, provides sequential ordering capability. Is
|
@li FLOW_ID - Numeric value, provides sequential ordering capability. Is
|
||||||
optional, will default to 0 if not provided.
|
optional, will default to 0 if not provided.
|
||||||
@li _CONTEXTNAME - Dictates which context should be used to run the job. If
|
@li _CONTEXTNAME - Dictates which context should be used to run the job. If
|
||||||
blank (or not provided), will default to `SAS Job Execution compute context`.
|
blank, or not provided, will default to `SAS Job Execution compute context`.
|
||||||
|
|
||||||
Any additional variables provided in this table are converted into macro
|
Any additional variables provided in this table are converted into macro
|
||||||
variables and passed into the relevant job.
|
variables and passed into the relevant job.
|
||||||
@@ -97,12 +97,14 @@
|
|||||||
run;
|
run;
|
||||||
|
|
||||||
|
|
||||||
@param [in] access_token_var= The global macro variable to contain the access token
|
@param [in] access_token_var= The global macro variable to contain the access
|
||||||
|
token
|
||||||
@param [in] grant_type= valid values:
|
@param [in] grant_type= valid values:
|
||||||
@li password
|
@li password
|
||||||
@li authorization_code
|
@li authorization_code
|
||||||
@li detect - will check if access_token exists, if not will use sas_services if
|
@li detect - will check if access_token exists, if not will use
|
||||||
a SASStudioV session else authorization_code. Default option.
|
sas_services if a SASStudioV session else authorization_code. Default
|
||||||
|
option.
|
||||||
@li sas_services - will use oauth_bearer=sas_services
|
@li sas_services - will use oauth_bearer=sas_services
|
||||||
@param [in] inds= The input dataset containing a list of jobs and parameters
|
@param [in] inds= The input dataset containing a list of jobs and parameters
|
||||||
@param [in] maxconcurrency= The max number of parallel jobs to run. Default=8.
|
@param [in] maxconcurrency= The max number of parallel jobs to run. Default=8.
|
||||||
@@ -185,7 +187,7 @@ select count(*) into: missings
|
|||||||
where flow_id is null or _program is null;
|
where flow_id is null or _program is null;
|
||||||
%mp_abort(iftrue=(&missings>0)
|
%mp_abort(iftrue=(&missings>0)
|
||||||
,mac=&sysmacroname
|
,mac=&sysmacroname
|
||||||
,msg=%str(input dataset contains &missings missing values for FLOW_ID or _PROGRAM)
|
,msg=%str(input dataset has &missings missing values for FLOW_ID or _PROGRAM)
|
||||||
)
|
)
|
||||||
|
|
||||||
%if %mf_nobs(&inds)=0 %then %do;
|
%if %mf_nobs(&inds)=0 %then %do;
|
||||||
@@ -284,7 +286,8 @@ data;run;%let jdswaitfor=&syslast;
|
|||||||
%if "&&jobuid&jid"="0" and &concurrency<&maxconcurrency %then %do;
|
%if "&&jobuid&jid"="0" and &concurrency<&maxconcurrency %then %do;
|
||||||
%local jobname jobpath;
|
%local jobname jobpath;
|
||||||
%let jobname=%scan(&&job&jid,-1,/);
|
%let jobname=%scan(&&job&jid,-1,/);
|
||||||
%let jobpath=%substr(&&job&jid,1,%length(&&job&jid)-%length(&jobname)-1);
|
%let jobpath=
|
||||||
|
%substr(&&job&jid,1,%length(&&job&jid)-%length(&jobname)-1);
|
||||||
%put executing &jobpath/&jobname with paramstring &&jparams&jid;
|
%put executing &jobpath/&jobname with paramstring &&jparams&jid;
|
||||||
%mv_jobexecute(path=&jobpath
|
%mv_jobexecute(path=&jobpath
|
||||||
,name=&jobname
|
,name=&jobname
|
||||||
@@ -334,7 +337,8 @@ data;run;%let jdswaitfor=&syslast;
|
|||||||
/* loop again if jobs are left */
|
/* loop again if jobs are left */
|
||||||
%if &completed < &jcnt %then %do;
|
%if &completed < &jcnt %then %do;
|
||||||
%let jid=0;
|
%let jid=0;
|
||||||
%put looping flow &fid again - &completed of &jcnt jobs completed, &concurrency jobs running;
|
%put looping flow &fid again - &completed of &jcnt jobs completed,
|
||||||
|
&concurrency jobs running;
|
||||||
%end;
|
%end;
|
||||||
%end;
|
%end;
|
||||||
%end;
|
%end;
|
||||||
|
|||||||
@@ -5,14 +5,16 @@
|
|||||||
This macro will obtain the Consul Token and use that to call the Web Service.
|
This macro will obtain the Consul Token and use that to call the Web Service.
|
||||||
|
|
||||||
more info: https://developer.sas.com/reference/auth/#register
|
more info: https://developer.sas.com/reference/auth/#register
|
||||||
and: http://proc-x.com/2019/01/authentication-to-sas-viya-a-couple-of-approaches/
|
and:
|
||||||
|
http://proc-x.com/2019/01/authentication-to-sas-viya-a-couple-of-approaches
|
||||||
|
|
||||||
The default viyaroot location is /opt/sas/viya/config
|
The default viyaroot location is /opt/sas/viya/config
|
||||||
|
|
||||||
Usage:
|
Usage:
|
||||||
|
|
||||||
%* compile macros;
|
%* compile macros;
|
||||||
filename mc url "https://raw.githubusercontent.com/sasjs/core/main/all.sas";
|
filename mc url
|
||||||
|
"https://raw.githubusercontent.com/sasjs/core/main/all.sas";
|
||||||
%inc mc;
|
%inc mc;
|
||||||
|
|
||||||
%* specific client with just openid scope;
|
%* specific client with just openid scope;
|
||||||
@@ -33,7 +35,8 @@
|
|||||||
@param client_id= The client name. Auto generated if blank.
|
@param client_id= The client name. Auto generated if blank.
|
||||||
@param client_secret= Client secret Auto generated if client is blank.
|
@param client_secret= Client secret Auto generated if client is blank.
|
||||||
@param scopes= list of space-seperated unquoted scopes (default is openid)
|
@param scopes= list of space-seperated unquoted scopes (default is openid)
|
||||||
@param grant_type= valid values are "password" or "authorization_code" (unquoted)
|
@param grant_type= valid values are "password" or "authorization_code"
|
||||||
|
(unquoted)
|
||||||
@param outds= the dataset to contain the registered client id and secret
|
@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)
|
in seconds. A value of DEFAULT will omit the entry (and use system default)
|
||||||
@@ -78,15 +81,16 @@
|
|||||||
,refresh_token_validity=DEFAULT
|
,refresh_token_validity=DEFAULT
|
||||||
,outjson=_null_
|
,outjson=_null_
|
||||||
);
|
);
|
||||||
%local consul_token fname1 fname2 fname3 libref access_token url;
|
%local consul_token fname1 fname2 fname3 libref access_token url tokloc;
|
||||||
|
|
||||||
%if client_name=DEFAULT %then %let client_name=
|
%if client_name=DEFAULT %then %let client_name=
|
||||||
Generated by %mf_getuser() on %sysfunc(datetime(),datetime19.) using SASjs;
|
Generated by %mf_getuser() on %sysfunc(datetime(),datetime19.) using SASjs;
|
||||||
|
|
||||||
options noquotelenmax;
|
options noquotelenmax;
|
||||||
/* first, get consul token needed to get client id / secret */
|
/* first, get consul token needed to get client id / secret */
|
||||||
|
%let tokloc=/etc/SASSecurityCertificateFramework/tokens/consul/default;
|
||||||
data _null_;
|
data _null_;
|
||||||
infile "%mf_loc(VIYACONFIG)/etc/SASSecurityCertificateFramework/tokens/consul/default/client.token";
|
infile "%mf_loc(VIYACONFIG)&tokloc/client.token";
|
||||||
input token:$64.;
|
input token:$64.;
|
||||||
call symputx('consul_token',token);
|
call symputx('consul_token',token);
|
||||||
run;
|
run;
|
||||||
@@ -97,7 +101,8 @@ run;
|
|||||||
/* request the client details */
|
/* request the client details */
|
||||||
%let fname1=%mf_getuniquefileref();
|
%let fname1=%mf_getuniquefileref();
|
||||||
proc http method='POST' out=&fname1
|
proc http method='POST' out=&fname1
|
||||||
url="&base_uri/SASLogon/oauth/clients/consul?callback=false%str(&)serviceId=app";
|
url="&base_uri/SASLogon/oauth/clients/consul?callback=false%str(&)%trim(
|
||||||
|
)serviceId=app";
|
||||||
headers "X-Consul-Token"="&consul_token";
|
headers "X-Consul-Token"="&consul_token";
|
||||||
run;
|
run;
|
||||||
|
|
||||||
@@ -122,7 +127,8 @@ run;
|
|||||||
%let scopes=%sysfunc(coalescec(&scopes,openid));
|
%let scopes=%sysfunc(coalescec(&scopes,openid));
|
||||||
%let scopes=%mf_getquotedstr(&scopes,QUOTE=D,indlm=|);
|
%let scopes=%mf_getquotedstr(&scopes,QUOTE=D,indlm=|);
|
||||||
%let grant_type=%mf_getquotedstr(&grant_type,QUOTE=D,indlm=|);
|
%let grant_type=%mf_getquotedstr(&grant_type,QUOTE=D,indlm=|);
|
||||||
%let required_user_groups=%mf_getquotedstr(&required_user_groups,QUOTE=D,indlm=|);
|
%let required_user_groups=
|
||||||
|
%mf_getquotedstr(&required_user_groups,QUOTE=D,indlm=|);
|
||||||
|
|
||||||
data _null_;
|
data _null_;
|
||||||
file &fname2;
|
file &fname2;
|
||||||
@@ -139,9 +145,11 @@ data _null_;
|
|||||||
if reqd_groups = '""' then reqd_groups ='';
|
if reqd_groups = '""' then reqd_groups ='';
|
||||||
else reqd_groups=cats(',"required_user_groups":[',reqd_groups,']');
|
else reqd_groups=cats(',"required_user_groups":[',reqd_groups,']');
|
||||||
autoapprove=trim(symget('autoapprove'));
|
autoapprove=trim(symget('autoapprove'));
|
||||||
if not missing(autoapprove) then autoapprove=cats(',"autoapprove":',autoapprove);
|
if not missing(autoapprove) then autoapprove=
|
||||||
|
cats(',"autoapprove":',autoapprove);
|
||||||
use_session=trim(symget('use_session'));
|
use_session=trim(symget('use_session'));
|
||||||
if not missing(use_session) then use_session=cats(',"use_session":',use_session);
|
if not missing(use_session) then use_session=
|
||||||
|
cats(',"use_session":',use_session);
|
||||||
|
|
||||||
put '{' clientid ;
|
put '{' clientid ;
|
||||||
put clientsecret ;
|
put clientsecret ;
|
||||||
@@ -206,10 +214,12 @@ run;
|
|||||||
%put GRANT_TYPE=&grant_type;
|
%put GRANT_TYPE=&grant_type;
|
||||||
%put;
|
%put;
|
||||||
%if %index(%superq(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 */
|
/* cannot use base_uri here as it includes the protocol which may be incorrect
|
||||||
%put NOTE: The developer must also register below and select 'openid' to get the grant code:;
|
externally */
|
||||||
|
%put NOTE: Visit the link below and select 'openid' to get the grant code:;
|
||||||
%put NOTE- ;
|
%put NOTE- ;
|
||||||
%put NOTE- &url/SASLogon/oauth/authorize?client_id=&client_id%str(&)response_type=code;
|
%put NOTE- &url/SASLogon/oauth/authorize?client_id=&client_id%str(&)%trim(
|
||||||
|
)response_type=code;
|
||||||
%put NOTE- ;
|
%put NOTE- ;
|
||||||
%end;
|
%end;
|
||||||
|
|
||||||
|
|||||||
@@ -15,7 +15,8 @@
|
|||||||
|
|
||||||
Usage:
|
Usage:
|
||||||
|
|
||||||
filename mc url "https://raw.githubusercontent.com/sasjs/core/main/all.sas";
|
filename mc url
|
||||||
|
"https://raw.githubusercontent.com/sasjs/core/main/all.sas";
|
||||||
%inc mc;
|
%inc mc;
|
||||||
|
|
||||||
|
|
||||||
@@ -31,13 +32,15 @@
|
|||||||
@param outds= A dataset containing access_token and refresh_token
|
@param outds= A dataset containing access_token and refresh_token
|
||||||
@param client_id= The client name
|
@param client_id= The client name
|
||||||
@param client_secret= client secret
|
@param client_secret= client secret
|
||||||
@param grant_type= valid values are "password" or "authorization_code" (unquoted).
|
@param grant_type= valid values are "password" or "authorization_code"
|
||||||
The default is authorization_code.
|
(unquoted). The default is authorization_code.
|
||||||
@param code= If grant_type=authorization_code then provide the necessary code here
|
@param code= If grant_type=authorization_code then provide the necessary code
|
||||||
|
here
|
||||||
@param user= If grant_type=password then provide the username here
|
@param user= If grant_type=password then provide the username here
|
||||||
@param pass= If grant_type=password then provide the password here
|
@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 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 refresh_token_var= The global macro variable to contain the refresh
|
||||||
|
token
|
||||||
@param base_uri= The Viya API server location
|
@param base_uri= The Viya API server location
|
||||||
|
|
||||||
@version VIYA V.03.04
|
@version VIYA V.03.04
|
||||||
@@ -88,7 +91,8 @@
|
|||||||
,msg=%str(Authorization code required)
|
,msg=%str(Authorization code required)
|
||||||
)
|
)
|
||||||
|
|
||||||
%mp_abort(iftrue=(&grant_type=password and (%str(&user)=%str() or %str(&pass)=%str()))
|
%mp_abort(iftrue=(
|
||||||
|
&grant_type=password and (%str(&user)=%str() or %str(&pass)=%str()))
|
||||||
,mac=&sysmacroname
|
,mac=&sysmacroname
|
||||||
,msg=%str(username / password required)
|
,msg=%str(username / password required)
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -32,12 +32,13 @@
|
|||||||
@param outds= A dataset containing access_token and refresh_token
|
@param outds= A dataset containing access_token and refresh_token
|
||||||
@param client_id= The client name (alternative to inds)
|
@param client_id= The client name (alternative to inds)
|
||||||
@param client_secret= client secret (alternative to inds)
|
@param client_secret= client secret (alternative to inds)
|
||||||
@param grant_type= valid values are "password" or "authorization_code" (unquoted).
|
@param grant_type= valid values are "password" or "authorization_code"
|
||||||
The default is authorization_code.
|
(unquoted). The default is authorization_code.
|
||||||
@param user= If grant_type=password then provide the username here
|
@param user= If grant_type=password then provide the username here
|
||||||
@param pass= If grant_type=password then provide the password here
|
@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 access_token_var= The global macro variable to contain the access token
|
||||||
@param refresh_token_var= The global macro variable containing the refresh token
|
@param refresh_token_var= The global macro variable containing the refresh
|
||||||
|
token
|
||||||
|
|
||||||
@version VIYA V.03.04
|
@version VIYA V.03.04
|
||||||
@author Allan Bowe, source: https://github.com/sasjs/core
|
@author Allan Bowe, source: https://github.com/sasjs/core
|
||||||
@@ -72,7 +73,8 @@ options noquotelenmax;
|
|||||||
,msg=%str(Invalid value for grant_type: &grant_type)
|
,msg=%str(Invalid value for grant_type: &grant_type)
|
||||||
)
|
)
|
||||||
|
|
||||||
%mp_abort(iftrue=(&grant_type=password and (%str(&user)=%str() or %str(&pass)=%str()))
|
%mp_abort(
|
||||||
|
iftrue=(&grant_type=password and (%str(&user)=%str() or %str(&pass)=%str()))
|
||||||
,mac=&sysmacroname
|
,mac=&sysmacroname
|
||||||
,msg=%str(username / password required)
|
,msg=%str(username / password required)
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/**
|
/**
|
||||||
@file mv_webout.sas
|
@file
|
||||||
@brief Send data to/from the SAS Viya Job Execution Service
|
@brief Send data to/from the SAS Viya Job Execution Service
|
||||||
@details This macro should be added to the start of each Job Execution
|
@details This macro should be added to the start of each Job Execution
|
||||||
Service, **immediately** followed by a call to:
|
Service, **immediately** followed by a call to:
|
||||||
@@ -109,7 +109,8 @@
|
|||||||
if _n_=1 then call symputx('input_statement',_infile_);
|
if _n_=1 then call symputx('input_statement',_infile_);
|
||||||
list;
|
list;
|
||||||
data &table;
|
data &table;
|
||||||
infile "%sysfunc(pathname(work))/&table..csv" firstobs=2 dsd termstr=crlf;
|
infile "%sysfunc(pathname(work))/&table..csv" firstobs=2 dsd
|
||||||
|
termstr=crlf;
|
||||||
input &input_statement;
|
input &input_statement;
|
||||||
run;
|
run;
|
||||||
%end;
|
%end;
|
||||||
@@ -150,7 +151,8 @@
|
|||||||
|
|
||||||
/* setup temp ref */
|
/* setup temp ref */
|
||||||
%if %upcase(&fref) ne _WEBOUT %then %do;
|
%if %upcase(&fref) ne _WEBOUT %then %do;
|
||||||
filename &fref temp lrecl=999999 permission='A::u::rwx,A::g::rw-,A::o::---' mod;
|
filename &fref temp lrecl=999999 permission='A::u::rwx,A::g::rw-,A::o::---'
|
||||||
|
mod;
|
||||||
%end;
|
%end;
|
||||||
|
|
||||||
/* setup json */
|
/* setup json */
|
||||||
@@ -160,7 +162,7 @@
|
|||||||
%end;
|
%end;
|
||||||
%else %if &action=ARR or &action=OBJ %then %do;
|
%else %if &action=ARR or &action=OBJ %then %do;
|
||||||
%mp_jsonout(&action,&ds,dslabel=&dslabel,fmt=&fmt
|
%mp_jsonout(&action,&ds,dslabel=&dslabel,fmt=&fmt
|
||||||
,jref=&fref,engine=PROCJSON,dbg=%str(&_debug)
|
,jref=&fref,engine=DATASTEP,dbg=%str(&_debug)
|
||||||
)
|
)
|
||||||
%end;
|
%end;
|
||||||
%else %if &action=CLOSE %then %do;
|
%else %if &action=CLOSE %then %do;
|
||||||
|
|||||||
Reference in New Issue
Block a user