diff --git a/.gitpod.yml b/.gitpod.yml index 8b02b22..549bf82 100644 --- a/.gitpod.yml +++ b/.gitpod.yml @@ -5,4 +5,4 @@ image: file: .gitpod.dockerfile vscode: extensions: - - sasjs.sasjs-for-vscode@1.6.0:V4hJpMtbpekMcPRNhh4SXQ== \ No newline at end of file + - sasjs.sasjs-for-vscode@1.7.2:R6y1nzpFh2P99BZg5FgH5g== \ No newline at end of file diff --git a/.sasjslint b/.sasjslint new file mode 100644 index 0000000..9de0cb1 --- /dev/null +++ b/.sasjslint @@ -0,0 +1,10 @@ +{ + "noTrailingSpaces": true, + "noEncodedPasswords": true, + "hasDoxygenHeader": true, + "noSpacesInFileNames": true, + "maxLineLength": 120, + "lowerCaseFileNames": true, + "noTabIndentation": true, + "indentationMultiple": 2 +} \ No newline at end of file diff --git a/README.md b/README.md index f98ba8e..94746be 100644 --- a/README.md +++ b/README.md @@ -40,7 +40,7 @@ Documentation: https://core.sasjs.io - X command enabled - Prefixes: _mmw_,_mmu_,_mmx_ -**lua** library +**lua** library Wait - this is a macro library - what is LUA doing here? Well, it is a little known fact that you CAN run LUA within a SAS Macro. It has to be written to a text file with a `.lua` extension, from where you can `%include` it. So, without using the `proc lua` wrapper. @@ -51,7 +51,7 @@ To contribute, simply write your freeform LUA in the LUA folder. Then run the ` %ml_yourmodule() /* Execute. Do not use the restart keyword! */ -proc lua; +proc lua; submit; print(yourStuff); endsubmit; @@ -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

SAS Macros

@li mf_nobs.sas @li mm_assignlib.sas diff --git a/viya/mv_getjoblog.sas b/viya/mv_getjoblog.sas index 860928e..b72c553 100644 --- a/viya/mv_getjoblog.sas +++ b/viya/mv_getjoblog.sas @@ -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): @@ -174,7 +175,7 @@ data _null_; outfile:write(logloc) io.close(infile) io.close(outfile) - '; + '; run; %inc "&fpath3..lua"; /* get log path*/ @@ -228,7 +229,7 @@ data _null_; io.input(infile) local resp=json.decode(io.read()) for i, v in pairs(resp["items"]) do - outfile:write(v.line,"\n") + outfile:write(v.line,"\n") end io.close(infile) io.close(outfile) diff --git a/viya/mv_getrefreshtoken.sas b/viya/mv_getrefreshtoken.sas index d7b7904..82ecf39 100644 --- a/viya/mv_getrefreshtoken.sas +++ b/viya/mv_getrefreshtoken.sas @@ -1,24 +1,24 @@ - /** - @file mv_getrefreshtoken.sas - @brief deprecated - replaced by mv_tokenauth.sas +/** + @file mv_getrefreshtoken.sas + @brief deprecated - replaced by mv_tokenauth.sas - @version VIYA V.03.04 - @author Allan Bowe, source: https://github.com/sasjs/core + @version VIYA V.03.04 + @author Allan Bowe, source: https://github.com/sasjs/core -

SAS Macros

- @li mv_tokenauth.sas +

SAS Macros

+ @li mv_tokenauth.sas - **/ +**/ - %macro mv_getrefreshtoken(client_id=someclient - ,client_secret=somesecret - ,grant_type=authorization_code - ,code= - ,user= - ,pass= - ,access_token_var=ACCESS_TOKEN - ,refresh_token_var=REFRESH_TOKEN - ); +%macro mv_getrefreshtoken(client_id=someclient + ,client_secret=somesecret + ,grant_type=authorization_code + ,code= + ,user= + ,pass= + ,access_token_var=ACCESS_TOKEN + ,refresh_token_var=REFRESH_TOKEN + ); %mv_tokenauth(client_id=&client_id ,client_secret=&client_secret diff --git a/viya/mv_getusergroups.sas b/viya/mv_getusergroups.sas index b67920c..72a4d21 100644 --- a/viya/mv_getusergroups.sas +++ b/viya/mv_getusergroups.sas @@ -65,7 +65,7 @@ proc http method='GET' out=&fname1 &oauth_bearer url="&base_uri/identities/users/&user/memberships?limit=10000"; headers %if &grant_type=authorization_code %then %do; - "Authorization"="Bearer &&&access_token_var" + "Authorization"="Bearer &&&access_token_var" %end; "Accept"="application/json"; run; diff --git a/viya/mv_getusers.sas b/viya/mv_getusers.sas index bef7bf4..935df83 100644 --- a/viya/mv_getusers.sas +++ b/viya/mv_getusers.sas @@ -37,11 +37,11 @@ @param access_token_var= The global macro variable to contain the access token @param grant_type= valid values: - * password - * authorization_code - * detect - will check if access_token exists, if not will use sas_services if - a SASStudioV session else authorization_code. Default option. - * sas_services - will use oauth_bearer=sas_services + * password + * authorization_code + * detect - will check if access_token exists, if not will use sas_services if + a SASStudioV session else authorization_code. Default option. + * sas_services - will use oauth_bearer=sas_services @param outds= The library.dataset to be created that contains the list of groups diff --git a/viya/mv_jobflow.sas b/viya/mv_jobflow.sas index 6b4142a..5f45d6b 100644 --- a/viya/mv_jobflow.sas +++ b/viya/mv_jobflow.sas @@ -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,15 +97,17 @@ 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. + @param [in] maxconcurrency= The max number of parallel jobs to run. Default=8. @param [in] raise_err=0 Set to 1 to raise SYSCC when a job does not complete succcessfully @param [in] mdebug= set to 1 to enable DEBUG messages @@ -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; diff --git a/viya/mv_jobwaitfor.sas b/viya/mv_jobwaitfor.sas index 9820903..4735f7c 100644 --- a/viya/mv_jobwaitfor.sas +++ b/viya/mv_jobwaitfor.sas @@ -225,7 +225,7 @@ run; %else %let SYSCC=5; %put %str(ERR)OR: Job &&jobname&i. did not complete successfully. &stateDetails; %return; - %end; + %end; %end; %end; diff --git a/viya/mv_registerclient.sas b/viya/mv_registerclient.sas index 9c2f585..a1fca07 100644 --- a/viya/mv_registerclient.sas +++ b/viya/mv_registerclient.sas @@ -4,15 +4,17 @@ @details When building apps on SAS Viya, an client id and secret is required. 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/ + more info: https://developer.sas.com/reference/auth/#register + 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,8 +101,9 @@ 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"; - headers "X-Consul-Token"="&consul_token"; + url="&base_uri/SASLogon/oauth/clients/consul?callback=false%str(&)%trim( + )serviceId=app"; + headers "X-Consul-Token"="&consul_token"; run; %let libref=%mf_getuniquelibref(); @@ -111,8 +116,8 @@ data _null_; run; /** - * register the new client - */ + * register the new client + */ %let fname2=%mf_getuniquefileref(); %if x&client_id.x=xx %then %do; %let client_id=client_%sysfunc(ranuni(0),hex16.); @@ -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; diff --git a/viya/mv_tokenauth.sas b/viya/mv_tokenauth.sas index 4263683..47c2d58 100644 --- a/viya/mv_tokenauth.sas +++ b/viya/mv_tokenauth.sas @@ -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) ) @@ -99,7 +103,7 @@ data _null_; file &fref1; if "&grant_type"='authorization_code' then string=cats( - 'grant_type=authorization_code&code=',symget('code')); + 'grant_type=authorization_code&code=',symget('code')); else string=cats('grant_type=password&username=',symget('user') ,'&password=',symget(pass)); call symputx('grantstring',cats("'",string,"'")); @@ -107,8 +111,8 @@ run; /*data _null_;infile &fref1;input;put _infile_;run;*/ /** - * Request access token - */ + * Request access token + */ %if &base_uri=#NOTSET# %then %let base_uri=%mf_getplatform(VIYARESTAPI); %let fref2=%mf_getuniquefileref(); @@ -123,8 +127,8 @@ run; /*data _null_;infile &fref2;input;put _infile_;run;*/ /** - * Extract access / refresh tokens - */ + * Extract access / refresh tokens + */ %let libref=%mf_getuniquelibref(); libname &libref JSON fileref=&fref2; diff --git a/viya/mv_tokenrefresh.sas b/viya/mv_tokenrefresh.sas index a3504bd..24977b7 100644 --- a/viya/mv_tokenrefresh.sas +++ b/viya/mv_tokenrefresh.sas @@ -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) ) @@ -92,8 +94,8 @@ options noquotelenmax; ) /** - * Request access token - */ + * Request access token + */ %local base_uri; /* location of rest apis */ %let base_uri=%mf_getplatform(VIYARESTAPI); @@ -111,8 +113,8 @@ run; /*data _null_;infile &fref1;input;put _infile_;run;*/ /** - * Extract access / refresh tokens - */ + * Extract access / refresh tokens + */ %let libref=%mf_getuniquelibref(); libname &libref JSON fileref=&fref1; diff --git a/viya/mv_webout.sas b/viya/mv_webout.sas index c02d1bb..44f965b 100644 --- a/viya/mv_webout.sas +++ b/viya/mv_webout.sas @@ -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; @@ -141,7 +142,7 @@ /* setup webout */ OPTIONS NOBOMFILE; %if "X&SYS_JES_JOB_URI.X"="XX" %then %do; - filename _webout temp lrecl=999999 mod; + filename _webout temp lrecl=999999 mod; %end; %else %do; filename _webout filesrvc parenturi="&SYS_JES_JOB_URI" @@ -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 */