1
0
mirror of https://github.com/sasjs/core.git synced 2025-12-21 02:01:20 +00:00

Compare commits

...

25 Commits

Author SHA1 Message Date
Allan Bowe
48212f8797 Merge pull request #11 from sasjs/sasjslintfixes
sasjs lint fixes
2021-04-03 20:58:58 +01:00
cb8992dade fix: remove .githooks now we have sasjs lint 2021-04-03 21:54:50 +02:00
7dec3120be chore: dependencies 2021-04-03 21:35:44 +02:00
9568b17f20 feat: enabling sasjs lint as a git pre-commit hook when contributing to @sasjs/core. To use, just run
> ghooks@2.0.4 install /home/zah/git/core/node_modules/ghooks
> node ./bin/module-install

> @sasjs/core@1.0.0 postinstall /home/zah/git/core
> node-git-hooks

Installing Git hooks...
added 14 packages from 12 contributors and audited 205 packages in 4.23s

17 packages are looking for funding
  run `npm fund` for details

found 0 vulnerabilities from the repository.
2021-04-03 21:34:40 +02:00
0a38056c69 fix: linting 2021-04-03 21:30:51 +02:00
Allan Bowe
096bf4fa11 chore: more indentation fixes 2021-04-03 18:21:29 +00:00
Allan Bowe
030c4a4fc1 fix: .sasjslint file and indentation issues 2021-04-03 18:10:41 +00:00
1b70205cab feat: two new macros, mp_mdtablewrite.sas (will create a markdown table from a sas dataset) and mm_getlibmetadiffs.sas (will create a set of datasets to describe the differences between a library in metadata and the physical library) 2021-04-02 13:38:46 +02:00
539447ed06 chore: fixing all.sas 2021-03-30 16:34:11 +02:00
e3c333ea39 feat: adding mm_deletelibrary.sas, a macro to easily delete a SAS Library from metadata. Documentation: https://core.sasjs.io/mm__deletelibrary_8sas.html 2021-03-30 00:02:20 +02:00
ae72446f85 chore: updating all.sas 2021-03-30 00:01:43 +02:00
2b6bf4bd02 chore: doxy fixes 2021-03-30 00:01:08 +02:00
6dbb3760e0 Merge branch 'main' of github.com:sasjs/core into main 2021-03-29 23:02:33 +02:00
200d9a5761 chore: doxy updates 2021-03-29 23:02:17 +02:00
Allan Bowe
01a9a5b823 Update README.md 2021-03-25 20:09:24 +01:00
Allan Bowe
35eadd0e9d Update README.md 2021-03-25 15:45:28 +01:00
5cdca95216 chore: vscode properties 2021-03-24 19:38:02 +01:00
Allan Bowe
81b75a32ed chore: latest sasjs version 2021-03-24 18:34:58 +00:00
b7f5a2ec00 chore: devops rules, updated gitignore and git hook 2021-03-22 10:14:52 +01:00
db859bbf1d chore: adding a git hook to prevent sas files from appearing with capital letters. To install, just run
> @sasjs/core@1.0.0 postinstall
> node-git-hooks

Installing Git hooks...

up to date, audited 2 packages in 1s

found 0 vulnerabilities.
2021-03-21 23:59:21 +01:00
27b56e8efd set executable 2021-03-21 23:33:19 +01:00
28ea218d02 chore: adding pre-commit hook 2021-03-21 22:24:46 +01:00
f356e1f351 chore: updating CONTRIBUTING, preventing files with spaces being added, adding git hooks package 2021-03-21 22:23:50 +01:00
4b375e0b8c fix: leading zero in time component of mf_uid() 2021-03-19 20:34:16 +01:00
7db207dd1c chore: updating all.sas 2021-03-19 20:29:35 +01:00
121 changed files with 4355 additions and 1657 deletions

6
.gitignore vendored
View File

@@ -1,3 +1,9 @@
node_modules
.DS_Store
sasjsbuild/
# avoid filenames with spaces being committed to source control
**\ **
# ignore the mc_* files - containing macros for individual libraries
mc_*

View File

@@ -5,4 +5,4 @@ image:
file: .gitpod.dockerfile
vscode:
extensions:
- sasjs.sasjs-for-vscode@1.4.3:LY5VLf6H5R3u7nqVRnCQRw==
- sasjs.sasjs-for-vscode@1.7.2:R6y1nzpFh2P99BZg5FgH5g==

10
.sasjslint Normal file
View File

@@ -0,0 +1,10 @@
{
"noTrailingSpaces": true,
"noEncodedPasswords": true,
"hasDoxygenHeader": true,
"noSpacesInFileNames": true,
"maxLineLength": 140,
"lowerCaseFileNames": true,
"noTabIndentation": true,
"indentationMultiple": 2
}

9
.vscode/.editorconfig vendored Normal file
View File

@@ -0,0 +1,9 @@
{
"search.exclude": {
"**/sasjsbuild/**": true,
"**/dist/**":true
},
"editor.insertSpaces": true,
"editor.tabSize": 2,
"trim_trailing_whitespace": true
}

10
.vscode/settings.json vendored Normal file
View File

@@ -0,0 +1,10 @@
{
"editor.tabSize": 2,
"editor.insertSpaces": true,
"editor.detectIndentation": true,
"editor.formatOnSave": true,
"editor.rulers": [
80
],
"files.trimTrailingWhitespace": true
}

View File

@@ -27,6 +27,5 @@ To contribute:
1. Create your feature branch (`git checkout -b myfeature`)
2. Make your change
3. Update the `all.sas` file (`python3 build.py`)
4. Commit the change, using the [conventional commit](https://www.conventionalcommits.org/en/v1.0.0) standard
5. Push and make a PR
4. Push and make a PR

View File

@@ -1,6 +1,6 @@
# Macro Core
Much quality. Many standards. The **Macro Core** library exists to save time and development effort! Herein ye shall find a veritable host of MIT-licenced, production quality SAS macros. These are a mix of tools, utilities, functions and code generators that are useful in the context of Application Development on the SAS platform (eg https://datacontroller.io). [Contributions](https://github.com/sasjs/core/blob/main/CONTRIBUTING.md) are welcomed.
Much quality. Many standards. The **Macro Core** library exists to save time and development effort! Herein ye shall find a veritable host of MIT-licenced, production quality SAS macros. These are a mix of tools, utilities, functions and code generators that are useful in the context of [Application Development](https://sasapps.io) on the SAS platform (eg https://datacontroller.io). [Contributions](https://github.com/sasjs/core/blob/main/CONTRIBUTING.md) are welcomed.
You can download and compile them all in just two lines of SAS code:
@@ -9,7 +9,7 @@ filename mc url "https://raw.githubusercontent.com/sasjs/core/main/all.sas";
%inc mc;
```
Documentation: https://sasjs.github.io/core.github.io/files.html
Documentation: https://core.sasjs.io
# Components
@@ -84,7 +84,7 @@ filename mc url "https://raw.githubusercontent.com/sasjs/core/main/all.sas";
## File Properties
- filenames much match macro names
- filenames must be lowercase
- filenames must be lowercase, without spaces
- macro names must be lowercase
- one macro per file
- prefixes:
@@ -99,7 +99,7 @@ filename mc url "https://raw.githubusercontent.com/sasjs/core/main/all.sas";
- unix style line endings (lf)
- individual lines should be no more than 80 characters long
- UTF-8
- no trailing white space
## Header Properties
@@ -118,7 +118,7 @@ All macros must be commented in the doxygen format, to enable the [online docume
### 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
<h4> SAS Macros </h4>
@li mf_nobs.sas
@li mm_assignlib.sas
@@ -136,13 +136,15 @@ When contributing to this library, it is therefore important to ensure that all
## Coding Standards
- Indentation = 2 spaces. No tabs!
- no trailing white space
- no invisible characters, other than spaces. If invisibles are needed, use hex literals.
- Macro variables should not have the trailing dot (`&var` not `&var.`) unless necessary to prevent incorrect resolution
- The closing `%mend;` should not contain the macro name.
- The closing `%mend;` should **not** contain the macro name.
- All macros should be defined with brackets, even if no variables are needed - ie `%macro x();` not `%macro x;`
- Mandatory parameters should be positional, all optional parameters should be keyword (var=) style.
- All dataset references must be 2 level (eg `work.blah`, not `blah`). This is to avoid contention when options [DATASTMTCHK](https://support.sas.com/documentation/cdl/en/lrdict/64316/HTML/default/viewer.htm#a000279064.htm)=ALLKEYWORDS is in effect.
- Avoid naming collisions! All macro variables should be local scope. Use system generated work tables where possible - eg `data ; set sashelp.class; run; data &output; set &syslast; run;`
- If you have a long-running SQL query, the use of a `quit;` statement is recommended in order to benefit from the timing statistics.
- The use of `quit;` for `proc sql` is optional unless you are looking to benefit from the timing statistics.
# General Notes

877
all.sas

File diff suppressed because it is too large Load Diff

View File

@@ -49,7 +49,10 @@
input; putlog _infile_;
i=1;
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_);
logonce+1;
end;
@@ -112,7 +115,8 @@
%let syscc=0;
%if %symexist(SYS_JES_JOB_URI) %then %do;
/* refer web service output to file service in one hit */
filename _webout filesrvc parenturi="&SYS_JES_JOB_URI" name="_webout.json";
filename _webout filesrvc parenturi="&SYS_JES_JOB_URI"
name="_webout.json";
%let rc=%sysfunc(fcopy(_web,_webout));
%end;
%else %do;

View File

@@ -32,7 +32,9 @@
/* in case the parameter is a libref.tablename, pull off just the libref */
%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;
%let engnum=%sysfunc(varnum(&dsid,ENGINE));
%let rc=%sysfunc(fetch(&dsid));

View File

@@ -16,7 +16,7 @@
%macro mf_getkeyvalue(key,libds=work.mp_setkeyvalue
)/*/STORE SOURCE*/;
%local ds dsid key valc valn type rc;
%local ds dsid key valc valn type rc;
%let dsid=%sysfunc(open(&libds(where=(key="&key"))));
%syscall set(dsid);
%let rc = %sysfunc(fetch(&dsid));

View File

@@ -9,7 +9,8 @@
%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

View File

@@ -6,7 +6,8 @@
%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

View File

@@ -1,7 +1,8 @@
/**
@file mf_mval.sas
@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.
Usage:

View File

@@ -12,7 +12,8 @@
@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

View File

@@ -1,11 +1,11 @@
/**
@file
@brief Creates a Unique ID based on system time in a friendly format
@brief Creates a unique ID based on system time in friendly format
@details format = YYYYMMDD_HHMMSSmmm_<sysjobid>_<3randomDigits>
%put %mf_uid();
@version 9.2
@version 9.3
@author Allan Bowe
**/
@@ -14,7 +14,7 @@
)/*/STORE SOURCE*/;
%local today now;
%let today=%sysfunc(today(),yymmddn8.);
%let now=%sysfunc(compress(%sysfunc(time(),time12.3),:.));
%let now=%sysfunc(compress(%sysfunc(time(),tod12.3),:.));
&today._&now._&sysjobid._%sysevalf(%sysfunc(ranuni(0))*999,CEIL)

View File

@@ -6,11 +6,11 @@
results back to the client in an STP Web App context, or completely stop
in the case of a batch run.
Using SAS Abort Cancel mechanisms can cause hung sessions in some Stored Process
environments. This macro takes a unique approach - we set the SAS syscc to 0,
run `stpsrvset('program error', 0)` (if SAS 9) and then - we open a macro
but don't close it! This provides a graceful abort for SAS web services in all
web enabled environments.
Using SAS Abort Cancel mechanisms can cause hung sessions in some Stored
Process environments. This macro takes a unique approach - we set the SAS
syscc to 0, run `stpsrvset('program error', 0)` (if SAS 9) and then - we open
a macro but don't close it! This provides a graceful abort for SAS web
services in all web enabled environments.
@param mac= to contain the name of the calling macro
@param msg= message to be returned
@@ -48,7 +48,10 @@
input; putlog _infile_;
i=1;
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_);
logonce+1;
end;

View File

@@ -4,7 +4,8 @@
@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
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)

View File

@@ -23,7 +23,7 @@
%macro mp_cleancsv(in=NOTPROVIDED,out=NOTPROVIDED,qchar='22'x);
%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;
%end;

View File

@@ -57,8 +57,11 @@
)/*/STORE SOURCE*/;
%let getattrs=%upcase(&getattrs)XX;
data &outds (compress=no keep=file_or_folder filepath filename ext msg directory);
length directory filepath $500 fref fref2 $8 file_or_folder $6 filename $80 ext $20 msg $200;
data &outds(compress=no
keep=file_or_folder filepath filename ext msg directory
);
length directory filepath $500 fref fref2 $8 file_or_folder $6 filename $80
ext $20 msg $200;
%if &fref=0 %then %do;
rc = filename(fref, "&path");
%end;
@@ -98,7 +101,7 @@ data &outds (compress=no keep=file_or_folder filepath filename ext msg directory
if index(fmsg,'File is in use') or index(dmsg,'is not a directory')
then file_or_folder='file';
else if index(fmsg, 'Insufficient authorization') then file_or_folder='file';
else if index(fmsg,'Insufficient authorization') then file_or_folder='file';
else if file_or_folder='' then file_or_folder='locked';
if file_or_folder='file' then do;

View File

@@ -14,16 +14,18 @@
- explicity setting a unix LF
- constraints / indexes etc
@param [in] base_ds= Should be two level - eg work.blah. This is the table that
is converted to a cards file.
@param [in] tgt_ds= Table that the generated cards file would create. Optional -
if omitted, will be same as BASE_DS.
@param [in] base_ds= Should be two level - eg work.blah. This is the table
that is converted to a cards file.
@param [in] tgt_ds= Table that the generated cards file would create.
Optional - if omitted, will be same as BASE_DS.
@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] showlog= whether to show generated cards file in the SAS log (YES/NO)
@param [in] maxobs= to limit output to the first <code>maxobs</code>
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] append= If NO then will rebuild the cards file if it already exists,
otherwise will append to it. Used by the mp_lib2cards.sas macro.
@param [in] append= If NO then will rebuild the cards file if it already
exists, otherwise will append to it. Used by the mp_lib2cards.sas macro.
@version 9.2
@@ -64,9 +66,11 @@ select count(*) into: nvars from dictionary.columns
%end;
/* get indexes */
proc sort data=sashelp.vindex
(where=(upcase(libname)="%scan(%upcase(&base_ds),1)"
and upcase(memname)="%scan(%upcase(&base_ds),2)"))
proc sort
data=sashelp.vindex(
where=(upcase(libname)="%scan(%upcase(&base_ds),1)"
and upcase(memname)="%scan(%upcase(&base_ds),2)")
)
out=_data_;
by indxname indxpos;
run;

View File

@@ -133,7 +133,9 @@ run;
if notnull='yes' then notnul=' not null';
if notnull='no' and missing(label) then put ' ' name typ;
else if notnull='yes' and missing(label) then put ' ' name typ '[' notnul ']';
else if notnull='yes' and missing(label) then do;
put ' ' name typ '[' notnul ']';
end;
else if notnull='no' then put ' ' name typ '[' lab ']';
else put ' ' name typ '[' notnul ',' lab ']';
@@ -166,7 +168,7 @@ run;
call symputx('constcheck',1);
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;
curds="&curds";
@@ -176,7 +178,8 @@ 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);
set &idxinfo (where=(
libname="%scan(&curds,1,.)"
@@ -187,7 +190,7 @@ run;
file &outref mod;
by idxusage indxname;
name=upcase(name);
if &constcheck=1 then stop; /* in fact we only care about PKs so stop if we have */
if &constcheck=1 then stop; /* we only care about PKs so stop if we have */
if _n_=1 and &constcheck=0 then put / ' indexes {';
length cols $5000;
@@ -261,7 +264,11 @@ run;
line='Ref: "'!!"&curds"
!!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;
run;
@@ -282,7 +289,9 @@ run;
create table &pkds.5b as
select curds,count(*) as cnt
from &pkds.5a
where curds not in (select curds from &pkds.2 where cols="&pkcols") /* not a one to one match */
where curds not in (
select curds from &pkds.2 where cols="&pkcols"
) /* not a one to one match */
and curds ne "&curds" /* exclude self */
group by 1;
create table &pkds.6 as

View File

@@ -86,7 +86,9 @@ create table _data_ as
%global constraints_used;
data _null_;
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;
by constraint_type constraint_name;
retain constraints_used;
@@ -161,10 +163,19 @@ run;
put ');';
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_;
*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;
by idxusage indxname;
/* ds=cats(libname,'.',memname); */
@@ -228,10 +239,19 @@ run;
/* Extra step for data constraints */
%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_;
*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;
by idxusage indxname;
*ds=cats(libname,'.',memname);
@@ -320,15 +340,24 @@ run;
put ');';
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_;
*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;
by idxusage indxname;
/* ds=cats(libname,'.',memname); */
if first.indxname then do;
put 'CREATE UNIQUE INDEX "' indxname +(-1) '" ' "ON &schema..&curds (" ;
put 'CREATE UNIQUE INDEX "' indxname +(-1) '" ' "ON &schema..&curds(";
put ' "' name +(-1) '"' ;
end;
else put ' ,"' name +(-1) '"';

View File

@@ -8,10 +8,11 @@
- NAME Name of the base dataset column
- MAXLEN Maximum length of the data contained therein.
Character fields may be allocated very large widths (eg 32000) of which the maximum
value is likely to be much narrower. This macro was designed to enable a HTML
table to be appropriately sized however this could be used as part of a data
audit to ensure we aren't over-sizing our tables in relation to the data therein.
Character fields may be allocated very large widths (eg 32000) of which the
maximum value is likely to be much narrower. This macro was designed to
enable a HTML table to be appropriately sized however this could be used as
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.
Usage:

View File

@@ -83,7 +83,8 @@
%end;
data _null_;file &jref mod ;
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_;
by varnum;
@@ -122,7 +123,8 @@
%end;
%end;
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';
data _null_; file _sjs lrecl=131068 encoding='utf-8' mod;
set &tempds;

98
base/mp_mdtablewrite.sas Normal file
View 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;

View File

@@ -79,7 +79,7 @@
%end;
%end;
%else %if &action=FETCH %then %do;
if &record > &prefix._key then putlog "Not enough records in &Prefix._hash yet";
if &record>&prefix._key then putlog "Not enough records in &Prefix._hash yet";
else &prefix._rc=&prefix._HASH.find(key: &prefix._KEY - &record);
if &prefix._rc then putlog &prefix._rc= " when fetching " &prefix._KEY=
"with record &record and " _n_=;

View File

@@ -20,9 +20,10 @@
@param ds= the dataset to search (leave blank to search entire library)
@param string= the string value to search
@param numval= the numeric value to search (must be exact)
@param outloc= the directory in which to create the output datasets with 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 outloc= the directory in which to create the output datasets with
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 filter_text= add a (valid) filter clause to further filter the results
<h4> SAS Macros </h4>
@@ -44,7 +45,8 @@
,filter_text=%str(1=1)
)/*/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.);
%if &syscc ge 4 %then %do;
@@ -101,7 +103,8 @@ proc sql
%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;
%put %str(WAR)NING: SQLRC=&sqlrc when processing &table;
%return;

View File

@@ -1,7 +1,8 @@
/**
@file
@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):
proc sql;

View File

@@ -6,7 +6,8 @@
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;
%mp_streamfile(contenttype=csv,inloc=/some/where.txt,outname=myfile.txt)
@@ -65,13 +66,15 @@
%else %if &contentype=XLSX %then %do;
%if &platform=SASMETA %then %do;
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");
run;
%end;
%else %if &platform=SASVIYA %then %do;
filename _webout filesrvc parenturi="&SYS_JES_JOB_URI" name='_webout.xls'
contenttype='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
contenttype=
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
contentdisp="attachment; filename=&outname";
%end;
%end;

View File

@@ -8,8 +8,11 @@
Credits:
* Roger Deangelis, https://communities.sas.com/t5/SAS-Programming/listing-all-files-within-a-directory-and-subdirectories/m-p/332616/highlight/true#M74887
* Tom, https://communities.sas.com/t5/SAS-Programming/listing-all-files-of-all-types-from-all-subdirectories/m-p/334113/highlight/true#M75419
Roger Deangelis:
https://communities.sas.com/t5/SAS-Programming/listing-all-files-within-a-directory-and-subdirectories/m-p/332616/highlight/true#M74887
Tom:
https://communities.sas.com/t5/SAS-Programming/listing-all-files-of-all-types-from-all-subdirectories/m-p/334113/highlight/true#M75419
@param dir= Directory to be scanned (default=/tmp)

View File

@@ -117,7 +117,8 @@ run;
%end;
%else %if &engine=REMOTE %then %do;
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;
rcCon = metadata_getnasn("&liburi", "LibraryConnection", 1, uriCon);
@@ -129,7 +130,8 @@ run;
rc = metadata_getattr(uriProp , "DefaultValue",PropertyValue);
rc = metadata_getattr(uriProp , "PropertyName",PropertyName);
rc = metadata_getattr(uriProp , "Delimiter",Delimiter);
properties = trim(properties) !! " " !! trim(PropertyName) !! trim(Delimiter) !! trim(PropertyValue);
properties = trim(properties) !! " " !! trim(PropertyName)
!! trim(Delimiter) !! trim(PropertyValue);
output;
k+1;
rcProp = metadata_getnasn(uriCon, "Properties", k, uriProp);
@@ -170,7 +172,8 @@ run;
else if value='Connection.OLE.Property.PROVIDER.Name.xmlKey.txt' then do;
rc4=metadata_getattr(conprop_uri,'DefaultValue',provider);
end;
else if value='Connection.OLE.Property.PROPERTIES.Name.xmlKey.txt' then do;
else if value='Connection.OLE.Property.PROPERTIES.Name.xmlKey.txt' then
do;
rc5=metadata_getattr(conprop_uri,'DefaultValue',properties);
end;
end;
@@ -357,7 +360,8 @@ run;
call symputx('authdomain',authdomain,'l');
/* 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');
/* schema */
@@ -366,14 +370,16 @@ run;
call symputx('schema',schema,'l');
run;
%put NOTE: Executing the following:/; %put NOTE-;
%put NOTE- libname &libref ORACLE path=&path schema=&schema authdomain=&authdomain;
%put NOTE- libname &libref ORACLE path=&path schema=&schema;
%put NOTE- authdomain=&authdomain;
%put NOTE-;
libname &libref ORACLE path=&path schema=&schema authdomain=&authdomain;
%end;
%else %if &engine=SQLSVR %then %do;
%put NOTE: Obtaining &engine library details;
data _null;
length assocuri1 assocuri2 assocuri3 authdomain path schema userid passwd $256;
length assocuri1 assocuri2 assocuri3 authdomain path schema userid
passwd $256;
call missing (of _all_);
rc=metadata_getnasn("&liburi",'DefaultLogin',1,assocuri1);
@@ -384,7 +390,8 @@ run;
/* path */
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');
/* schema */
@@ -394,15 +401,17 @@ run;
run;
%put NOTE: Executing the following:/; %put NOTE-;
%put NOTE- libname &libref SQLSVR datasrc=&path schema=&schema user="&user" pass="XXX";
%put NOTE- libname &libref SQLSVR datasrc=&path schema=&schema ;
%put NOTE- user="&user" pass="XXX";
%put NOTE-;
libname &libref SQLSVR datasrc=&path schema=&schema user="&user" pass="&pass" ;
libname &libref SQLSVR datasrc=&path schema=&schema user="&user" pass="&pass";
%end;
%else %if &engine=TERADATA %then %do;
%put NOTE: Obtaining &engine library details;
data _null;
length assocuri1 assocuri2 assocuri3 authdomain path schema userid passwd $256;
length assocuri1 assocuri2 assocuri3 authdomain path schema userid
passwd $256;
call missing (of _all_);
/* get auth domain */
@@ -421,7 +430,8 @@ run;
/* path */
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');
/* schema */
@@ -431,7 +441,8 @@ run;
run;
%put NOTE: Executing the following:/; %put NOTE-;
%put NOTE- libname &libref TERADATA server=&path schema=&schema authdomain=&authdomain;
%put NOTE- libname &libref TERADATA server=&path schema=&schema ;
%put NOTe- authdomain=&authdomain;
%put NOTE-;
libname &libref TERADATA server=&path schema=&schema authdomain=&authdomain;

View File

@@ -14,7 +14,8 @@
@li mp_abort.sas
@param libref the libref (not name) of the metadata library
@param mAbort= If not assigned, HARD will call %mp_abort(), SOFT will silently return
@param mAbort= If not assigned, HARD will call %mp_abort(), SOFT will
silently return
@returns libname statement

View File

@@ -13,7 +13,9 @@
,params= name1=value1&#x0a;name2=value2&#x0a;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>
@li mp_abort.sas

View File

@@ -55,7 +55,7 @@ data _null_;
* must have a starting slash ;
if ( substr(folderPath,1,1) ne '/' ) then do;
put "%str(ERR)OR: &sysmacroname PATH parameter value must have starting slash";
put "%str(ERR)OR: &sysmacroname PATH param value must have starting slash";
stop;
end;
@@ -123,9 +123,10 @@ run;
%local inmeta;
%put creating: &path;
%let inmeta=<AddMetadata><Reposid>$METAREPOSITORY</Reposid><Metadata>
<Tree Name='&child' PublicType='Folder' TreeType='BIP Folder' UsageVersion='1000000'>
<ParentTree><Tree ObjRef='&parentFolderObjId'/></ParentTree></Tree></Metadata>
<NS>SAS</NS><Flags>268435456</Flags></AddMetadata>;
<Tree Name='&child' PublicType='Folder' TreeType='BIP Folder'
UsageVersion='1000000'><ParentTree><Tree ObjRef='&parentFolderObjId'/>
</ParentTree></Tree></Metadata><NS>SAS</NS><Flags>268435456</Flags>
</AddMetadata>;
proc metadata in="&inmeta" out=__newdir verbose;
run ;

View File

@@ -166,7 +166,7 @@ filename &frefout temp;
putlog (_all_)(=);
run;
%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;
%end;

View File

@@ -210,7 +210,8 @@ run;
rc3=METADATA_SETATTR(prompturi, 'GroupType','2');
rc4=METADATA_SETATTR(prompturi, 'Name','Parameters');
rc5=METADATA_SETATTR(prompturi, 'PublicType','Embedded:PromptGroup');
GroupInfo="<PromptGroup promptId='PromptGroup_%sysfunc(datetime())_&sysprocessid'"
GroupInfo=
"<PromptGroup promptId='PromptGroup_%sysfunc(datetime())_&sysprocessid'"
!!" version='1.0'><Label><Text xml:lang='en-GB'>Parameters</Text>"
!!"</Label></PromptGroup>";
rc6 = METADATA_SETATTR(prompturi, 'GroupInfo',groupinfo);

View File

@@ -22,7 +22,7 @@ Usage:
%webout(OBJ,example2) * Object format, easier to work with ;
%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>
@li mm_createstp.sas
@@ -119,7 +119,8 @@ data _null_;
put ' %end; ';
put ' data _null_;file &jref mod ; ';
put ' put "["; call symputx(''cols'',0,''l''); ';
put ' proc sort data=sashelp.vcolumn(where=(libname=''WORK'' & memname="%upcase(&ds)")) ';
put ' proc sort ';
put ' data=sashelp.vcolumn(where=(libname=''WORK'' & memname="%upcase(&ds)")) ';
put ' out=_data_; ';
put ' by varnum; ';
put ' ';
@@ -158,7 +159,8 @@ data _null_;
put ' %end; ';
put ' %end; ';
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 ' data _null_; file _sjs lrecl=131068 encoding=''utf-8'' mod; ';
put ' set &tempds; ';

92
meta/mm_deletelibrary.sas Normal file
View File

@@ -0,0 +1,92 @@
/**
@file
@brief Deletes a library by Name
@details Used to delete a library.
Usage:
%* create a library in the home directory ;
%mm_createlibrary(
libname=My Temp Library,
libref=XXTEMPXX,
tree=/User Folders/&sysuserid,
directory=%sysfunc(pathname(work))
)
%* delete the library ;
%mm_deletelibrary(name=My Temp Library)
After running the above, the following will be shown in the log:
![](https://i.imgur.com/Y4Tog24.png)
@param [in] name= the name (not libref) of the library to be deleted
<h4> SAS Macros </h4>
@li mf_getuniquefileref.sas
@li mp_abort.sas
@version 9.4
@author Allan Bowe
**/
%macro mm_deletelibrary(
name=
)/*/STORE SOURCE*/;
/**
* Check if library exists and get uri
*/
data _null_;
length type uri $256;
rc=metadata_resolve("omsobj:SASLibrary?@Name='&name'",type,uri);
call symputx('checktype',type,'l');
call symputx('liburi',uri,'l');
putlog (_all_)(=);
run;
%if &checktype ne SASLibrary %then %do;
%put &sysmacroname: Library (&name) was not found, and so will not be deleted;
%return;
%end;
%local fname1 fname2;
%let fname1=%mf_getuniquefileref();
%let fname2=%mf_getuniquefileref();
filename &fname1 temp lrecl=10000;
filename &fname2 temp lrecl=10000;
data _null_ ;
file &fname1 ;
put "<DeleteMetadata><Metadata><SASLibrary Id='&liburi'/>";
put "</Metadata><NS>SAS</NS><Flags>268436480</Flags><Options/>";
put "</DeleteMetadata>";
run ;
proc metadata in=&fname1 out=&fname2 verbose;run;
/* list the result */
data _null_;infile &fname2; input; list; run;
filename &fname1 clear;
filename &fname2 clear;
/**
* Check deletion
*/
%local isgone;
data _null_;
length type uri $256;
rc=metadata_resolve("omsobj:SASLibrary?@Id='&liburi'",type,uri);
call symputx('isgone',type,'l');
run;
%mp_abort(iftrue=(&isgone = SASLibrary)
,mac=&sysmacroname
,msg=%str(Library (&name) NOT deleted)
)
%put &sysmacroname: Library &name (&liburi) was successfully deleted;
%mend;

View File

@@ -21,8 +21,8 @@
)/*/STORE SOURCE*/;
%if %length(&outds)>30 %then %do;
%put %str(ERR)OR: Temp tables are created with the &outds prefix, which therefore
needs to be 30 characters or less;
%put %str(ERR)OR: Temp tables are created with the &outds prefix, which
therefore needs to be 30 characters or less;
%return;
%end;
%if %index(&outds,'.')>0 %then %do;

View File

@@ -39,8 +39,10 @@ data &outds (keep=directoryuri name directoryname directorydesc );
do while
(metadata_getnobj("omsobj:Directory?@Id contains '.'",__i,directoryuri)>0);
%end; %else %do;
do while
(metadata_getnobj("omsobj:Directory?@DirectoryName='&path'",__i,directoryuri)>0);
do while(
metadata_getnobj("omsobj:Directory?@DirectoryName='&path'",__i,directoryuri)
>0
);
%end;
__rc1=metadata_getattr(directoryuri, "Name", name);
__rc2=metadata_getattr(directoryuri, "DirectoryName", directoryname);

View File

@@ -11,7 +11,8 @@
%mm_getfoldermembers(root=/User Folders/&sysuserid, outds=usercontent)
@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
<h4> Data Outputs </h4>

View File

@@ -13,7 +13,8 @@
options notes source;
@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
<h4> SAS Macros </h4>

View File

@@ -11,7 +11,8 @@
@param [in] user= the metadata user to return groups for. Leave blank for all
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 [out] outds= the dataset to create that contains the list of groups
@@ -39,7 +40,8 @@
%&mD.put Executing mm_getGroups.sas;
%&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;
options metarepository=&repo;
%end;

129
meta/mm_getlibmetadiffs.sas Normal file
View 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;

View File

@@ -46,7 +46,8 @@ filename sxlemap temp;
data _null_;
file sxlemap;
put '<SXLEMAP version="1.2" name="SASObjects"><TABLE name="SASObjects">';
put "<TABLE-PATH syntax='XPath'>/GetMetadataObjects/Objects/&type</TABLE-PATH>";
put "<TABLE-PATH syntax='XPath'>/GetMetadataObjects/Objects/&type";
put "</TABLE-PATH>";
put '<COLUMN name="id">';
put "<PATH syntax='XPath'>/GetMetadataObjects/Objects/&type/@Id</PATH>";
put "<TYPE>character</TYPE><DATATYPE>string</DATATYPE><LENGTH>200</LENGTH>";

View File

@@ -1,7 +1,8 @@
/**
@file mm_getpublictypes.sas
@brief Creates a dataset with all deployable public types
@details More info: https://support.sas.com/documentation/cdl/en/bisag/65422/HTML/default/viewer.htm#n1nkrdzsq5iunln18bk2236istkb.htm
@details More info:
https://support.sas.com/documentation/cdl/en/bisag/65422/HTML/default/viewer.htm#n1nkrdzsq5iunln18bk2236istkb.htm
Usage:

View File

@@ -44,61 +44,76 @@ filename sxlemap temp;
data _null_;
file sxlemap;
put '<SXLEMAP version="1.2" name="SASRepos"><TABLE name="SASRepos">';
put "<TABLE-PATH syntax='XPath'>/GetRepositories/Repositories/Repository</TABLE-PATH>";
put "<TABLE-PATH syntax='XPath'>/GetRepositories/Repositories/Repository";
put "</TABLE-PATH>";
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 '</COLUMN>';
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 '</COLUMN>';
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 '</COLUMN>';
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 '</COLUMN>';
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 '</COLUMN>';
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 '</COLUMN>';
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 '</COLUMN>';
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 '</COLUMN>';
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 '</COLUMN>';
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 '</COLUMN>';
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 '</COLUMN>';
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 '</COLUMN>';
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 '</COLUMN>';
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 '</COLUMN>';
put '</TABLE></SXLEMAP>';

View File

@@ -44,15 +44,19 @@ filename sxlemap temp;
data _null_;
file sxlemap;
put '<SXLEMAP version="1.2" name="roles"><TABLE name="roles">';
put "<TABLE-PATH syntax='XPath'>/GetMetadataObjects/Objects/IdentityGroup</TABLE-PATH>";
put "<TABLE-PATH syntax='XPath'>/GetMetadataObjects/Objects/IdentityGroup";
put "</TABLE-PATH>";
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 '</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 '</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 '</COLUMN></TABLE></SXLEMAP>';
run;

View File

@@ -23,9 +23,10 @@
combine with the <code>tree=</code> parameter.
@param outds= the dataset to create that contains the list of stps.
@param mDebug= set to 1 to show debug messages in the log
@param showDesc= provide a non blank value to return stored process descriptions
@param showUsageVersion= provide a non blank value to return the UsageVersion. This
is either 1000000 (type 1, 9.2) or 2000000 (type2, 9.3 onwards).
@param showDesc= provide a non blank value to return stored process
descriptions
@param showUsageVersion= provide a non blank value to return the UsageVersion.
This is either 1000000 (type 1, 9.2) or 2000000 (type2, 9.3 onwards).
@returns outds dataset containing the following columns
- stpuri

View File

@@ -1,7 +1,7 @@
/**
@file mm_gettableid.sas
@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.
Usage:

View File

@@ -35,7 +35,7 @@ proc metadata in=
<!-- include <REPOSID> XML element and a repository identifier -->
<Reposid>$METAREPOSITORY</Reposid>
</Options>
</GetTypes>'
</GetTypes>'
out=response;
run;

View File

@@ -48,7 +48,8 @@ filename sxlemap temp;
data _null_;
file sxlemap;
put '<SXLEMAP version="1.2" name="SASObjects"><TABLE name="SASObjects">';
put "<TABLE-PATH syntax='XPath'>/GetMetadataObjects/Objects/Person</TABLE-PATH>";
put "<TABLE-PATH syntax='XPath'>/GetMetadataObjects/Objects/Person";
put "</TABLE-PATH>";
put '<COLUMN name="uri">';
put "<PATH syntax='XPath'>/GetMetadataObjects/Objects/Person/@Id</PATH>";
put "<TYPE>character</TYPE><DATATYPE>string</DATATYPE><LENGTH>32</LENGTH>";

View File

@@ -12,7 +12,8 @@
Usage:
%* 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;
%* create sample text file as input to the macro;
@@ -92,7 +93,8 @@
%let port=%sysfunc(getoption(metaport));
%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)

View File

@@ -15,7 +15,8 @@
Usage:
%* 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;
%* export everything;
@@ -104,7 +105,8 @@ filename sxlemap temp;
data _null_;
file sxlemap;
put '<SXLEMAP version="1.2" name="SASObjects"><TABLE name="SASObjects">';
put "<TABLE-PATH syntax='XPath'>/GetMetadataObjects/Objects/Tree</TABLE-PATH>";
put "<TABLE-PATH syntax='XPath'>/GetMetadataObjects/Objects/Tree";
put "</TABLE-PATH>";
put '<COLUMN name="pathuri">';
put "<PATH syntax='XPath'>/GetMetadataObjects/Objects/Tree/@Id</PATH>";
put "<TYPE>character</TYPE><DATATYPE>string</DATATYPE><LENGTH>64</LENGTH>";

View File

@@ -10,8 +10,8 @@
@param target= full path to the STP being deleted
@param type= Either WKS or STP depending on whether Workspace or Stored Process
type required
@param type= Either WKS or STP depending on whether Workspace or
Stored Process type required
@version 9.4
@author Allan Bowe
@@ -51,7 +51,8 @@ data _null_;
n+1;
rc=metadata_getattr(uri,"Name",name);
if name='Stored Process' then do;
rc = METADATA_SETATTR(uri,'StoredText','<?xml version="1.0" encoding="UTF-8"?>'
rc = METADATA_SETATTR(uri,'StoredText'
,'<?xml version="1.0" encoding="UTF-8"?>'
!!'<StoredProcess><ServerContext LogicalServerType="'!!"&newtype"
!!'" OtherAllowed="false"/><ResultCapabilities Package="false" '
!!' Streaming="true"/><OutputParameters/></StoredProcess>');

View File

@@ -20,7 +20,8 @@ Usage:
run;
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
,outspkpath=%str(/tmp)
)
@@ -56,7 +57,8 @@ Usage:
/* get creds */
%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)

1688
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
{
"name": "@sasjs/core",
"description": "SAS Macro Library for Application Development",
"description": "Production Ready Macros for SAS Application Developers",
"license": "MIT",
"keywords": [
"SAS",
@@ -29,5 +29,14 @@
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"docs": "sasjs doc && ./sasjs/utils/build.sh"
},
"devDependencies": {
"@sasjs/cli": "^2.14.2",
"ghooks": "^2.0.4"
},
"config": {
"ghooks": {
"pre-commit": "sasjs lint"
}
}
}

View File

@@ -14,20 +14,20 @@ HIDE_SCOPE_NAMES = YES
HIDE_UNDOC_CLASSES = YES
HIDE_UNDOC_MEMBERS = YES
HTML_OUTPUT = $(DOXY_HTML_OUTPUT)
HTML_HEADER = $(DOXY_CONTENT)new_header.html
HTML_FOOTER = $(DOXY_CONTENT)new_footer.html
HTML_EXTRA_STYLESHEET = $(DOXY_CONTENT)new_stylesheet.css
HTML_HEADER = $(HTML_HEADER)
HTML_EXTRA_FILES = $(HTML_EXTRA_FILES)
HTML_FOOTER = $(HTML_FOOTER)
HTML_EXTRA_STYLESHEET = $(HTML_EXTRA_STYLESHEET)
INHERIT_DOCS = NO
INLINE_INFO = NO
INPUT = $(DOXY_CONTENT)../../README.md \
= $(DOXY_CONTENT)../../main.dox \
$(DOXY_INPUT)
INPUT = $(DOXY_INPUT)
INPUT += main.dox
LAYOUT_FILE = $(LAYOUT_FILE)
USE_MDFILE_AS_MAINPAGE = README.md
LAYOUT_FILE = $(DOXY_CONTENT)DoxygenLayout.xml
MAX_INITIALIZER_LINES = 0
PROJECT_NAME = Macro Core
PROJECT_LOGO = $(DOXY_CONTENT)Macro_core_website_1.png
PROJECT_BRIEF = "Production Ready Macros for SAS Application Developers"
PROJECT_NAME = $(PROJECT_NAME)
PROJECT_LOGO = $(PROJECT_LOGO)
PROJECT_BRIEF = $(PROJECT_BRIEF)
RECURSIVE = YES
REPEAT_BRIEF = NO
SHOW_NAMESPACES = NO

View File

@@ -1,6 +0,0 @@
ECHO on
rmdir /s /q C:\DEVELOPMENT\output
mkdir C:\DEVELOPMENT\output
doxygen.exe Doxyfile

26
sasjs/doxy/doxygen.svg Normal file

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

BIN
sasjs/doxy/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.5 KiB

View File

@@ -8,7 +8,7 @@
<li class="footer">
$generatedby
<a href="https://www.doxygen.org/index.html">
<img class="footer" src="$relpath^doxygen.png" alt="doxygen"
<img class="footer" src="$relpath^doxygen.svg" alt="doxygen"
/></a>
$doxygenversion
</li>
@@ -24,9 +24,10 @@
<address class="footer">
<small>
$generatedby &#160;<a href="http://www.doxygen.org/index.html">
<img class="footer" src="$relpath^doxygen.png" alt="doxygen" />
<img class="footer" src="$relpath^doxygen.svg" alt="doxygen" />
</a>
$doxygenversion
</small>
</address>
<!--END !GENERATE_TREEVIEW-->

View File

@@ -9,7 +9,7 @@
<meta name="generator" content="Doxygen $doxygenversion" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<!--BEGIN PROJECT_NAME-->
<title>$projectname: $title</title>
<meta name="description" content="$projectbrief" />
<!--END PROJECT_NAME-->
<!--BEGIN !PROJECT_NAME-->
<title>$title</title>
@@ -19,7 +19,7 @@
<script type="text/javascript" src="$relpath^dynsections.js"></script>
$treeview $search $mathjax
<link href="$relpath^$stylesheet" rel="stylesheet" type="text/css" />
<link rel="icon" href="https://sasjs.io/img/runningman.jpg" />
<link rel="shortcut icon" href="$relpath^favicon.ico" type="image/x-icon" />
$extrastylesheet
</head>
<body>
@@ -33,52 +33,24 @@
<tr style="height: 56px">
<!--BEGIN PROJECT_LOGO-->
<td id="projectlogo">
<img alt="Logo" src="$relpath^$projectlogo" />
<a href="$relpath^"
><img alt="Logo" src="$relpath^$projectlogo"
/></a>
</td>
<!--END PROJECT_LOGO-->
<!--BEGIN PROJECT_NAME-->
<td id="projectalign" style="padding-left: 0.5em">
<div id="projectname">
<!--BEGIN PROJECT_NUMBER-->&#160;<span id="projectnumber"
>$projectnumber</span
><!--END PROJECT_NUMBER-->
</div>
<!--BEGIN PROJECT_BRIEF-->
<div id="projectbrief">
Production Ready Macros for SAS Application Developers<br />
<a href="https://github.com/sasjs/core">
https://github.com/sasjs/core
</a>
</div>
<meta name="Description" content="$projectbrief" />
<!--END PROJECT_BRIEF-->
</td>
<!--END PROJECT_NAME-->
<!--BEGIN !PROJECT_NAME-->
<!--BEGIN PROJECT_BRIEF-->
<td style="padding-left: 0.5em">
<div id="projectbrief">$projectbrief</div>
<table
style="padding-left: 2em"
cellspacing="0"
cellpadding="0"
>
<tr>
<td>
Production Ready Macros for SAS Application Developers
</td>
</tr>
<tr>
<td>
<a href="https://github.com/sasjs/core">
https://github.com/sasjs/core
</a>
</td>
</tr>
</table>
</td>
<!--END PROJECT_BRIEF-->
<!--END !PROJECT_NAME-->
<!--BEGIN DISABLE_INDEX-->
<!--BEGIN SEARCHENGINE-->
<td>$searchbox</td>

View File

@@ -2,6 +2,12 @@
"$schema": "https://cli.sasjs.io/sasjsconfig-schema.json",
"macroFolders": ["base", "meta", "metax", "viya", "lua"],
"docConfig":{
"displayMacroCore": false
"displayMacroCore": false,
"enableLineage": false,
"doxyContent": {
"favIcon": "runningman.jpg",
"logo": "Macro_core_website_1.png",
"readMe": "../../README.md"
}
}
}

View File

@@ -5,7 +5,8 @@
Code is passed in as one or more filerefs.
%* Step 1 - compile macros ;
filename mc url "https://raw.githubusercontent.com/sasjs/core/main/all.sas";
filename mc url
"https://raw.githubusercontent.com/sasjs/core/main/all.sas";
%inc mc;
%* Step 2 - Create some SAS code and add it to a job;

View File

@@ -5,7 +5,8 @@
Code is passed in as one or more filerefs.
%* Step 1 - compile macros ;
filename mc url "https://raw.githubusercontent.com/sasjs/core/main/all.sas";
filename mc url
"https://raw.githubusercontent.com/sasjs/core/main/all.sas";
%inc mc;
%* Step 2 - Create some code and add it to a web service;
@@ -18,7 +19,7 @@
run;
%* send data back;
%webout(OPEN)
%webout(ARR,example1) * Array format, fast, suitable for large tables ;
%webout(ARR,example1) * Array format, fast, suitable for large tables;
%webout(OBJ,example2) * Object format, easier to work with ;
%webout(CLOSE)
;;;;
@@ -52,7 +53,8 @@
adapter, add a (different) fileref here.
@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
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
@author Allan Bowe, source: https://github.com/sasjs/core
@@ -256,7 +258,8 @@ data _null_;
put ' %end; ';
put ' data _null_;file &jref mod ; ';
put ' put "["; call symputx(''cols'',0,''l''); ';
put ' proc sort data=sashelp.vcolumn(where=(libname=''WORK'' & memname="%upcase(&ds)")) ';
put ' proc sort ';
put ' data=sashelp.vcolumn(where=(libname=''WORK'' & memname="%upcase(&ds)")) ';
put ' out=_data_; ';
put ' by varnum; ';
put ' ';
@@ -295,7 +298,8 @@ data _null_;
put ' %end; ';
put ' %end; ';
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 ' data _null_; file _sjs lrecl=131068 encoding=''utf-8'' mod; ';
put ' set &tempds; ';
@@ -410,7 +414,8 @@ data _null_;
put ' if _n_=1 then call symputx(''input_statement'',_infile_); ';
put ' list; ';
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 ' run; ';
put ' %end; ';
@@ -451,7 +456,8 @@ data _null_;
put ' ';
put ' /* setup temp ref */ ';
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 ' ';
put ' /* setup json */ ';

View File

@@ -1,4 +1,4 @@
/**
/**
@file mv_getaccesstoken.sas
@brief deprecated - replaced by mv_tokenrefresh.sas
@@ -8,9 +8,9 @@
<h4> SAS Macros </h4>
@li mv_tokenrefresh.sas
**/
**/
%macro mv_getaccesstoken(client_id=someclient
%macro mv_getaccesstoken(client_id=someclient
,client_secret=somesecret
,grant_type=authorization_code
,code=

View File

@@ -1,4 +1,4 @@
/**
/**
@file
@brief deprecated - replaced by mv_registerclient.sas
@@ -8,9 +8,9 @@
<h4> SAS Macros </h4>
@li mv_registerclient.sas
**/
**/
%macro mv_getapptoken(client_id=someclient
%macro mv_getapptoken(client_id=someclient
,client_secret=somesecret
,grant_type=authorization_code
);

View File

@@ -10,7 +10,8 @@
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;
Next, create a job (in this case, a web service):

View File

@@ -1,4 +1,4 @@
/**
/**
@file mv_getrefreshtoken.sas
@brief deprecated - replaced by mv_tokenauth.sas
@@ -8,9 +8,9 @@
<h4> SAS Macros </h4>
@li mv_tokenauth.sas
**/
**/
%macro mv_getrefreshtoken(client_id=someclient
%macro mv_getrefreshtoken(client_id=someclient
,client_secret=somesecret
,grant_type=authorization_code
,code=

View File

@@ -13,7 +13,7 @@
@li FLOW_ID - Numeric value, provides sequential ordering capability. Is
optional, will default to 0 if not provided.
@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
variables and passed into the relevant job.
@@ -97,12 +97,14 @@
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:
@li password
@li authorization_code
@li detect - will check if access_token exists, if not will use sas_services if
a SASStudioV session else authorization_code. Default option.
@li detect - will check if access_token exists, if not will use
sas_services if a SASStudioV session else authorization_code. Default
option.
@li sas_services - will use oauth_bearer=sas_services
@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.
@@ -185,7 +187,7 @@ select count(*) into: missings
where flow_id is null or _program is null;
%mp_abort(iftrue=(&missings>0)
,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;
@@ -284,7 +286,8 @@ data;run;%let jdswaitfor=&syslast;
%if "&&jobuid&jid"="0" and &concurrency<&maxconcurrency %then %do;
%local jobname jobpath;
%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;
%mv_jobexecute(path=&jobpath
,name=&jobname
@@ -334,7 +337,8 @@ data;run;%let jdswaitfor=&syslast;
/* loop again if jobs are left */
%if &completed < &jcnt %then %do;
%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;

View File

@@ -5,14 +5,16 @@
This macro will obtain the Consul Token and use that to call the Web Service.
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
Usage:
%* 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;
%* specific client with just openid scope;
@@ -33,7 +35,8 @@
@param client_id= The client name. Auto generated if blank.
@param client_secret= Client secret Auto generated if client is blank.
@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 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)
@@ -78,15 +81,16 @@
,refresh_token_validity=DEFAULT
,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=
Generated by %mf_getuser() on %sysfunc(datetime(),datetime19.) using SASjs;
options noquotelenmax;
/* first, get consul token needed to get client id / secret */
%let tokloc=/etc/SASSecurityCertificateFramework/tokens/consul/default;
data _null_;
infile "%mf_loc(VIYACONFIG)/etc/SASSecurityCertificateFramework/tokens/consul/default/client.token";
infile "%mf_loc(VIYACONFIG)&tokloc/client.token";
input token:$64.;
call symputx('consul_token',token);
run;
@@ -97,7 +101,8 @@ run;
/* request the client details */
%let fname1=%mf_getuniquefileref();
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";
run;
@@ -122,7 +127,8 @@ run;
%let scopes=%sysfunc(coalescec(&scopes,openid));
%let scopes=%mf_getquotedstr(&scopes,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_;
file &fname2;
@@ -139,9 +145,11 @@ data _null_;
if reqd_groups = '""' then reqd_groups ='';
else reqd_groups=cats(',"required_user_groups":[',reqd_groups,']');
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'));
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 clientsecret ;
@@ -206,10 +214,12 @@ run;
%put GRANT_TYPE=&grant_type;
%put;
%if %index(%superq(grant_type),authorization_code) %then %do;
/* cannot use base_uri here as it includes the protocol which may be incorrect externally */
%put NOTE: The developer must also register below and select 'openid' to get the grant code:;
/* cannot use base_uri here as it includes the protocol which may be incorrect
externally */
%put NOTE: Visit the link below and select 'openid' to get the grant code:;
%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- ;
%end;

View File

@@ -15,7 +15,8 @@
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;
@@ -31,13 +32,15 @@
@param outds= A dataset containing access_token and refresh_token
@param client_id= The client name
@param client_secret= client secret
@param grant_type= valid values are "password" or "authorization_code" (unquoted).
The default is authorization_code.
@param code= If grant_type=authorization_code then provide the necessary code here
@param grant_type= valid values are "password" or "authorization_code"
(unquoted). The default is authorization_code.
@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 pass= If grant_type=password then provide the password here
@param access_token_var= The global macro variable to contain the access token
@param refresh_token_var= The global macro variable to contain the refresh token
@param refresh_token_var= The global macro variable to contain the refresh
token
@param base_uri= The Viya API server location
@version VIYA V.03.04
@@ -88,7 +91,8 @@
,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
,msg=%str(username / password required)
)

View File

@@ -32,12 +32,13 @@
@param outds= A dataset containing access_token and refresh_token
@param client_id= The client name (alternative to inds)
@param client_secret= client secret (alternative to inds)
@param grant_type= valid values are "password" or "authorization_code" (unquoted).
The default is authorization_code.
@param grant_type= valid values are "password" or "authorization_code"
(unquoted). The default is authorization_code.
@param user= If grant_type=password then provide the username 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 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
@author Allan Bowe, source: https://github.com/sasjs/core
@@ -72,7 +73,8 @@ options noquotelenmax;
,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
,msg=%str(username / password required)
)

View File

@@ -109,7 +109,8 @@
if _n_=1 then call symputx('input_statement',_infile_);
list;
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;
run;
%end;
@@ -150,7 +151,8 @@
/* setup temp ref */
%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;
/* setup json */