mirror of
https://github.com/sasjs/core.git
synced 2025-12-11 22:44:36 +00:00
Compare commits
16 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 2a894419ab | |||
| 58bfc7b4aa | |||
| 818c0f5eae | |||
| dff9e2f387 | |||
| 6c9256e097 | |||
| 0631a05a78 | |||
| 268bdca4e0 | |||
|
|
e38f331ad5 | ||
| 8d64b30419 | |||
| 4a6c8ffbb3 | |||
| b5c86e7031 | |||
| 9783edd0e3 | |||
| 961728a987 | |||
| 4b34322d94 | |||
|
|
fbd8196230 | ||
|
|
5720caaf86 |
3
.gitignore
vendored
3
.gitignore
vendored
@@ -1,2 +1,3 @@
|
||||
node_modules
|
||||
.DS_Store
|
||||
.DS_Store
|
||||
sasjsbuild/
|
||||
|
||||
@@ -1,83 +1,32 @@
|
||||
# Contributing
|
||||
|
||||
Contributions to the macrocore library are warmly welcomed! To avoid any
|
||||
misunderstandings, do please first discuss the change you wish to make via issue,
|
||||
email, or any other method with the owners of this repository before submitting
|
||||
a PR.
|
||||
Contributions are warmly welcomed! To avoid any misunderstandings, do please first discuss the change you wish to make via issue, email, or any other method with the owners of this repository before submitting a PR.
|
||||
|
||||
Please note we have a code of conduct, please follow it in all your interactions
|
||||
with the project.
|
||||
Please note we have a [code of conduct](https://www.contributor-covenant.org/version/2/0/code_of_conduct/), please follow it in all your interactions with the project.
|
||||
|
||||
# Environment Setup
|
||||
|
||||
This repository makes use of the [SASjs](https://sasjs.io) framework for code organisation, compilation, documentation, and deployment. The following tools are highly recommended:
|
||||
|
||||
* [NPM](https://sasjs.io/windows/#npm) - the runtime and dependency manager for [SASjs CLI](https://cli.sasjs.io) (batteries included)
|
||||
* [VSCode](https://sasjs.io/windows/#vscode) - feature packed IDE for code editing (warning - highly effective!)
|
||||
* [GIT](https://sasjs.io/windows/#git) - a safety net you cannot (and should not) do without.
|
||||
|
||||
For generating the documentation (`sasjs doc`) it is also necessary to install [doxygen](https://www.doxygen.nl/manual/install.html).
|
||||
|
||||
|
||||
## Code of Conduct
|
||||
To get configured:
|
||||
|
||||
### Our Pledge
|
||||
1. Clone the repository
|
||||
2. Install local dependencies (`npm install`)
|
||||
3. Install the SASjs CLI globally (`npm install -g @sasjs/cli`)
|
||||
4. Add a target, and authentication (`npm add`). See [docs](https://cli.sasjs.io/add/).
|
||||
|
||||
In the interest of fostering an open and welcoming environment, we as
|
||||
contributors and maintainers pledge to making participation in our project and
|
||||
our community a harassment-free experience for everyone, regardless of age, body
|
||||
size, disability, ethnicity, gender identity and expression, level of experience,
|
||||
nationality, personal appearance, race, religion, or sexual identity and
|
||||
orientation.
|
||||
To contribute:
|
||||
|
||||
### Our Standards
|
||||
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
|
||||
|
||||
Examples of behavior that contributes to creating a positive environment
|
||||
include:
|
||||
|
||||
* Using welcoming and inclusive language
|
||||
* Being respectful of differing viewpoints and experiences
|
||||
* Gracefully accepting constructive criticism
|
||||
* Focusing on what is best for the community
|
||||
* Showing empathy towards other community members
|
||||
|
||||
Examples of unacceptable behavior by participants include:
|
||||
|
||||
* Trolling, insulting/derogatory comments, and personal or political attacks
|
||||
* Public or private harassment
|
||||
* Publishing others' private information, such as a physical or electronic
|
||||
address, without explicit permission
|
||||
* Other conduct which could reasonably be considered inappropriate in a
|
||||
professional setting
|
||||
|
||||
### Our Responsibilities
|
||||
|
||||
Project maintainers are responsible for clarifying the standards of acceptable
|
||||
behavior and are expected to take appropriate and fair corrective action in
|
||||
response to any instances of unacceptable behavior.
|
||||
|
||||
Project maintainers have the right and responsibility to remove, edit, or
|
||||
reject comments, commits, code, wiki edits, issues, and other contributions
|
||||
that are not aligned to this Code of Conduct, or to ban temporarily or
|
||||
permanently any contributor for other behaviors that they deem inappropriate,
|
||||
threatening, offensive, or harmful.
|
||||
|
||||
### Scope
|
||||
|
||||
This Code of Conduct applies both within project spaces and in public spaces
|
||||
when an individual is representing the project or its community. Examples of
|
||||
representing a project or community include using an official project e-mail
|
||||
address, posting via an official social media account, or acting as an appointed
|
||||
representative at an online or offline event. Representation of a project may be
|
||||
further defined and clarified by project maintainers.
|
||||
|
||||
### Enforcement
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||
reported by contacting the project team at support@macropeople.com. All
|
||||
complaints will be reviewed and investigated and will result in a response that
|
||||
is deemed necessary and appropriate to the circumstances. The project team is
|
||||
obligated to maintain confidentiality with regard to the reporter of an incident.
|
||||
Further details of specific enforcement policies may be posted separately.
|
||||
|
||||
Project maintainers who do not follow or enforce the Code of Conduct in good
|
||||
faith may face temporary or permanent repercussions as determined by other
|
||||
members of the project's leadership.
|
||||
|
||||
### Attribution
|
||||
|
||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
|
||||
available at [http://contributor-covenant.org/version/1/4][version]
|
||||
|
||||
[homepage]: http://contributor-covenant.org
|
||||
[version]: http://contributor-covenant.org/version/1/4/
|
||||
335
all.sas
335
all.sas
@@ -218,9 +218,11 @@ options noquotelenmax;
|
||||
%put Supported features: PROCLUA;
|
||||
%end;
|
||||
%else %if &feature=PROCLUA %then %do;
|
||||
/* https://blogs.sas.com/content/sasdummy/2015/08/03/using-lua-within-your-sas-programs */
|
||||
%if &platform=SASVIYA %then 1;
|
||||
%else %if "&sysver"="9.3" or "&sysver"="9.4" %then 1;
|
||||
%else 0;
|
||||
%else %if "&sysver"="9.2" or "&sysver"="9.3" %then 0;
|
||||
%else %if "&SYSVLONG" < "9.04.01M3" %then 0;
|
||||
%else 1;
|
||||
%end;
|
||||
%else %do;
|
||||
-1
|
||||
@@ -404,7 +406,7 @@ options noquotelenmax;
|
||||
%local dsid rc;
|
||||
%let dsid=%sysfunc(open(&libds,is));
|
||||
%if &dsid = 0 %then %do;
|
||||
%put WARNING: Cannot open %trim(&libds), system message below;
|
||||
%put %str(WARN)ING: Cannot open %trim(&libds), system message below;
|
||||
%put %sysfunc(sysmsg());
|
||||
-1
|
||||
%end;
|
||||
@@ -510,8 +512,8 @@ options noquotelenmax;
|
||||
@brief retrieves a key value pair from a control dataset
|
||||
@details By default, control dataset is work.mp_setkeyvalue. Usage:
|
||||
|
||||
%mp_setkeyvalue(someindex,22,type=N)
|
||||
%put %mf_getkeyvalue(someindex)
|
||||
%mp_setkeyvalue(someindex,22,type=N)
|
||||
%put %mf_getkeyvalue(someindex)
|
||||
|
||||
|
||||
@param key Provide a key on which to perform the lookup
|
||||
@@ -2439,25 +2441,27 @@ run;
|
||||
@brief Create a CARDS file from a SAS dataset.
|
||||
@details Uses dataset attributes to convert all data into datalines.
|
||||
Running the generated file will rebuild the original dataset.
|
||||
usage:
|
||||
Usage:
|
||||
|
||||
%mp_ds2cards(base_ds=sashelp.class
|
||||
, cards_file= "C:\temp\class.sas"
|
||||
, maxobs=5)
|
||||
|
||||
stuff to add
|
||||
TODO:
|
||||
- labelling the dataset
|
||||
- explicity setting a unix LF
|
||||
- constraints / indexes etc
|
||||
|
||||
@param 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 that
|
||||
is converted to a cards file.
|
||||
@param tgt_ds= Table that the generated cards file would create. Optional -
|
||||
@param [in] tgt_ds= Table that the generated cards file would create. Optional -
|
||||
if omitted, will be same as BASE_DS.
|
||||
@param cards_file= Location in which to write the (.sas) cards file
|
||||
@param maxobs= to limit output to the first <code>maxobs</code> observations
|
||||
@param showlog= whether to show generated cards file in the SAS log (YES/NO)
|
||||
@param outencoding= provide encoding value for file statement (eg utf-8)
|
||||
@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] 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.
|
||||
|
||||
|
||||
@version 9.2
|
||||
@@ -2470,6 +2474,7 @@ run;
|
||||
,random_sample=NO
|
||||
,showlog=YES
|
||||
,outencoding=
|
||||
,append=NO
|
||||
)/*/STORE SOURCE*/;
|
||||
%local i setds nvars;
|
||||
|
||||
@@ -2482,6 +2487,8 @@ run;
|
||||
%if (&tgt_ds = ) %then %let tgt_ds=&base_ds;
|
||||
%if %index(&tgt_ds,.)=0 %then %let tgt_ds=WORK.%scan(&base_ds,2,.);
|
||||
%if ("&outencoding" ne "") %then %let outencoding=encoding="&outencoding";
|
||||
%if ("&append" = "") %then %let append=;
|
||||
%else %let append=mod;
|
||||
|
||||
/* get varcount */
|
||||
%let nvars=0;
|
||||
@@ -2608,7 +2615,7 @@ data _null_;
|
||||
run;
|
||||
|
||||
data _null_;
|
||||
file &cards_file. &outencoding lrecl=32767 termstr=nl;
|
||||
file &cards_file. &outencoding lrecl=32767 termstr=nl &append;
|
||||
length __attrib $32767;
|
||||
if _n_=1 then do;
|
||||
put '/*******************************************************************';
|
||||
@@ -4011,21 +4018,26 @@ create table &outds (rename=(
|
||||
%mend;/**
|
||||
@file
|
||||
@brief Convert all library members to CARDS files
|
||||
@details Gets list of members then calls the <code>%mp_ds2cards()</code>
|
||||
macro
|
||||
usage:
|
||||
@details Gets list of members then calls the <code>%mp_ds2cards()</code> macro.
|
||||
Usage:
|
||||
|
||||
%mp_lib2cards(lib=sashelp
|
||||
, outloc= C:\temp )
|
||||
%mp_lib2cards(lib=sashelp
|
||||
, outloc= C:\temp )
|
||||
|
||||
The output will be one cards file in the `outloc` directory per dataset in the
|
||||
input `lib` library. If the `outloc` directory does not exist, it is created.
|
||||
|
||||
<h4> SAS Macros </h4>
|
||||
@li mf_mkdir.sas
|
||||
@li mf_trimstr.sas
|
||||
@li mp_ds2cards.sas
|
||||
|
||||
@param lib= Library in which to convert all datasets
|
||||
@param outloc= Location in which to store output. Defaults to WORK library.
|
||||
Do not use a trailing slash (my/path not my/path/). No quotes.
|
||||
@param maxobs= limit output to the first <code>maxobs</code> observations
|
||||
@param [in] lib= Library in which to convert all datasets
|
||||
@param [out] outloc= Location in which to store output. Defaults to WORK
|
||||
library. No quotes.
|
||||
@param [out] outfile= Optional output file NAME - if provided, then will create
|
||||
a single output file instead of one file per input table.
|
||||
@param [in] maxobs= limit output to the first <code>maxobs</code> observations
|
||||
|
||||
@version 9.2
|
||||
@author Allan Bowe
|
||||
@@ -4035,6 +4047,7 @@ create table &outds (rename=(
|
||||
,outloc=%sysfunc(pathname(work)) /* without trailing slash */
|
||||
,maxobs=max
|
||||
,random_sample=NO
|
||||
,outfile=0
|
||||
)/*/STORE SOURCE*/;
|
||||
|
||||
/* Find the tables */
|
||||
@@ -4046,16 +4059,28 @@ select distinct lowcase(memname)
|
||||
from dictionary.tables
|
||||
where upcase(libname)="%upcase(&lib)";
|
||||
|
||||
/* trim trailing slash, if provided */
|
||||
%let outloc=%mf_trimstr(&outloc,/);
|
||||
%let outloc=%mf_trimstr(&outloc,\);
|
||||
|
||||
/* create the output directory */
|
||||
%mf_mkdir(&outloc)
|
||||
|
||||
/* create the cards files */
|
||||
%do x=1 %to %sysfunc(countw(&memlist));
|
||||
%let ds=%scan(&memlist,&x);
|
||||
%mp_ds2cards(base_ds=&lib..&ds
|
||||
,cards_file="&outloc/&ds..sas"
|
||||
,maxobs=&maxobs
|
||||
,random_sample=&random_sample)
|
||||
%let ds=%scan(&memlist,&x);
|
||||
%mp_ds2cards(base_ds=&lib..&ds
|
||||
,maxobs=&maxobs
|
||||
,random_sample=&random_sample
|
||||
%if "&outfile" ne "0" %then %do;
|
||||
,append=YES
|
||||
,cards_file="&outloc/&outfile"
|
||||
%end;
|
||||
%else %do;
|
||||
,append=NO
|
||||
,cards_file="&outloc/&ds..sas"
|
||||
%end;
|
||||
)
|
||||
%end;
|
||||
|
||||
%mend;/**
|
||||
@@ -4548,8 +4573,8 @@ proc sql
|
||||
@brief Logs a key value pair a control dataset
|
||||
@details If the dataset does not exist, it is created. Usage:
|
||||
|
||||
%mp_setkeyvalue(someindex,22,type=N)
|
||||
%mp_setkeyvalue(somenewindex,somevalue)
|
||||
%mp_setkeyvalue(someindex,22,type=N)
|
||||
%mp_setkeyvalue(somenewindex,somevalue)
|
||||
|
||||
<h4> SAS Macros </h4>
|
||||
@li mf_existds.sas
|
||||
@@ -4571,7 +4596,7 @@ proc sql
|
||||
|
||||
%if not (%mf_existds(&libds)) %then %do;
|
||||
data &libds (index=(key/unique));
|
||||
length key $32 valc $256 valn 8 type $1;
|
||||
length key $64 valc $2048 valn 8 type $1;
|
||||
call missing(of _all_);
|
||||
stop;
|
||||
run;
|
||||
@@ -10659,7 +10684,6 @@ run;
|
||||
@details Expects oauth token in a global macro variable (default
|
||||
ACCESS_TOKEN).
|
||||
|
||||
options mprint;
|
||||
%mv_createfolder(path=/Public)
|
||||
|
||||
|
||||
@@ -11509,7 +11533,7 @@ run;
|
||||
%let oauth_bearer=oauth_bearer=sas_services;
|
||||
%let &access_token_var=;
|
||||
%end;
|
||||
%put &sysmacroname: grant_type=&grant_type;
|
||||
|
||||
%mp_abort(iftrue=(&grant_type ne authorization_code and &grant_type ne password
|
||||
and &grant_type ne sas_services
|
||||
)
|
||||
@@ -11764,7 +11788,6 @@ libname &libref1a clear;
|
||||
@details If not running in Studo 5 +, will expect an oauth token in a global
|
||||
macro variable (default ACCESS_TOKEN).
|
||||
|
||||
options mprint;
|
||||
%mv_createfolder(path=/Public/test/blah)
|
||||
%mv_deleteviyafolder(path=/Public/test)
|
||||
|
||||
@@ -12094,7 +12117,7 @@ libname &libref1 clear;
|
||||
%let oauth_bearer=oauth_bearer=sas_services;
|
||||
%let &access_token_var=;
|
||||
%end;
|
||||
%put &sysmacroname: grant_type=&grant_type;
|
||||
|
||||
%mp_abort(iftrue=(&grant_type ne authorization_code and &grant_type ne password
|
||||
and &grant_type ne sas_services
|
||||
)
|
||||
@@ -12278,31 +12301,20 @@ filename &fname1 clear;
|
||||
%mend;/**
|
||||
@file mv_getgroups.sas
|
||||
@brief Creates a dataset with a list of viya groups
|
||||
@details First, be sure you have an access token (which requires an app token).
|
||||
|
||||
Using the macros here:
|
||||
@details First, load the macros:
|
||||
|
||||
filename mc url
|
||||
"https://raw.githubusercontent.com/sasjs/core/main/all.sas";
|
||||
%inc mc;
|
||||
|
||||
An administrator needs to set you up with an access code:
|
||||
Next, execute:
|
||||
|
||||
%mv_registerclient(outds=client)
|
||||
%mv_getgroups(outds=work.groups)
|
||||
|
||||
Navigate to the url from the log (opting in to the groups) and paste the
|
||||
access code below:
|
||||
|
||||
%mv_tokenauth(inds=client,code=wKDZYTEPK6)
|
||||
|
||||
Now we can run the macro!
|
||||
|
||||
%mv_getgroups()
|
||||
|
||||
@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 [in] access_token_var= The global macro variable to contain the access token
|
||||
@param [in] grant_type= valid values are "password" or "authorization_code" (unquoted).
|
||||
The default is authorization_code.
|
||||
@param outds= The library.dataset to be created that contains the list of groups
|
||||
@param [out] outds= The library.dataset to be created that contains the list of groups
|
||||
|
||||
|
||||
@version VIYA V.03.04
|
||||
@@ -12329,7 +12341,7 @@ filename &fname1 clear;
|
||||
%let oauth_bearer=oauth_bearer=sas_services;
|
||||
%let &access_token_var=;
|
||||
%end;
|
||||
%put &sysmacroname: grant_type=&grant_type;
|
||||
|
||||
%mp_abort(iftrue=(&grant_type ne authorization_code and &grant_type ne password
|
||||
and &grant_type ne sas_services
|
||||
)
|
||||
@@ -12422,7 +12434,6 @@ libname &libref1 clear;
|
||||
%let oauth_bearer=oauth_bearer=sas_services;
|
||||
%let &access_token_var=;
|
||||
%end;
|
||||
%put &sysmacroname: grant_type=&grant_type;
|
||||
%mp_abort(iftrue=(&grant_type ne authorization_code and &grant_type ne password
|
||||
and &grant_type ne sas_services
|
||||
)
|
||||
@@ -12487,7 +12498,7 @@ run;
|
||||
%let fname3=%mf_getuniquefileref();
|
||||
%let fpath1=%sysfunc(pathname(&fname1));
|
||||
%let fpath2=%sysfunc(pathname(&fname2));
|
||||
%let fpath3=%sysfunc(pathname(&fname2));
|
||||
%let fpath3=%sysfunc(pathname(&fname3));
|
||||
|
||||
/* compile the lua JSON module */
|
||||
%ml_json()
|
||||
@@ -12520,6 +12531,7 @@ data _null_;
|
||||
run;
|
||||
filename &fname1 clear;
|
||||
filename &fname2 clear;
|
||||
filename &fname3 clear;
|
||||
%mend;
|
||||
/**
|
||||
@file
|
||||
@@ -12572,11 +12584,12 @@ filename &fname2 clear;
|
||||
|
||||
%mv_getjoblog(uri=&uri,outref=mylog)
|
||||
|
||||
This macro is used by the mv_jobwaitfor macro, which is generally a more
|
||||
This macro is used by the mv_jobwaitfor.sas macro, which is generally a more
|
||||
convenient way to wait for the job to finish before fetching the log.
|
||||
|
||||
|
||||
@param [in] access_token_var= The global macro variable to contain the access token
|
||||
@param [in] mdebug= set to 1 to enable DEBUG messages
|
||||
@param [in] grant_type= valid values:
|
||||
@li password
|
||||
@li authorization_code
|
||||
@@ -12604,6 +12617,7 @@ filename &fname2 clear;
|
||||
,contextName=SAS Job Execution compute context
|
||||
,access_token_var=ACCESS_TOKEN
|
||||
,grant_type=sas_services
|
||||
,mdebug=0
|
||||
);
|
||||
%local oauth_bearer;
|
||||
%if &grant_type=detect %then %do;
|
||||
@@ -12614,7 +12628,7 @@ filename &fname2 clear;
|
||||
%let oauth_bearer=oauth_bearer=sas_services;
|
||||
%let &access_token_var=;
|
||||
%end;
|
||||
%put &sysmacroname: grant_type=&grant_type;
|
||||
|
||||
%mp_abort(iftrue=(&grant_type ne authorization_code and &grant_type ne password
|
||||
and &grant_type ne sas_services
|
||||
)
|
||||
@@ -12660,7 +12674,7 @@ options noquotelenmax;
|
||||
%local fname1;
|
||||
%let fname1=%mf_getuniquefileref();
|
||||
proc http method='GET' out=&fname1 &oauth_bearer
|
||||
url="&base_uri&joburi";
|
||||
url="&base_uri&uri";
|
||||
headers
|
||||
%if &grant_type=authorization_code %then %do;
|
||||
"Authorization"="Bearer &&&access_token_var"
|
||||
@@ -12679,7 +12693,7 @@ run;
|
||||
%let fname3=%mf_getuniquefileref();
|
||||
%let fpath1=%sysfunc(pathname(&fname1));
|
||||
%let fpath2=%sysfunc(pathname(&fname2));
|
||||
%let fpath3=%sysfunc(pathname(&fname2));
|
||||
%let fpath3=%sysfunc(pathname(&fname3));
|
||||
|
||||
/* compile the lua JSON module */
|
||||
%ml_json()
|
||||
@@ -12699,15 +12713,94 @@ data _null_;
|
||||
run;
|
||||
%inc "&fpath3..lua";
|
||||
/* get log path*/
|
||||
%let errflg=1;
|
||||
%let errmsg=No entry in &fname2 fileref;
|
||||
data _null_;
|
||||
infile &fname2;
|
||||
input;
|
||||
call symputx('logloc',_infile_,'l');
|
||||
uri=_infile_;
|
||||
if length(uri)<12 then do;
|
||||
call symputx('errflg',1);
|
||||
call symputx('errmsg',"URI is invalid (too short) - '&uri'",'l');
|
||||
end;
|
||||
if scan(uri,1) ne 'files' or scan(uri,2) ne 'files' then do;
|
||||
call symputx('errflg',1);
|
||||
call symputx('errmsg',
|
||||
"URI should be in format /files/files/$$$$UUID$$$$"
|
||||
!!" but is actually like: &uri",'l');
|
||||
end;
|
||||
call symputx('errflg',0,'l');
|
||||
call symputx('logloc',uri,'l');
|
||||
run;
|
||||
%put &=logloc;
|
||||
filename &fname1 clear;
|
||||
filename &fname2 clear;
|
||||
%mend;/**
|
||||
|
||||
%mp_abort(iftrue=(&errflg=1)
|
||||
,mac=&sysmacroname
|
||||
,msg=%str(&errmsg)
|
||||
)
|
||||
|
||||
/* we have a log uri - now fetch the log */
|
||||
proc http method='GET' out=&fname1 &oauth_bearer
|
||||
url="&base_uri&logloc/content";
|
||||
headers
|
||||
%if &grant_type=authorization_code %then %do;
|
||||
"Authorization"="Bearer &&&access_token_var"
|
||||
%end;
|
||||
;
|
||||
run;
|
||||
%if &SYS_PROCHTTP_STATUS_CODE ne 200 and &SYS_PROCHTTP_STATUS_CODE ne 201 %then
|
||||
%do;
|
||||
data _null_;infile &fname1;input;putlog _infile_;run;
|
||||
%mp_abort(mac=&sysmacroname
|
||||
,msg=%str(logfetch: &SYS_PROCHTTP_STATUS_CODE &SYS_PROCHTTP_STATUS_PHRASE)
|
||||
)
|
||||
%end;
|
||||
|
||||
data _null_;
|
||||
file "&fpath3..lua";
|
||||
put '
|
||||
infile = io.open (sas.symget("fpath1"), "r")
|
||||
outfile = io.open (sas.symget("fpath2"), "w")
|
||||
io.input(infile)
|
||||
local resp=json.decode(io.read())
|
||||
for i, v in pairs(resp["items"]) do
|
||||
outfile:write(v.line,"\n")
|
||||
end
|
||||
io.close(infile)
|
||||
io.close(outfile)
|
||||
';
|
||||
run;
|
||||
%inc "&fpath3..lua";
|
||||
|
||||
/* write log out to the specified fileref */
|
||||
data _null_;
|
||||
infile &fname2 end=last;
|
||||
file &outref mod;
|
||||
if _n_=1 then do;
|
||||
put "/** SASJS Viya Job Log Extract start: &uri **/";
|
||||
end;
|
||||
input;
|
||||
put _infile_;
|
||||
%if &mdebug=1 %then %do;
|
||||
putlog _infile_;
|
||||
%end;
|
||||
if last then do;
|
||||
put "/** SASJS Viya Job Log Extract end: &uri **/";
|
||||
end;
|
||||
run;
|
||||
|
||||
%if &mdebug=0 %then %do;
|
||||
filename &fname1 clear;
|
||||
filename &fname2 clear;
|
||||
filename &fname3 clear;
|
||||
%end;
|
||||
%else %do;
|
||||
%put _local_;
|
||||
%end;
|
||||
%mend;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
@file
|
||||
@brief Extract the status from a running SAS Viya job
|
||||
@details Extracts the status from a running job and appends it to an output
|
||||
@@ -12810,7 +12903,7 @@ filename &fname2 clear;
|
||||
%let oauth_bearer=oauth_bearer=sas_services;
|
||||
%let &access_token_var=;
|
||||
%end;
|
||||
%put &sysmacroname: grant_type=&grant_type;
|
||||
|
||||
%mp_abort(iftrue=(&grant_type ne authorization_code and &grant_type ne password
|
||||
and &grant_type ne sas_services
|
||||
)
|
||||
@@ -13296,18 +13389,19 @@ libname &libref;
|
||||
|
||||
## Input table (minimum variables needed)
|
||||
|
||||
@li FLOW_ID - Numeric value, provides sequential ordering capability
|
||||
@li _CONTEXTNAME - Dictates which context should be used to run the job. If
|
||||
blank, will default to `SAS Job Execution compute context`.
|
||||
@li _PROGRAM - Provides the path to the job itself
|
||||
@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`.
|
||||
|
||||
Any additional variables provided in this table are converted into macro
|
||||
variables and passed into the relevant job.
|
||||
|
||||
| FLOW_ID| _CONTEXTNAME |_PROGRAM|
|
||||
|_PROGRAM| FLOW_ID (optional)| _CONTEXTNAME (optional) |
|
||||
|---|---|---|
|
||||
|0|SAS Job Execution compute context|/Public/jobs/somejob1|
|
||||
|0|SAS Job Execution compute context|/Public/jobs/somejob2|
|
||||
|/Public/jobs/somejob1|0|SAS Job Execution compute context|
|
||||
|/Public/jobs/somejob2|0|SAS Job Execution compute context|
|
||||
|
||||
## Output table (minimum variables produced)
|
||||
|
||||
@@ -13320,6 +13414,9 @@ libname &libref;
|
||||
|
||||

|
||||
|
||||
To avoid hammering the box with many hits in rapid succession, a one
|
||||
second pause is made between every request.
|
||||
|
||||
|
||||
## Example
|
||||
|
||||
@@ -13368,7 +13465,16 @@ libname &libref;
|
||||
|
||||
Trigger the flow
|
||||
|
||||
%mv_jobflow(inds=work.inputjobs,outds=work.results,maxconcurrency=4)
|
||||
%mv_jobflow(inds=work.inputjobs
|
||||
,maxconcurrency=4
|
||||
,outds=work.results
|
||||
,outref=myjoblog
|
||||
)
|
||||
|
||||
data _null_;
|
||||
infile myjoblog;
|
||||
input; put _infile_;
|
||||
run;
|
||||
|
||||
|
||||
@param [in] access_token_var= The global macro variable to contain the access token
|
||||
@@ -13380,7 +13486,9 @@ libname &libref;
|
||||
@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.
|
||||
@param [in] mdebug= set to 1 to enable DEBUG messages
|
||||
@param [out] outds= The output dataset containing the results
|
||||
@param [out] outref= The output fileref to which to append the log file(s).
|
||||
|
||||
@version VIYA V.03.05
|
||||
@author Allan Bowe, source: https://github.com/sasjs/core
|
||||
@@ -13400,6 +13508,8 @@ libname &libref;
|
||||
,maxconcurrency=8
|
||||
,access_token_var=ACCESS_TOKEN
|
||||
,grant_type=sas_services
|
||||
,outref=0
|
||||
,mdebug=0
|
||||
);
|
||||
%local oauth_bearer;
|
||||
%if &grant_type=detect %then %do;
|
||||
@@ -13422,16 +13532,29 @@ libname &libref;
|
||||
,mac=&sysmacroname
|
||||
,msg=%str(Input dataset was not provided)
|
||||
)
|
||||
%mp_abort(iftrue=(%mf_existVarList(&inds,_CONTEXTNAME FLOW_ID _PROGRAM)=0)
|
||||
%mp_abort(iftrue=(%mf_existVarList(&inds,_PROGRAM)=0)
|
||||
,mac=&sysmacroname
|
||||
,msg=%str(The following columns must exist on input dataset &inds:
|
||||
_CONTEXTNAME FLOW_ID _PROGRAM)
|
||||
,msg=%str(The _PROGRAM column must exist on input dataset &inds)
|
||||
)
|
||||
%mp_abort(iftrue=(&maxconcurrency<1)
|
||||
,mac=&sysmacroname
|
||||
,msg=%str(The maxconcurrency variable should be a positive integer)
|
||||
)
|
||||
|
||||
/* set defaults if not provided */
|
||||
%if %mf_existVarList(&inds,_CONTEXTNAME FLOW_ID)=0 %then %do;
|
||||
data &inds;
|
||||
%if %mf_existvarList(&inds,_CONTEXTNAME)=0 %then %do;
|
||||
length _CONTEXTNAME $128;
|
||||
retain _CONTEXTNAME "SAS Job Execution compute context";
|
||||
%end;
|
||||
%if %mf_existvarList(&inds,FLOW_ID)=0 %then %do;
|
||||
retain FLOW_ID 0;
|
||||
%end;
|
||||
set &inds;
|
||||
run;
|
||||
%end;
|
||||
|
||||
%local missings;
|
||||
proc sql noprint;
|
||||
select count(*) into: missings
|
||||
@@ -13509,8 +13632,8 @@ data;run;%let jdswaitfor=&syslast;
|
||||
jparams='jparams'!!left(symget('jid'));
|
||||
call symputx(jparams,substr(_infile_,3,length(_infile_)-4));
|
||||
run;
|
||||
%local joburi&jid;
|
||||
%let joburi&jid=0; /* used in next loop */
|
||||
%local jobuid&jid;
|
||||
%let jobuid&jid=0; /* used in next loop */
|
||||
%end;
|
||||
%local concurrency completed;
|
||||
%let concurrency=0;
|
||||
@@ -13521,8 +13644,21 @@ data;run;%let jdswaitfor=&syslast;
|
||||
* now we can execute the jobs up to the maxconcurrency setting
|
||||
*/
|
||||
%if "&&job&jid" ne "0" %then %do; /* this var is zero if job finished */
|
||||
%if "&&joburi&jid"="0" and &concurrency<&maxconcurrency %then %do;
|
||||
/* job has not been triggered and we have free slots */
|
||||
|
||||
/* check to see if the job finished in the previous round */
|
||||
%if %sysfunc(exist(&outds))=1 %then %do;
|
||||
%local jobcheck; %let jobcheck=0;
|
||||
proc sql noprint;
|
||||
select count(*) into: jobcheck
|
||||
from &outds where uuid="&&jobuid&jid";
|
||||
%if &jobcheck>0 %then %do;
|
||||
%put &&job&jid in flow &fid with uid &&jobuid&jid completed!;
|
||||
%let job&jid=0;
|
||||
%end;
|
||||
%end;
|
||||
|
||||
/* check if job was triggered and if so, if we have enough slots to run */
|
||||
%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);
|
||||
@@ -13536,27 +13672,22 @@ data;run;%let jdswaitfor=&syslast;
|
||||
format jobparams $32767.;
|
||||
set &jdsapp(where=(method='GET' and rel='state'));
|
||||
jobparams=symget("jparams&jid");
|
||||
call symputx("joburi&jid",uri,'l');
|
||||
/* uri here has the /state suffix */
|
||||
uuid=scan(uri,-2,'/');
|
||||
call symputx("jobuid&jid",uuid,'l');
|
||||
run;
|
||||
proc append base=&jdsrunning data=&jdsapp;
|
||||
run;
|
||||
%let concurrency=%eval(&concurrency+1);
|
||||
%end;
|
||||
%else %if %sysfunc(exist(&outds))=1 %then %do;
|
||||
/* check to see if the job has finished as was previously executed */
|
||||
%local jobcheck; %let jobcheck=0;
|
||||
proc sql noprint;
|
||||
select count(*) into: jobcheck
|
||||
from &outds where uri="&&joburi&jid";
|
||||
%if &jobcheck>0 %then %do;
|
||||
%put &&job&jid in flow &fid with uri &&joburi&jid completed!;
|
||||
%let job&jid=0;
|
||||
%end;
|
||||
/* sleep one second after every request to smooth the impact */
|
||||
data _null_;
|
||||
call sleep(1,1);
|
||||
run;
|
||||
%end;
|
||||
%end;
|
||||
%if &jid=&jcnt %then %do;
|
||||
/* we are at the end of the loop - time to see which jobs have finished */
|
||||
%mv_jobwaitfor(ANY,inds=&jdsrunning,outds=&jdswaitfor)
|
||||
%mv_jobwaitfor(ANY,inds=&jdsrunning,outds=&jdswaitfor,outref=&outref)
|
||||
%local done;
|
||||
%let done=%mf_nobs(&jdswaitfor);
|
||||
%if &done>0 %then %do;
|
||||
@@ -13565,13 +13696,14 @@ data;run;%let jdswaitfor=&syslast;
|
||||
data &jdsapp;
|
||||
set &jdswaitfor;
|
||||
flow_id=&&flow&fid;
|
||||
uuid=scan(uri,-1,'/');
|
||||
run;
|
||||
proc append base=&outds data=&jdsapp;
|
||||
run;
|
||||
%end;
|
||||
proc sql;
|
||||
delete from &jdsrunning
|
||||
where uri in (select uri from &outds
|
||||
where uuid in (select uuid from &outds
|
||||
where state in ('canceled','completed','failed')
|
||||
);
|
||||
|
||||
@@ -13585,6 +13717,9 @@ data;run;%let jdswaitfor=&syslast;
|
||||
/* back up and execute the next flow */
|
||||
%end;
|
||||
|
||||
%if &mdebug=1 %then %do;
|
||||
%put _local_;
|
||||
%end;
|
||||
|
||||
%mend;
|
||||
/**
|
||||
@@ -13661,7 +13796,7 @@ data;run;%let jdswaitfor=&syslast;
|
||||
should be in a `_program` variable.
|
||||
@param [out] outds= The output dataset containing the list of states by job
|
||||
(default=work.mv_jobexecute)
|
||||
|
||||
@param [out] outref= A fileref to which the spawned job logs should be appended.
|
||||
|
||||
@version VIYA V.03.04
|
||||
@author Allan Bowe, source: https://github.com/sasjs/core
|
||||
@@ -13672,6 +13807,7 @@ data;run;%let jdswaitfor=&syslast;
|
||||
@li mf_getuniquefileref.sas
|
||||
@li mf_existvar.sas
|
||||
@li mf_nobs.sas
|
||||
@li mv_getjoblog.sas
|
||||
|
||||
**/
|
||||
|
||||
@@ -13680,6 +13816,7 @@ data;run;%let jdswaitfor=&syslast;
|
||||
,grant_type=sas_services
|
||||
,inds=0
|
||||
,outds=work.mv_jobwaitfor
|
||||
,outref=0
|
||||
);
|
||||
%local oauth_bearer;
|
||||
%if &grant_type=detect %then %do;
|
||||
@@ -13723,7 +13860,7 @@ options noquotelenmax;
|
||||
data _null_;
|
||||
length jobparams $32767;
|
||||
set &inds end=last;
|
||||
call symputx(cats('joburi',_n_),uri,'l');
|
||||
call symputx(cats('joburi',_n_),substr(uri,1,55)!!'/state','l');
|
||||
call symputx(cats('jobname',_n_),_program,'l');
|
||||
call symputx(cats('jobparams',_n_),jobparams,'l');
|
||||
if last then call symputx('uricnt',_n_,'l');
|
||||
@@ -13767,14 +13904,20 @@ run;
|
||||
run;
|
||||
|
||||
%if &status=completed or &status=failed or &status=canceled %then %do;
|
||||
%local plainuri;
|
||||
%let plainuri=%substr(&&joburi&i,1,55);
|
||||
proc sql;
|
||||
insert into &outds set
|
||||
_program="&&jobname&i",
|
||||
uri="&&joburi&i",
|
||||
uri="&plainuri",
|
||||
state="&status",
|
||||
timestamp=datetime(),
|
||||
jobparams=symget("jobparams&i");
|
||||
%let joburi&i=0; /* do not re-check */
|
||||
/* fetch log */
|
||||
%if %str(&outref) ne 0 %then %do;
|
||||
%mv_getjoblog(uri=&plainuri,outref=&outref)
|
||||
%end;
|
||||
%end;
|
||||
%else %if &status=idle or &status=pending or &status=running %then %do;
|
||||
data _null_;
|
||||
|
||||
@@ -32,9 +32,11 @@
|
||||
%put Supported features: PROCLUA;
|
||||
%end;
|
||||
%else %if &feature=PROCLUA %then %do;
|
||||
/* https://blogs.sas.com/content/sasdummy/2015/08/03/using-lua-within-your-sas-programs */
|
||||
%if &platform=SASVIYA %then 1;
|
||||
%else %if "&sysver"="9.3" or "&sysver"="9.4" %then 1;
|
||||
%else 0;
|
||||
%else %if "&sysver"="9.2" or "&sysver"="9.3" %then 0;
|
||||
%else %if "&SYSVLONG" < "9.04.01M3" %then 0;
|
||||
%else 1;
|
||||
%end;
|
||||
%else %do;
|
||||
-1
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
%local dsid rc;
|
||||
%let dsid=%sysfunc(open(&libds,is));
|
||||
%if &dsid = 0 %then %do;
|
||||
%put WARNING: Cannot open %trim(&libds), system message below;
|
||||
%put %str(WARN)ING: Cannot open %trim(&libds), system message below;
|
||||
%put %sysfunc(sysmsg());
|
||||
-1
|
||||
%end;
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
@brief retrieves a key value pair from a control dataset
|
||||
@details By default, control dataset is work.mp_setkeyvalue. Usage:
|
||||
|
||||
%mp_setkeyvalue(someindex,22,type=N)
|
||||
%put %mf_getkeyvalue(someindex)
|
||||
%mp_setkeyvalue(someindex,22,type=N)
|
||||
%put %mf_getkeyvalue(someindex)
|
||||
|
||||
|
||||
@param key Provide a key on which to perform the lookup
|
||||
|
||||
@@ -3,25 +3,27 @@
|
||||
@brief Create a CARDS file from a SAS dataset.
|
||||
@details Uses dataset attributes to convert all data into datalines.
|
||||
Running the generated file will rebuild the original dataset.
|
||||
usage:
|
||||
Usage:
|
||||
|
||||
%mp_ds2cards(base_ds=sashelp.class
|
||||
, cards_file= "C:\temp\class.sas"
|
||||
, maxobs=5)
|
||||
|
||||
stuff to add
|
||||
TODO:
|
||||
- labelling the dataset
|
||||
- explicity setting a unix LF
|
||||
- constraints / indexes etc
|
||||
|
||||
@param 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 that
|
||||
is converted to a cards file.
|
||||
@param tgt_ds= Table that the generated cards file would create. Optional -
|
||||
@param [in] tgt_ds= Table that the generated cards file would create. Optional -
|
||||
if omitted, will be same as BASE_DS.
|
||||
@param cards_file= Location in which to write the (.sas) cards file
|
||||
@param maxobs= to limit output to the first <code>maxobs</code> observations
|
||||
@param showlog= whether to show generated cards file in the SAS log (YES/NO)
|
||||
@param outencoding= provide encoding value for file statement (eg utf-8)
|
||||
@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] 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.
|
||||
|
||||
|
||||
@version 9.2
|
||||
@@ -34,6 +36,7 @@
|
||||
,random_sample=NO
|
||||
,showlog=YES
|
||||
,outencoding=
|
||||
,append=NO
|
||||
)/*/STORE SOURCE*/;
|
||||
%local i setds nvars;
|
||||
|
||||
@@ -46,6 +49,8 @@
|
||||
%if (&tgt_ds = ) %then %let tgt_ds=&base_ds;
|
||||
%if %index(&tgt_ds,.)=0 %then %let tgt_ds=WORK.%scan(&base_ds,2,.);
|
||||
%if ("&outencoding" ne "") %then %let outencoding=encoding="&outencoding";
|
||||
%if ("&append" = "") %then %let append=;
|
||||
%else %let append=mod;
|
||||
|
||||
/* get varcount */
|
||||
%let nvars=0;
|
||||
@@ -172,7 +177,7 @@ data _null_;
|
||||
run;
|
||||
|
||||
data _null_;
|
||||
file &cards_file. &outencoding lrecl=32767 termstr=nl;
|
||||
file &cards_file. &outencoding lrecl=32767 termstr=nl &append;
|
||||
length __attrib $32767;
|
||||
if _n_=1 then do;
|
||||
put '/*******************************************************************';
|
||||
|
||||
@@ -1,21 +1,26 @@
|
||||
/**
|
||||
@file
|
||||
@brief Convert all library members to CARDS files
|
||||
@details Gets list of members then calls the <code>%mp_ds2cards()</code>
|
||||
macro
|
||||
usage:
|
||||
@details Gets list of members then calls the <code>%mp_ds2cards()</code> macro.
|
||||
Usage:
|
||||
|
||||
%mp_lib2cards(lib=sashelp
|
||||
, outloc= C:\temp )
|
||||
%mp_lib2cards(lib=sashelp
|
||||
, outloc= C:\temp )
|
||||
|
||||
The output will be one cards file in the `outloc` directory per dataset in the
|
||||
input `lib` library. If the `outloc` directory does not exist, it is created.
|
||||
|
||||
<h4> SAS Macros </h4>
|
||||
@li mf_mkdir.sas
|
||||
@li mf_trimstr.sas
|
||||
@li mp_ds2cards.sas
|
||||
|
||||
@param lib= Library in which to convert all datasets
|
||||
@param outloc= Location in which to store output. Defaults to WORK library.
|
||||
Do not use a trailing slash (my/path not my/path/). No quotes.
|
||||
@param maxobs= limit output to the first <code>maxobs</code> observations
|
||||
@param [in] lib= Library in which to convert all datasets
|
||||
@param [out] outloc= Location in which to store output. Defaults to WORK
|
||||
library. No quotes.
|
||||
@param [out] outfile= Optional output file NAME - if provided, then will create
|
||||
a single output file instead of one file per input table.
|
||||
@param [in] maxobs= limit output to the first <code>maxobs</code> observations
|
||||
|
||||
@version 9.2
|
||||
@author Allan Bowe
|
||||
@@ -25,6 +30,7 @@
|
||||
,outloc=%sysfunc(pathname(work)) /* without trailing slash */
|
||||
,maxobs=max
|
||||
,random_sample=NO
|
||||
,outfile=0
|
||||
)/*/STORE SOURCE*/;
|
||||
|
||||
/* Find the tables */
|
||||
@@ -36,16 +42,28 @@ select distinct lowcase(memname)
|
||||
from dictionary.tables
|
||||
where upcase(libname)="%upcase(&lib)";
|
||||
|
||||
/* trim trailing slash, if provided */
|
||||
%let outloc=%mf_trimstr(&outloc,/);
|
||||
%let outloc=%mf_trimstr(&outloc,\);
|
||||
|
||||
/* create the output directory */
|
||||
%mf_mkdir(&outloc)
|
||||
|
||||
/* create the cards files */
|
||||
%do x=1 %to %sysfunc(countw(&memlist));
|
||||
%let ds=%scan(&memlist,&x);
|
||||
%mp_ds2cards(base_ds=&lib..&ds
|
||||
,cards_file="&outloc/&ds..sas"
|
||||
,maxobs=&maxobs
|
||||
,random_sample=&random_sample)
|
||||
%let ds=%scan(&memlist,&x);
|
||||
%mp_ds2cards(base_ds=&lib..&ds
|
||||
,maxobs=&maxobs
|
||||
,random_sample=&random_sample
|
||||
%if "&outfile" ne "0" %then %do;
|
||||
,append=YES
|
||||
,cards_file="&outloc/&outfile"
|
||||
%end;
|
||||
%else %do;
|
||||
,append=NO
|
||||
,cards_file="&outloc/&ds..sas"
|
||||
%end;
|
||||
)
|
||||
%end;
|
||||
|
||||
%mend;
|
||||
@@ -3,8 +3,8 @@
|
||||
@brief Logs a key value pair a control dataset
|
||||
@details If the dataset does not exist, it is created. Usage:
|
||||
|
||||
%mp_setkeyvalue(someindex,22,type=N)
|
||||
%mp_setkeyvalue(somenewindex,somevalue)
|
||||
%mp_setkeyvalue(someindex,22,type=N)
|
||||
%mp_setkeyvalue(somenewindex,somevalue)
|
||||
|
||||
<h4> SAS Macros </h4>
|
||||
@li mf_existds.sas
|
||||
@@ -26,7 +26,7 @@
|
||||
|
||||
%if not (%mf_existds(&libds)) %then %do;
|
||||
data &libds (index=(key/unique));
|
||||
length key $32 valc $256 valn 8 type $1;
|
||||
length key $64 valc $2048 valn 8 type $1;
|
||||
call missing(of _all_);
|
||||
stop;
|
||||
run;
|
||||
|
||||
@@ -1,45 +0,0 @@
|
||||
#!/bin/bash
|
||||
####################################################################
|
||||
# PROJECT: Macro Core Docs Build #
|
||||
####################################################################
|
||||
|
||||
BUILD_FOLDER="/tmp/macrocore_docs"
|
||||
|
||||
# move to project root
|
||||
cd ..
|
||||
|
||||
# create build directory
|
||||
rm -rf $BUILD_FOLDER
|
||||
mkdir $BUILD_FOLDER
|
||||
|
||||
# copy relevant files
|
||||
cp -r base $BUILD_FOLDER
|
||||
cp -r meta $BUILD_FOLDER
|
||||
cp -r metax $BUILD_FOLDER
|
||||
cp -r lua $BUILD_FOLDER
|
||||
cp -r viya $BUILD_FOLDER
|
||||
cp -r doxy $BUILD_FOLDER
|
||||
cp main.dox $BUILD_FOLDER
|
||||
cp doxy/Doxyfile $BUILD_FOLDER
|
||||
|
||||
# update Doxyfile and generate
|
||||
cd $BUILD_FOLDER
|
||||
echo "OUTPUT_DIRECTORY=$BUILD_FOLDER/out" >> $BUILD_FOLDER/Doxyfile
|
||||
echo "INPUT+=main.dox" >> $BUILD_FOLDER/Doxyfile
|
||||
doxygen Doxyfile
|
||||
|
||||
# refresh github pages site
|
||||
git clone git@github.com:sasjs/core.github.io.git
|
||||
cd core.github.io
|
||||
rm -r *
|
||||
mv $BUILD_FOLDER/out/doxy/* .
|
||||
echo 'core.sasjs.io' > CNAME
|
||||
git add .
|
||||
git commit -m "build.sh build on $(date +%F:%H:%M:%S)"
|
||||
git push
|
||||
npx sitemap-generator-cli https://core.sasjs.io
|
||||
git add .
|
||||
git commit -m "adding sitemap"
|
||||
git push
|
||||
|
||||
echo "check it out: https://sasjs.github.io/core.github.io/files.html"
|
||||
@@ -1,23 +0,0 @@
|
||||
<!-- HTML footer for doxygen 1.8.17-->
|
||||
<!-- start footer part -->
|
||||
<!--BEGIN GENERATE_TREEVIEW-->
|
||||
<div id="nav-path" class="navpath"><!-- id is needed for treeview function! -->
|
||||
<ul>
|
||||
$navpath
|
||||
<li class="footer">$generatedby
|
||||
<a href="https://www.doxygen.org/index.html">
|
||||
<img class="footer" src="$relpath^doxygen.png" alt="doxygen"/></a> $doxygenversion </li>
|
||||
<i> For more information visit the </i> <a href="https://github.com/sasjs/core">Macro Core library</a>.</li>
|
||||
|
||||
</ul>
|
||||
</div>
|
||||
<!--END GENERATE_TREEVIEW-->
|
||||
<!--BEGIN !GENERATE_TREEVIEW-->
|
||||
<hr class="footer"/><address class="footer"><small>
|
||||
$generatedby  <a href="http://www.doxygen.org/index.html">
|
||||
<img class="footer" src="$relpath^doxygen.png" alt="doxygen"/>
|
||||
</a> $doxygenversion
|
||||
</small></address>
|
||||
<!--END !GENERATE_TREEVIEW-->
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,72 +0,0 @@
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
<!-- HTML header for doxygen 1.8.17-->
|
||||
<html xmlns="https://www.w3.org/1999/xhtml" lang="en">
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8"/>
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=9"/>
|
||||
<meta name="generator" content="Doxygen $doxygenversion"/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1"/>
|
||||
<!--BEGIN PROJECT_NAME--><title>$projectname: $title</title><!--END PROJECT_NAME-->
|
||||
<!--BEGIN !PROJECT_NAME--><title>$title</title><!--END !PROJECT_NAME-->
|
||||
<link href="$relpath^tabs.css" rel="stylesheet" type="text/css"/>
|
||||
<script type="text/javascript" src="$relpath^jquery.js"></script>
|
||||
<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">
|
||||
$extrastylesheet
|
||||
</head>
|
||||
<body>
|
||||
<div id="top"><!-- do not remove this div, it is closed by doxygen! -->
|
||||
|
||||
<!--BEGIN TITLEAREA-->
|
||||
<div id="titlearea">
|
||||
<table cellspacing="0" cellpadding="0">
|
||||
<tbody>
|
||||
<tr style="height: 56px;">
|
||||
<!--BEGIN PROJECT_LOGO-->
|
||||
<td id="projectlogo">
|
||||
<img alt="Logo" src="$relpath^$projectlogo"/></td>
|
||||
<!--END PROJECT_LOGO-->
|
||||
<!--BEGIN PROJECT_NAME-->
|
||||
<td id="projectalign" style="padding-left: 0.5em;">
|
||||
<div id="projectname">
|
||||
<!--BEGIN PROJECT_NUMBER--> <span id="projectnumber">$projectnumber</span><!--END PROJECT_NUMBER-->
|
||||
</div>
|
||||
<!--BEGIN PROJECT_BRIEF--><div id="projectbrief">
|
||||
Production Ready Macros for SAS Application Developers</br>
|
||||
<a href="https://github.com/sasjs/core">
|
||||
https://github.com/sasjs/core
|
||||
</a>
|
||||
|
||||
</div>
|
||||
<meta name="Description" content="$projectbrief">
|
||||
<!--END PROJECT_BRIEF-->
|
||||
</td>
|
||||
<!--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>
|
||||
<!--END SEARCHENGINE-->
|
||||
<!--END DISABLE_INDEX-->
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<!--END TITLEAREA-->
|
||||
<!-- end header part -->
|
||||
3419
package-lock.json
generated
3419
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
20
package.json
20
package.json
@@ -10,13 +10,27 @@
|
||||
"author": "Allan Bowe <support@macropeople.com>",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/sasjs/core"
|
||||
"url": "git+https://github.com/sasjs/core.git"
|
||||
},
|
||||
"release": {
|
||||
"branches": ["main"]
|
||||
"branches": [
|
||||
"main"
|
||||
]
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"devDependencies": {}
|
||||
"bugs": {
|
||||
"url": "https://github.com/sasjs/core/issues"
|
||||
},
|
||||
"homepage": "https://github.com/sasjs/core#readme",
|
||||
"version": "1.0.0",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1",
|
||||
"docs": "./sasjs/utils/build.sh"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@sasjs/cli": "^2.4.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
ALPHABETICAL_INDEX = NO
|
||||
DISABLE_INDEX = YES
|
||||
DISABLE_INDEX = NO
|
||||
ENABLE_PREPROCESSING = NO
|
||||
EXTENSION_MAPPING = sas=Java ddl=Java
|
||||
EXTRACT_LOCAL_CLASSES = NO
|
||||
@@ -13,17 +13,20 @@ HIDE_IN_BODY_DOCS = YES
|
||||
HIDE_SCOPE_NAMES = YES
|
||||
HIDE_UNDOC_CLASSES = YES
|
||||
HIDE_UNDOC_MEMBERS = YES
|
||||
HTML_OUTPUT = doxy
|
||||
HTML_HEADER = ./doxy/new_header.html
|
||||
HTML_FOOTER = ./doxy/new_footer.html
|
||||
HTML_EXTRA_STYLESHEET = ./doxy/new_stylesheet.css
|
||||
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
|
||||
INHERIT_DOCS = NO
|
||||
INLINE_INFO = NO
|
||||
INPUT = base meta metax viya lua
|
||||
LAYOUT_FILE = ./doxy/DoxygenLayout.xml
|
||||
INPUT = $(DOXY_CONTENT)../../README.md \
|
||||
= $(DOXY_CONTENT)../../main.dox \
|
||||
$(DOXY_INPUT)
|
||||
USE_MDFILE_AS_MAINPAGE = README.md
|
||||
LAYOUT_FILE = $(DOXY_CONTENT)DoxygenLayout.xml
|
||||
MAX_INITIALIZER_LINES = 0
|
||||
PROJECT_NAME = Macro Core
|
||||
PROJECT_LOGO = doxy/Macro_core_website_1.png
|
||||
PROJECT_LOGO = $(DOXY_CONTENT)Macro_core_website_1.png
|
||||
PROJECT_BRIEF = "Production Ready Macros for SAS Application Developers"
|
||||
RECURSIVE = YES
|
||||
REPEAT_BRIEF = NO
|
||||
@@ -2,7 +2,7 @@
|
||||
<!-- Generated by doxygen 1.8.14 -->
|
||||
<!-- Navigation index tabs for HTML output -->
|
||||
<navindex>
|
||||
<tab type="mainpage" visible="no" title=""/>
|
||||
<tab type="mainpage" visible="yes" title="Home"/>
|
||||
<tab type="pages" visible="no" title="" intro=""/>
|
||||
<tab type="modules" visible="no" title="" intro=""/>
|
||||
<tab type="namespaces" visible="no" title="">
|
||||
@@ -108,4 +108,4 @@
|
||||
<files visible="yes"/>
|
||||
</memberdecl>
|
||||
</directory>
|
||||
</doxygenlayout>
|
||||
</doxygenlayout>
|
||||
|
Before Width: | Height: | Size: 57 KiB After Width: | Height: | Size: 57 KiB |
BIN
sasjs/doxy/favicon.ico
Normal file
BIN
sasjs/doxy/favicon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.1 KiB |
32
sasjs/doxy/new_footer.html
Normal file
32
sasjs/doxy/new_footer.html
Normal file
@@ -0,0 +1,32 @@
|
||||
<!-- HTML footer for doxygen 1.8.17-->
|
||||
<!-- start footer part -->
|
||||
<!--BEGIN GENERATE_TREEVIEW-->
|
||||
<div id="nav-path" class="navpath">
|
||||
<!-- id is needed for treeview function! -->
|
||||
<ul>
|
||||
$navpath
|
||||
<li class="footer">
|
||||
$generatedby
|
||||
<a href="https://www.doxygen.org/index.html">
|
||||
<img class="footer" src="$relpath^doxygen.png" alt="doxygen"
|
||||
/></a>
|
||||
$doxygenversion
|
||||
</li>
|
||||
<li>
|
||||
<i> For more information visit the </i>
|
||||
<a href="https://github.com/sasjs/core">Macro Core library</a>.
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<!--END GENERATE_TREEVIEW-->
|
||||
<!--BEGIN !GENERATE_TREEVIEW-->
|
||||
<hr class="footer" />
|
||||
<address class="footer">
|
||||
<small>
|
||||
$generatedby  <a href="http://www.doxygen.org/index.html">
|
||||
<img class="footer" src="$relpath^doxygen.png" alt="doxygen" />
|
||||
</a>
|
||||
$doxygenversion
|
||||
</small>
|
||||
</address>
|
||||
<!--END !GENERATE_TREEVIEW-->
|
||||
93
sasjs/doxy/new_header.html
Normal file
93
sasjs/doxy/new_header.html
Normal file
@@ -0,0 +1,93 @@
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
<!-- HTML header for doxygen 1.8.17-->
|
||||
<html xmlns="https://www.w3.org/1999/xhtml" lang="en">
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8" />
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=9" />
|
||||
<meta name="generator" content="Doxygen $doxygenversion" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<!--BEGIN PROJECT_NAME-->
|
||||
<title>$projectname: $title</title>
|
||||
<!--END PROJECT_NAME-->
|
||||
<!--BEGIN !PROJECT_NAME-->
|
||||
<title>$title</title>
|
||||
<!--END !PROJECT_NAME-->
|
||||
<link href="$relpath^tabs.css" rel="stylesheet" type="text/css" />
|
||||
<script type="text/javascript" src="$relpath^jquery.js"></script>
|
||||
<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" />
|
||||
$extrastylesheet
|
||||
</head>
|
||||
<body>
|
||||
<div id="top">
|
||||
<!-- do not remove this div, it is closed by doxygen! -->
|
||||
|
||||
<!--BEGIN TITLEAREA-->
|
||||
<div id="titlearea">
|
||||
<table cellspacing="0" cellpadding="0">
|
||||
<tbody>
|
||||
<tr style="height: 56px">
|
||||
<!--BEGIN PROJECT_LOGO-->
|
||||
<td id="projectlogo">
|
||||
<img alt="Logo" src="$relpath^$projectlogo" />
|
||||
</td>
|
||||
<!--END PROJECT_LOGO-->
|
||||
<!--BEGIN PROJECT_NAME-->
|
||||
<td id="projectalign" style="padding-left: 0.5em">
|
||||
<div id="projectname">
|
||||
<!--BEGIN PROJECT_NUMBER--> <span id="projectnumber"
|
||||
>$projectnumber</span
|
||||
><!--END PROJECT_NUMBER-->
|
||||
</div>
|
||||
<!--BEGIN PROJECT_BRIEF-->
|
||||
<div id="projectbrief">
|
||||
Production Ready Macros for SAS Application Developers<br />
|
||||
<a href="https://github.com/sasjs/core">
|
||||
https://github.com/sasjs/core
|
||||
</a>
|
||||
</div>
|
||||
<meta name="Description" content="$projectbrief" />
|
||||
<!--END PROJECT_BRIEF-->
|
||||
</td>
|
||||
<!--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>
|
||||
<!--END SEARCHENGINE-->
|
||||
<!--END DISABLE_INDEX-->
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<!--END TITLEAREA-->
|
||||
<!-- end header part -->
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,5 +1,5 @@
|
||||
#projectlogo img
|
||||
{
|
||||
border: 0px none;
|
||||
max-height:70px
|
||||
#projectlogo img
|
||||
{
|
||||
border: 0px none;
|
||||
max-height:70px
|
||||
}
|
||||
|
Before Width: | Height: | Size: 3.4 KiB After Width: | Height: | Size: 3.4 KiB |
7
sasjs/sasjsconfig.json
Normal file
7
sasjs/sasjsconfig.json
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"$schema": "https://cli.sasjs.io/sasjsconfig-schema.json",
|
||||
"macroFolders": ["base", "meta", "metax", "viya", "lua"],
|
||||
"docConfig":{
|
||||
"displayMacroCore": false
|
||||
}
|
||||
}
|
||||
25
sasjs/utils/build.sh
Executable file
25
sasjs/utils/build.sh
Executable file
@@ -0,0 +1,25 @@
|
||||
#!/bin/bash
|
||||
####################################################################
|
||||
# PROJECT: Macro Core Docs Build #
|
||||
####################################################################
|
||||
|
||||
cd ../..
|
||||
|
||||
sasjs doc
|
||||
|
||||
# refresh github pages site
|
||||
rm -rf sasjsbuild/docsite
|
||||
git clone git@github.com:sasjs/core.github.io.git sasjsbuild/docsite
|
||||
rm -rf sasjsbuild/docsite/*
|
||||
mv sasjsbuild/docs/* sasjsbuild/docsite/
|
||||
cd sasjsbuild/docsite/
|
||||
echo 'core.sasjs.io' > CNAME
|
||||
git add .
|
||||
git commit -m "build.sh build on $(date +%F:%H:%M:%S)"
|
||||
git push
|
||||
npx sitemap-generator-cli https://core.sasjs.io
|
||||
git add .
|
||||
git commit -m "adding sitemap"
|
||||
git push
|
||||
|
||||
echo "check it out: https://sasjs.github.io/core.github.io/files.html"
|
||||
@@ -4,7 +4,6 @@
|
||||
@details Expects oauth token in a global macro variable (default
|
||||
ACCESS_TOKEN).
|
||||
|
||||
options mprint;
|
||||
%mv_createfolder(path=/Public)
|
||||
|
||||
|
||||
|
||||
@@ -46,7 +46,7 @@
|
||||
%let oauth_bearer=oauth_bearer=sas_services;
|
||||
%let &access_token_var=;
|
||||
%end;
|
||||
%put &sysmacroname: grant_type=&grant_type;
|
||||
|
||||
%mp_abort(iftrue=(&grant_type ne authorization_code and &grant_type ne password
|
||||
and &grant_type ne sas_services
|
||||
)
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
@details If not running in Studo 5 +, will expect an oauth token in a global
|
||||
macro variable (default ACCESS_TOKEN).
|
||||
|
||||
options mprint;
|
||||
%mv_createfolder(path=/Public/test/blah)
|
||||
%mv_deleteviyafolder(path=/Public/test)
|
||||
|
||||
|
||||
@@ -40,7 +40,7 @@
|
||||
%let oauth_bearer=oauth_bearer=sas_services;
|
||||
%let &access_token_var=;
|
||||
%end;
|
||||
%put &sysmacroname: grant_type=&grant_type;
|
||||
|
||||
%mp_abort(iftrue=(&grant_type ne authorization_code and &grant_type ne password
|
||||
and &grant_type ne sas_services
|
||||
)
|
||||
|
||||
@@ -1,31 +1,20 @@
|
||||
/**
|
||||
@file mv_getgroups.sas
|
||||
@brief Creates a dataset with a list of viya groups
|
||||
@details First, be sure you have an access token (which requires an app token).
|
||||
|
||||
Using the macros here:
|
||||
@details First, load the macros:
|
||||
|
||||
filename mc url
|
||||
"https://raw.githubusercontent.com/sasjs/core/main/all.sas";
|
||||
%inc mc;
|
||||
|
||||
An administrator needs to set you up with an access code:
|
||||
Next, execute:
|
||||
|
||||
%mv_registerclient(outds=client)
|
||||
%mv_getgroups(outds=work.groups)
|
||||
|
||||
Navigate to the url from the log (opting in to the groups) and paste the
|
||||
access code below:
|
||||
|
||||
%mv_tokenauth(inds=client,code=wKDZYTEPK6)
|
||||
|
||||
Now we can run the macro!
|
||||
|
||||
%mv_getgroups()
|
||||
|
||||
@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 [in] access_token_var= The global macro variable to contain the access token
|
||||
@param [in] grant_type= valid values are "password" or "authorization_code" (unquoted).
|
||||
The default is authorization_code.
|
||||
@param outds= The library.dataset to be created that contains the list of groups
|
||||
@param [out] outds= The library.dataset to be created that contains the list of groups
|
||||
|
||||
|
||||
@version VIYA V.03.04
|
||||
@@ -52,7 +41,7 @@
|
||||
%let oauth_bearer=oauth_bearer=sas_services;
|
||||
%let &access_token_var=;
|
||||
%end;
|
||||
%put &sysmacroname: grant_type=&grant_type;
|
||||
|
||||
%mp_abort(iftrue=(&grant_type ne authorization_code and &grant_type ne password
|
||||
and &grant_type ne sas_services
|
||||
)
|
||||
|
||||
@@ -49,7 +49,6 @@
|
||||
%let oauth_bearer=oauth_bearer=sas_services;
|
||||
%let &access_token_var=;
|
||||
%end;
|
||||
%put &sysmacroname: grant_type=&grant_type;
|
||||
%mp_abort(iftrue=(&grant_type ne authorization_code and &grant_type ne password
|
||||
and &grant_type ne sas_services
|
||||
)
|
||||
@@ -114,10 +113,10 @@ run;
|
||||
%let fname3=%mf_getuniquefileref();
|
||||
%let fpath1=%sysfunc(pathname(&fname1));
|
||||
%let fpath2=%sysfunc(pathname(&fname2));
|
||||
%let fpath3=%sysfunc(pathname(&fname2));
|
||||
%let fpath3=%sysfunc(pathname(&fname3));
|
||||
|
||||
/* compile the lua JSON module */
|
||||
%ml_json()
|
||||
%ml_json()
|
||||
/* read using LUA - this allows the code to be of any length */
|
||||
data _null_;
|
||||
file "&fpath3..lua";
|
||||
@@ -147,4 +146,5 @@ data _null_;
|
||||
run;
|
||||
filename &fname1 clear;
|
||||
filename &fname2 clear;
|
||||
filename &fname3 clear;
|
||||
%mend;
|
||||
|
||||
267
viya/mv_getjoblog.sas
Normal file
267
viya/mv_getjoblog.sas
Normal file
@@ -0,0 +1,267 @@
|
||||
/**
|
||||
@file
|
||||
@brief Extract the log from a completed SAS Viya Job
|
||||
@details Extracts log from a Viya job and writes it out to a fileref
|
||||
|
||||
To query the job, you need the URI. Sample code for achieving this
|
||||
is provided below.
|
||||
|
||||
## Example
|
||||
|
||||
First, compile the macros:
|
||||
|
||||
filename mc url "https://raw.githubusercontent.com/sasjs/core/main/all.sas";
|
||||
%inc mc;
|
||||
|
||||
Next, create a job (in this case, a web service):
|
||||
|
||||
filename ft15f001 temp;
|
||||
parmcards4;
|
||||
data ;
|
||||
rand=ranuni(0)*1000;
|
||||
do x=1 to rand;
|
||||
y=rand*4;
|
||||
output;
|
||||
end;
|
||||
run;
|
||||
proc sort data=&syslast
|
||||
by descending y;
|
||||
run;
|
||||
;;;;
|
||||
%mv_createwebservice(path=/Public/temp,name=demo)
|
||||
|
||||
Execute it:
|
||||
|
||||
%mv_jobexecute(path=/Public/temp
|
||||
,name=demo
|
||||
,outds=work.info
|
||||
)
|
||||
|
||||
Wait for it to finish, and grab the uri:
|
||||
|
||||
data _null_;
|
||||
set work.info;
|
||||
if method='GET' and rel='self';
|
||||
call symputx('uri',uri);
|
||||
run;
|
||||
|
||||
Finally, fetch the log:
|
||||
|
||||
%mv_getjoblog(uri=&uri,outref=mylog)
|
||||
|
||||
This macro is used by the mv_jobwaitfor.sas macro, which is generally a more
|
||||
convenient way to wait for the job to finish before fetching the log.
|
||||
|
||||
|
||||
@param [in] access_token_var= The global macro variable to contain the access token
|
||||
@param [in] mdebug= set to 1 to enable DEBUG messages
|
||||
@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 sas_services - will use oauth_bearer=sas_services.
|
||||
@param [in] uri= The uri of the running job for which to fetch the status,
|
||||
in the format `/jobExecution/jobs/$UUID/state` (unquoted).
|
||||
@param [out] outref= The output fileref to which to APPEND the log (is always
|
||||
appended).
|
||||
|
||||
|
||||
@version VIYA V.03.04
|
||||
@author Allan Bowe, source: https://github.com/sasjs/core
|
||||
|
||||
<h4> SAS Macros </h4>
|
||||
@li mp_abort.sas
|
||||
@li mf_getplatform.sas
|
||||
@li mf_existfileref.sas
|
||||
@li ml_json.sas
|
||||
|
||||
**/
|
||||
|
||||
%macro mv_getjoblog(uri=0,outref=0
|
||||
,contextName=SAS Job Execution compute context
|
||||
,access_token_var=ACCESS_TOKEN
|
||||
,grant_type=sas_services
|
||||
,mdebug=0
|
||||
);
|
||||
%local oauth_bearer;
|
||||
%if &grant_type=detect %then %do;
|
||||
%if %symexist(&access_token_var) %then %let grant_type=authorization_code;
|
||||
%else %let grant_type=sas_services;
|
||||
%end;
|
||||
%if &grant_type=sas_services %then %do;
|
||||
%let oauth_bearer=oauth_bearer=sas_services;
|
||||
%let &access_token_var=;
|
||||
%end;
|
||||
|
||||
%mp_abort(iftrue=(&grant_type ne authorization_code and &grant_type ne password
|
||||
and &grant_type ne sas_services
|
||||
)
|
||||
,mac=&sysmacroname
|
||||
,msg=%str(Invalid value for grant_type: &grant_type)
|
||||
)
|
||||
|
||||
/* validation in datastep for better character safety */
|
||||
%local errmsg errflg;
|
||||
data _null_;
|
||||
uri=symget('uri');
|
||||
if length(uri)<12 then do;
|
||||
call symputx('errflg',1);
|
||||
call symputx('errmsg',"URI is invalid (too short) - '&uri'",'l');
|
||||
end;
|
||||
if scan(uri,-1)='state' or scan(uri,1) ne 'jobExecution' then do;
|
||||
call symputx('errflg',1);
|
||||
call symputx('errmsg',
|
||||
"URI should be in format /jobExecution/jobs/$$$$UUID$$$$"
|
||||
!!" but is actually like: &uri",'l');
|
||||
end;
|
||||
run;
|
||||
|
||||
%mp_abort(iftrue=(&errflg=1)
|
||||
,mac=&sysmacroname
|
||||
,msg=%str(&errmsg)
|
||||
)
|
||||
|
||||
%mp_abort(iftrue=(&outref=0)
|
||||
,mac=&sysmacroname
|
||||
,msg=%str(Output fileref should be provided)
|
||||
)
|
||||
|
||||
%if %mf_existfileref(&outref) ne 1 %then %do;
|
||||
filename &outref temp;
|
||||
%end;
|
||||
|
||||
options noquotelenmax;
|
||||
%local base_uri; /* location of rest apis */
|
||||
%let base_uri=%mf_getplatform(VIYARESTAPI);
|
||||
|
||||
/* prepare request*/
|
||||
%local fname1;
|
||||
%let fname1=%mf_getuniquefileref();
|
||||
proc http method='GET' out=&fname1 &oauth_bearer
|
||||
url="&base_uri&uri";
|
||||
headers
|
||||
%if &grant_type=authorization_code %then %do;
|
||||
"Authorization"="Bearer &&&access_token_var"
|
||||
%end;
|
||||
;
|
||||
run;
|
||||
%if &SYS_PROCHTTP_STATUS_CODE ne 200 and &SYS_PROCHTTP_STATUS_CODE ne 201 %then
|
||||
%do;
|
||||
data _null_;infile &fname1;input;putlog _infile_;run;
|
||||
%mp_abort(mac=&sysmacroname
|
||||
,msg=%str(&SYS_PROCHTTP_STATUS_CODE &SYS_PROCHTTP_STATUS_PHRASE)
|
||||
)
|
||||
%end;
|
||||
%local fname2 fname3 fpath1 fpath2 fpath3;
|
||||
%let fname2=%mf_getuniquefileref();
|
||||
%let fname3=%mf_getuniquefileref();
|
||||
%let fpath1=%sysfunc(pathname(&fname1));
|
||||
%let fpath2=%sysfunc(pathname(&fname2));
|
||||
%let fpath3=%sysfunc(pathname(&fname3));
|
||||
|
||||
/* compile the lua JSON module */
|
||||
%ml_json()
|
||||
/* read using LUA - this allows the code to be of any length */
|
||||
data _null_;
|
||||
file "&fpath3..lua";
|
||||
put '
|
||||
infile = io.open (sas.symget("fpath1"), "r")
|
||||
outfile = io.open (sas.symget("fpath2"), "w")
|
||||
io.input(infile)
|
||||
local resp=json.decode(io.read())
|
||||
local logloc=resp["logLocation"]
|
||||
outfile:write(logloc)
|
||||
io.close(infile)
|
||||
io.close(outfile)
|
||||
';
|
||||
run;
|
||||
%inc "&fpath3..lua";
|
||||
/* get log path*/
|
||||
%let errflg=1;
|
||||
%let errmsg=No entry in &fname2 fileref;
|
||||
data _null_;
|
||||
infile &fname2;
|
||||
input;
|
||||
uri=_infile_;
|
||||
if length(uri)<12 then do;
|
||||
call symputx('errflg',1);
|
||||
call symputx('errmsg',"URI is invalid (too short) - '&uri'",'l');
|
||||
end;
|
||||
if scan(uri,1) ne 'files' or scan(uri,2) ne 'files' then do;
|
||||
call symputx('errflg',1);
|
||||
call symputx('errmsg',
|
||||
"URI should be in format /files/files/$$$$UUID$$$$"
|
||||
!!" but is actually like: &uri",'l');
|
||||
end;
|
||||
call symputx('errflg',0,'l');
|
||||
call symputx('logloc',uri,'l');
|
||||
run;
|
||||
|
||||
%mp_abort(iftrue=(&errflg=1)
|
||||
,mac=&sysmacroname
|
||||
,msg=%str(&errmsg)
|
||||
)
|
||||
|
||||
/* we have a log uri - now fetch the log */
|
||||
proc http method='GET' out=&fname1 &oauth_bearer
|
||||
url="&base_uri&logloc/content";
|
||||
headers
|
||||
%if &grant_type=authorization_code %then %do;
|
||||
"Authorization"="Bearer &&&access_token_var"
|
||||
%end;
|
||||
;
|
||||
run;
|
||||
%if &SYS_PROCHTTP_STATUS_CODE ne 200 and &SYS_PROCHTTP_STATUS_CODE ne 201 %then
|
||||
%do;
|
||||
data _null_;infile &fname1;input;putlog _infile_;run;
|
||||
%mp_abort(mac=&sysmacroname
|
||||
,msg=%str(logfetch: &SYS_PROCHTTP_STATUS_CODE &SYS_PROCHTTP_STATUS_PHRASE)
|
||||
)
|
||||
%end;
|
||||
|
||||
data _null_;
|
||||
file "&fpath3..lua";
|
||||
put '
|
||||
infile = io.open (sas.symget("fpath1"), "r")
|
||||
outfile = io.open (sas.symget("fpath2"), "w")
|
||||
io.input(infile)
|
||||
local resp=json.decode(io.read())
|
||||
for i, v in pairs(resp["items"]) do
|
||||
outfile:write(v.line,"\n")
|
||||
end
|
||||
io.close(infile)
|
||||
io.close(outfile)
|
||||
';
|
||||
run;
|
||||
%inc "&fpath3..lua";
|
||||
|
||||
/* write log out to the specified fileref */
|
||||
data _null_;
|
||||
infile &fname2 end=last;
|
||||
file &outref mod;
|
||||
if _n_=1 then do;
|
||||
put "/** SASJS Viya Job Log Extract start: &uri **/";
|
||||
end;
|
||||
input;
|
||||
put _infile_;
|
||||
%if &mdebug=1 %then %do;
|
||||
putlog _infile_;
|
||||
%end;
|
||||
if last then do;
|
||||
put "/** SASJS Viya Job Log Extract end: &uri **/";
|
||||
end;
|
||||
run;
|
||||
|
||||
%if &mdebug=0 %then %do;
|
||||
filename &fname1 clear;
|
||||
filename &fname2 clear;
|
||||
filename &fname3 clear;
|
||||
%end;
|
||||
%else %do;
|
||||
%put _local_;
|
||||
%end;
|
||||
%mend;
|
||||
|
||||
|
||||
|
||||
@@ -101,7 +101,7 @@
|
||||
%let oauth_bearer=oauth_bearer=sas_services;
|
||||
%let &access_token_var=;
|
||||
%end;
|
||||
%put &sysmacroname: grant_type=&grant_type;
|
||||
|
||||
%mp_abort(iftrue=(&grant_type ne authorization_code and &grant_type ne password
|
||||
and &grant_type ne sas_services
|
||||
)
|
||||
|
||||
@@ -9,18 +9,19 @@
|
||||
|
||||
## Input table (minimum variables needed)
|
||||
|
||||
@li FLOW_ID - Numeric value, provides sequential ordering capability
|
||||
@li _CONTEXTNAME - Dictates which context should be used to run the job. If
|
||||
blank, will default to `SAS Job Execution compute context`.
|
||||
@li _PROGRAM - Provides the path to the job itself
|
||||
@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`.
|
||||
|
||||
Any additional variables provided in this table are converted into macro
|
||||
variables and passed into the relevant job.
|
||||
|
||||
| FLOW_ID| _CONTEXTNAME |_PROGRAM|
|
||||
|_PROGRAM| FLOW_ID (optional)| _CONTEXTNAME (optional) |
|
||||
|---|---|---|
|
||||
|0|SAS Job Execution compute context|/Public/jobs/somejob1|
|
||||
|0|SAS Job Execution compute context|/Public/jobs/somejob2|
|
||||
|/Public/jobs/somejob1|0|SAS Job Execution compute context|
|
||||
|/Public/jobs/somejob2|0|SAS Job Execution compute context|
|
||||
|
||||
## Output table (minimum variables produced)
|
||||
|
||||
@@ -33,6 +34,9 @@
|
||||
|
||||

|
||||
|
||||
To avoid hammering the box with many hits in rapid succession, a one
|
||||
second pause is made between every request.
|
||||
|
||||
|
||||
## Example
|
||||
|
||||
@@ -81,7 +85,16 @@
|
||||
|
||||
Trigger the flow
|
||||
|
||||
%mv_jobflow(inds=work.inputjobs,outds=work.results,maxconcurrency=4)
|
||||
%mv_jobflow(inds=work.inputjobs
|
||||
,maxconcurrency=4
|
||||
,outds=work.results
|
||||
,outref=myjoblog
|
||||
)
|
||||
|
||||
data _null_;
|
||||
infile myjoblog;
|
||||
input; put _infile_;
|
||||
run;
|
||||
|
||||
|
||||
@param [in] access_token_var= The global macro variable to contain the access token
|
||||
@@ -93,7 +106,9 @@
|
||||
@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.
|
||||
@param [in] mdebug= set to 1 to enable DEBUG messages
|
||||
@param [out] outds= The output dataset containing the results
|
||||
@param [out] outref= The output fileref to which to append the log file(s).
|
||||
|
||||
@version VIYA V.03.05
|
||||
@author Allan Bowe, source: https://github.com/sasjs/core
|
||||
@@ -113,6 +128,8 @@
|
||||
,maxconcurrency=8
|
||||
,access_token_var=ACCESS_TOKEN
|
||||
,grant_type=sas_services
|
||||
,outref=0
|
||||
,mdebug=0
|
||||
);
|
||||
%local oauth_bearer;
|
||||
%if &grant_type=detect %then %do;
|
||||
@@ -135,16 +152,29 @@
|
||||
,mac=&sysmacroname
|
||||
,msg=%str(Input dataset was not provided)
|
||||
)
|
||||
%mp_abort(iftrue=(%mf_existVarList(&inds,_CONTEXTNAME FLOW_ID _PROGRAM)=0)
|
||||
%mp_abort(iftrue=(%mf_existVarList(&inds,_PROGRAM)=0)
|
||||
,mac=&sysmacroname
|
||||
,msg=%str(The following columns must exist on input dataset &inds:
|
||||
_CONTEXTNAME FLOW_ID _PROGRAM)
|
||||
,msg=%str(The _PROGRAM column must exist on input dataset &inds)
|
||||
)
|
||||
%mp_abort(iftrue=(&maxconcurrency<1)
|
||||
,mac=&sysmacroname
|
||||
,msg=%str(The maxconcurrency variable should be a positive integer)
|
||||
)
|
||||
|
||||
/* set defaults if not provided */
|
||||
%if %mf_existVarList(&inds,_CONTEXTNAME FLOW_ID)=0 %then %do;
|
||||
data &inds;
|
||||
%if %mf_existvarList(&inds,_CONTEXTNAME)=0 %then %do;
|
||||
length _CONTEXTNAME $128;
|
||||
retain _CONTEXTNAME "SAS Job Execution compute context";
|
||||
%end;
|
||||
%if %mf_existvarList(&inds,FLOW_ID)=0 %then %do;
|
||||
retain FLOW_ID 0;
|
||||
%end;
|
||||
set &inds;
|
||||
run;
|
||||
%end;
|
||||
|
||||
%local missings;
|
||||
proc sql noprint;
|
||||
select count(*) into: missings
|
||||
@@ -222,8 +252,8 @@ data;run;%let jdswaitfor=&syslast;
|
||||
jparams='jparams'!!left(symget('jid'));
|
||||
call symputx(jparams,substr(_infile_,3,length(_infile_)-4));
|
||||
run;
|
||||
%local joburi&jid;
|
||||
%let joburi&jid=0; /* used in next loop */
|
||||
%local jobuid&jid;
|
||||
%let jobuid&jid=0; /* used in next loop */
|
||||
%end;
|
||||
%local concurrency completed;
|
||||
%let concurrency=0;
|
||||
@@ -234,8 +264,21 @@ data;run;%let jdswaitfor=&syslast;
|
||||
* now we can execute the jobs up to the maxconcurrency setting
|
||||
*/
|
||||
%if "&&job&jid" ne "0" %then %do; /* this var is zero if job finished */
|
||||
%if "&&joburi&jid"="0" and &concurrency<&maxconcurrency %then %do;
|
||||
/* job has not been triggered and we have free slots */
|
||||
|
||||
/* check to see if the job finished in the previous round */
|
||||
%if %sysfunc(exist(&outds))=1 %then %do;
|
||||
%local jobcheck; %let jobcheck=0;
|
||||
proc sql noprint;
|
||||
select count(*) into: jobcheck
|
||||
from &outds where uuid="&&jobuid&jid";
|
||||
%if &jobcheck>0 %then %do;
|
||||
%put &&job&jid in flow &fid with uid &&jobuid&jid completed!;
|
||||
%let job&jid=0;
|
||||
%end;
|
||||
%end;
|
||||
|
||||
/* check if job was triggered and if so, if we have enough slots to run */
|
||||
%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);
|
||||
@@ -249,27 +292,22 @@ data;run;%let jdswaitfor=&syslast;
|
||||
format jobparams $32767.;
|
||||
set &jdsapp(where=(method='GET' and rel='state'));
|
||||
jobparams=symget("jparams&jid");
|
||||
call symputx("joburi&jid",uri,'l');
|
||||
/* uri here has the /state suffix */
|
||||
uuid=scan(uri,-2,'/');
|
||||
call symputx("jobuid&jid",uuid,'l');
|
||||
run;
|
||||
proc append base=&jdsrunning data=&jdsapp;
|
||||
run;
|
||||
%let concurrency=%eval(&concurrency+1);
|
||||
%end;
|
||||
%else %if %sysfunc(exist(&outds))=1 %then %do;
|
||||
/* check to see if the job has finished as was previously executed */
|
||||
%local jobcheck; %let jobcheck=0;
|
||||
proc sql noprint;
|
||||
select count(*) into: jobcheck
|
||||
from &outds where uri="&&joburi&jid";
|
||||
%if &jobcheck>0 %then %do;
|
||||
%put &&job&jid in flow &fid with uri &&joburi&jid completed!;
|
||||
%let job&jid=0;
|
||||
%end;
|
||||
/* sleep one second after every request to smooth the impact */
|
||||
data _null_;
|
||||
call sleep(1,1);
|
||||
run;
|
||||
%end;
|
||||
%end;
|
||||
%if &jid=&jcnt %then %do;
|
||||
/* we are at the end of the loop - time to see which jobs have finished */
|
||||
%mv_jobwaitfor(ANY,inds=&jdsrunning,outds=&jdswaitfor)
|
||||
%mv_jobwaitfor(ANY,inds=&jdsrunning,outds=&jdswaitfor,outref=&outref)
|
||||
%local done;
|
||||
%let done=%mf_nobs(&jdswaitfor);
|
||||
%if &done>0 %then %do;
|
||||
@@ -278,13 +316,14 @@ data;run;%let jdswaitfor=&syslast;
|
||||
data &jdsapp;
|
||||
set &jdswaitfor;
|
||||
flow_id=&&flow&fid;
|
||||
uuid=scan(uri,-1,'/');
|
||||
run;
|
||||
proc append base=&outds data=&jdsapp;
|
||||
run;
|
||||
%end;
|
||||
proc sql;
|
||||
delete from &jdsrunning
|
||||
where uri in (select uri from &outds
|
||||
where uuid in (select uuid from &outds
|
||||
where state in ('canceled','completed','failed')
|
||||
);
|
||||
|
||||
@@ -298,5 +337,8 @@ data;run;%let jdswaitfor=&syslast;
|
||||
/* back up and execute the next flow */
|
||||
%end;
|
||||
|
||||
%if &mdebug=1 %then %do;
|
||||
%put _local_;
|
||||
%end;
|
||||
|
||||
%mend;
|
||||
|
||||
@@ -72,7 +72,7 @@
|
||||
should be in a `_program` variable.
|
||||
@param [out] outds= The output dataset containing the list of states by job
|
||||
(default=work.mv_jobexecute)
|
||||
|
||||
@param [out] outref= A fileref to which the spawned job logs should be appended.
|
||||
|
||||
@version VIYA V.03.04
|
||||
@author Allan Bowe, source: https://github.com/sasjs/core
|
||||
@@ -83,6 +83,7 @@
|
||||
@li mf_getuniquefileref.sas
|
||||
@li mf_existvar.sas
|
||||
@li mf_nobs.sas
|
||||
@li mv_getjoblog.sas
|
||||
|
||||
**/
|
||||
|
||||
@@ -91,6 +92,7 @@
|
||||
,grant_type=sas_services
|
||||
,inds=0
|
||||
,outds=work.mv_jobwaitfor
|
||||
,outref=0
|
||||
);
|
||||
%local oauth_bearer;
|
||||
%if &grant_type=detect %then %do;
|
||||
@@ -134,7 +136,7 @@ options noquotelenmax;
|
||||
data _null_;
|
||||
length jobparams $32767;
|
||||
set &inds end=last;
|
||||
call symputx(cats('joburi',_n_),uri,'l');
|
||||
call symputx(cats('joburi',_n_),substr(uri,1,55)!!'/state','l');
|
||||
call symputx(cats('jobname',_n_),_program,'l');
|
||||
call symputx(cats('jobparams',_n_),jobparams,'l');
|
||||
if last then call symputx('uricnt',_n_,'l');
|
||||
@@ -178,14 +180,20 @@ run;
|
||||
run;
|
||||
|
||||
%if &status=completed or &status=failed or &status=canceled %then %do;
|
||||
%local plainuri;
|
||||
%let plainuri=%substr(&&joburi&i,1,55);
|
||||
proc sql;
|
||||
insert into &outds set
|
||||
_program="&&jobname&i",
|
||||
uri="&&joburi&i",
|
||||
uri="&plainuri",
|
||||
state="&status",
|
||||
timestamp=datetime(),
|
||||
jobparams=symget("jobparams&i");
|
||||
%let joburi&i=0; /* do not re-check */
|
||||
/* fetch log */
|
||||
%if %str(&outref) ne 0 %then %do;
|
||||
%mv_getjoblog(uri=&plainuri,outref=&outref)
|
||||
%end;
|
||||
%end;
|
||||
%else %if &status=idle or &status=pending or &status=running %then %do;
|
||||
data _null_;
|
||||
|
||||
Reference in New Issue
Block a user