1
0
mirror of https://github.com/sasjs/core.git synced 2025-12-11 06:24:35 +00:00

Compare commits

...

67 Commits

Author SHA1 Message Date
Allan Bowe
842662aae1 Merge pull request #172 from sasjs/serverupdates
fix: updating mp_streamfile for sasjs/server compatibility
2022-02-28 23:21:48 +02:00
munja
876fac2332 feat: several macros for working with the sasjs/server apis 2022-02-28 21:17:38 +00:00
Allan Bowe
427deca350 Merge pull request #178 from sasjs/allanbowe/enable-consul-token-as-177
feat: adding consul_token option as parameter in mv_registerclient.
2022-02-24 23:20:03 +02:00
Allan Bowe
07bde4b25c feat: adding consul_token option as parameter in mv_registerclient. Closes #177 2022-02-24 21:16:23 +00:00
Allan Bowe
80b06af581 Merge pull request #176 from sasjs/streamserver
feat: adding SASjs server support to mp_streamfile.sas
2022-02-23 19:02:37 +02:00
Allan Bowe
3c026811e9 feat: adding SASjs server support to mp_streamfile.sas 2022-02-23 17:01:50 +00:00
Allan Bowe
cf547ce7e4 Merge pull request #175 from sasjs/authbranch
fix: tidyup of mm_getauthinfo.sas
2022-02-23 11:43:33 +02:00
Allan Bowe
6952c79899 fix: tidyup of mm_getauthinfo.sas 2022-02-23 09:42:47 +00:00
Allan Bowe
09e3f63da7 Merge pull request #174 from sasjs/stpabortissue
fix: missing dependency in mm_createstp
2022-02-22 13:47:05 +02:00
Allan Bowe
d6956f4122 fix: missing dependency in mm_createstp 2022-02-22 11:46:31 +00:00
Allan Bowe
6fca73e7da fix: adding content type to SASjs server webout response 2022-02-21 00:31:14 +00:00
Allan Bowe
880df4138c fix: removing wrapper for sasjs webout 2022-02-21 00:26:03 +00:00
munja
badf5b5761 fix: updating mp_streamfile for sasjs/server compatibility 2022-02-18 22:22:52 +00:00
Allan Bowe
b174aa25b3 Merge pull request #171 from sasjs/httpheader
feat: new httpheader macro (and test) for sasjs/server
2022-02-16 18:25:41 +02:00
Allan Bowe
bc6eac6977 Merge pull request #170 from sasjs/dependabot/npm_and_yarn/follow-redirects-1.14.8
chore(deps): bump follow-redirects from 1.14.7 to 1.14.8
2022-02-16 18:25:26 +02:00
munja
2d4d595e5d feat: new httpheader macro (and test) for sasjs/server 2022-02-16 16:21:00 +00:00
dependabot[bot]
7111fe14fb chore(deps): bump follow-redirects from 1.14.7 to 1.14.8
Bumps [follow-redirects](https://github.com/follow-redirects/follow-redirects) from 1.14.7 to 1.14.8.
- [Release notes](https://github.com/follow-redirects/follow-redirects/releases)
- [Commits](https://github.com/follow-redirects/follow-redirects/compare/v1.14.7...v1.14.8)

---
updated-dependencies:
- dependency-name: follow-redirects
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-02-14 04:41:55 +00:00
Allan Bowe
8499e38c55 Merge pull request #169 from sasjs/mp_cntlout
feat: new mp_cntlout.sas macro
2022-02-11 17:46:39 +02:00
munja
682d80b1b8 fix: warning in mp_getformats 2022-02-11 16:46:04 +01:00
munja
4fe6f233f2 chore: updating all.sas 2022-02-11 15:47:33 +01:00
munja
6ba3588eff feat: new mp_cntlout.sas macro 2022-02-11 14:22:38 +01:00
Allan Bowe
53aa403630 Merge pull request #168 from sasjs/loadformat
feat: new mp_loadformat macro
2022-02-10 15:46:20 +02:00
munja
cba9255732 feat: mp_loadformat macro (and test) 2022-02-10 14:08:19 +01:00
munja
a7b78c73c4 chore: adding mprintnest in debug mode in testinit.sas 2022-02-09 22:07:30 +01:00
munja
85e0b6a4a9 fix: upcasing vars in mp_assertscope 2022-02-09 22:07:03 +01:00
munja
3c7e762eeb fix: support for SASJS server type in mf_getplatform and mp_streamfile 2022-02-09 22:06:24 +01:00
munja
9a1f7d0985 feat: new macro (mp_md5) for calculating an md5 hash of a set of columns 2022-02-09 21:56:46 +01:00
munja
dfd60200fb chore(docs): removing invalid example 2022-02-08 23:23:13 +01:00
Allan Bowe
713f7544cd Merge pull request #167 from sasjs/issue166
feat: adding mddl_xx series of macros (and tests).  Closes #166
2022-02-07 15:42:01 +02:00
munja
de4e96ab01 fix: dependency under wrong header in mp_getformats 2022-02-07 14:41:32 +01:00
munja
3e7b15c7db feat: adding mddl_xx series of macros (and tests). Closes #166 2022-02-07 14:14:25 +01:00
Allan Bowe
eb2ccfbbca Merge pull request #165 from sasjs/fmtstore
feat: adding format catalog capability to mp_filterstore
2022-02-07 00:02:34 +02:00
munja
70e508e583 fix: failing test 2022-02-06 23:01:46 +01:00
munja
8b0acf2eae feat: adding format catalog capability to mp_filterstore 2022-02-06 22:12:00 +01:00
Allan Bowe
d254870439 Merge pull request #164 from sasjs/mf_verify
fix: updating mm_x macros following fix to mf_verifymacvars
2022-02-06 20:58:55 +02:00
munja
303225cb85 fix: updating mm_x macros following fix to mf_verifymacvars 2022-02-06 19:57:52 +01:00
Allan Bowe
90de167643 Merge pull request #163 from sasjs/mf_verfiy
fix: updating mf_abort param in mf_verifymacvars, also fixing return …
2022-02-06 17:16:54 +02:00
munja
8ee997de8e fix: updating mf_abort param in mf_verifymacvars, also fixing return code as per documentation, adding a test, and updating the header info 2022-02-06 16:11:18 +01:00
Allan Bowe
e27f6ac716 Merge pull request #162 from sasjs/mf_getapploc
fix: adding support for testsetup and testteardown in mf_getapploc.sas
2022-02-05 22:56:10 +02:00
munja
ec4de95fcf fix: reset syscc for testterm syscc check 2022-02-05 21:55:50 +01:00
munja
df0fa95519 fix: adding sasjs/core dependency - see: https://github.com/sasjs/cli/issues/1113 2022-02-05 21:29:03 +01:00
munja
2fe7fba79b fix: adding support for testsetup and testteardown in mf_getapploc.sas 2022-02-05 21:19:26 +01:00
Allan Bowe
e40234ee29 Merge pull request #160 from sasjs/allanbowe-patch-1
chore(docs): Update README.md to clarify LUA prefixes
2022-02-04 20:56:51 +02:00
Allan Bowe
a287cc27a7 Update README.md 2022-02-04 18:56:19 +00:00
munja
921186eb74 fix: adding images to mp_streamfile.sas 2022-02-03 20:17:43 +01:00
munja
6fd215ceff fix: streaming output in mp_streamfile by default (eg when param not found) 2022-02-03 20:13:57 +01:00
Allan Bowe
0297509aa0 Merge pull request #159 from sasjs/woff3
fix: avoiding error and including test in mp_streamfile.sas
2022-02-03 20:59:11 +02:00
munja
c5a8bc745d chore: fix test 2022-02-03 19:58:12 +01:00
munja
36aa466561 fix: avoiding error and including a test 2022-02-03 19:44:21 +01:00
Allan Bowe
009485e5b9 Merge pull request #158 from sasjs/woff2
fix: support for WOFF2 and TTF
2022-02-03 19:51:39 +02:00
munja
eb01c8772d fix: support for WOFF2 and TTF 2022-02-03 18:51:07 +01:00
Allan Bowe
e3fb69928c Merge pull request #157 from sasjs/dependabot
chore: adding dependabot
2022-02-03 19:31:16 +02:00
munja
65afa14466 chore: removing leading spaces 2022-02-03 18:30:49 +01:00
munja
0176b19616 chore: adding dependabot 2022-02-03 18:29:31 +01:00
Allan Bowe
9f3dfd9a59 Merge pull request #156 from sasjs/csv
adding WOFF to mp_streamfile
2022-02-03 19:23:09 +02:00
munja
513ea354ab chore: updating headers in mp_streamfile and running all.sas 2022-02-03 16:45:42 +01:00
munja
7b686e11c9 feat: adding WOFF support to mp_streamfile (also re-ordering sections alphabetically) 2022-02-03 16:44:39 +01:00
munja
3997000266 fix: encoding issue in mp_ds2csv (option should have been in quotes) 2022-02-03 15:00:46 +01:00
Allan Bowe
6e177d4cae Merge pull request #155 from sasjs/cmplib
fix: auto-detect cmplib in mcfxx funcs, mp_ds2csv supports dates etc,…
2022-02-02 23:43:43 +02:00
munja
3554991ff8 fix: auto-detect cmplib in mcfxx funcs, mp_ds2csv supports dates etc, fix to mp_abort in viya due to abort cancel FILE hard stop (according to docs it should continue outside of the include) 2022-02-02 21:18:51 +01:00
Allan Bowe
58d2d6382a Merge pull request #154 from sasjs/errtext
fix: removing syserrortext as it breaks when commas are embedded
2022-02-02 21:36:30 +02:00
Allan Bowe
67f28a366c fix: removing syserrortext as it breaks when commas are embedded 2022-02-02 19:35:54 +00:00
Allan Bowe
64f53acce2 Merge pull request #153 from sasjs/mcf_fmtclass
Mcf fmtclass
2022-02-01 19:50:36 +02:00
munja
2e790f69a1 fix: removing view due to potential for vbufsize violations 2022-02-01 16:14:37 +01:00
munja
e62011d97e chore: updating variable name to fit doc header 2022-02-01 13:52:08 +01:00
munja
cd8d16d09f chore: updating all.sas 2022-02-01 13:48:48 +01:00
munja
9c61965d4b feat: new macro (mcf_getfmttype) to determine the type (DATE,DATETIME,TIME,CHAR,NUM) of a SAS format 2022-02-01 13:48:23 +01:00
77 changed files with 3821 additions and 791 deletions

9
.github/dependabot.yml vendored Normal file
View File

@@ -0,0 +1,9 @@
version: 2
updates:
- package-ecosystem: npm
directory: '/'
schedule:
interval: monthly
open-pull-requests-limit: 3
allow:
- dependency-type: "production"

View File

@@ -31,19 +31,28 @@ Documentation: https://core.sasjs.io
## Components
### BASE library (SAS9/Viya)
### BASE library (All Platforms)
- OS independent
- Not metadata aware
- Works on all SAS Platforms
- No X command
- Prefixes: _mf_, _mp_
#### FCMP library (SAS9/Viya)
### DDL library (All Platforms)
- OS independent
- Works on all SAS Platforms
- No X command
- Prefixes: _mddl_(lib)_ -> where lib can be "SAS" (in relation to a SAS component) or "DC" (in relation to a Data Controller component)
This library will not be used for storing data entries (such as formats or datalines). Where this becomes necessary in the future, a new repo will be created, in order to keep the NPM bundle size down (for the benefit of those looking to embed purely macros in their applications).
### FCMP library (All Platforms)
- Function and macro names are identical, except for special cases
- Prefixes: _mcf_
The fcmp macros are used to generate fcmp functions, and can be used with or
without the `proc fcmp` wrapper.
The fcmp macros are used to generate fcmp functions, and can be used with or without the `proc fcmp` wrapper.
### META library (SAS9 only)
@@ -81,7 +90,7 @@ Macros used for interfacing with SAS Viya.
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.
To contribute, simply write your freeform LUA in the LUA folder. Then run the `build.py`, which will convert your LUA into a data step with put statements, and create the macro wrapper with a `ml_` prefix. You can then use your module in any program by running:
To contribute, simply write your freeform LUA in the LUA folder. Then run the `build.py`, which will convert all files with a ".lua" extension into a macro wrapper with an `ml_` prefix (embedding the necessary data step put statements). You can then use your module in any program by running:
```sas
/* compile the lua module */
@@ -95,8 +104,7 @@ endsubmit;
run;
```
- X command enabled
- Prefixes: _mmw_,_mmu_,_mmx_
- Prefixes: _ml_
## Installation
@@ -126,14 +134,15 @@ filename mc url "https://raw.githubusercontent.com/sasjs/core/main/all.sas";
- one macro per file
- prefixes:
- _mcf_ for macro compiled functions (proc fcmp)
- _mddl_ for macros containing DDL (Data Definition Language)
- _mf_ for macro functions (can be used in open code).
- _ml_ for macros that are used to compile LUA modules
- _mm_ for metadata macros (interface with the metadata server).
- _mmx_ for macros that use metadata and are XCMD enabled
- _mmx_ for macros that use metadata and are XCMD enabled (working on both windows and unix)
- _mp_ for macro procedures (which generate sas code)
- _ms_ for macro procedures that will only work with [@sasjs/server](https://github.com/sasjs/server)
- _mv_ for macro procedures that will only work in Viya
- _mx_ for macros that are XCMD enabled
- _mx_ for macros that are XCMD enabled (working on both windows and unix)
- follow verb-noun convention
- unix style line endings (lf)
- individual lines should be no more than 80 characters long
@@ -193,9 +202,13 @@ When contributing to this library, it is therefore important to ensure that all
## Breaking Changes
We are currently on major release v4. Breaking changes should be marked with the [deprecated](https://www.doxygen.nl/manual/commands.html#cmddeprecated) doxygen tag. The following changes are planned when the next major (breaking) release becomes necessary:
We are currently on major release v4. Breaking changes should be marked with the [deprecated](https://www.doxygen.nl/manual/commands.html#cmddeprecated) doxygen tag. The following changes are planned when the next major/breaking release (v5) becomes necessary:
* mp_testservice.sas to be renamed as mp_execute.sas (as it doesn't actually test anything)
* `insert_cmplib` option of mcf_xxx macros will be deprecated (the option is now checked automatically with value inserted only if needed)
* mcf_xxx macros to have `wrap=` option defaulted to YES for convenience. Set this option explicitly to avoid issues.
* mp_getddl.sas to be renamed to mp_ds2ddl.sas (consistent with other ds2xxx macros). A wrapper macro is already in place, and you are able to use this immediately. The default for SHOWLOG will also be YES instead of NO.
* mp_coretable.sas will be replaced by the standalone macros in the `ddl` folder (which are already available)
## Star Gazing
@@ -204,7 +217,6 @@ If you find this library useful, please leave a [star](https://github.com/sasjs/
![](https://starchart.cc/sasjs/core.svg)
## Contributors ✨
<!-- ALL-CONTRIBUTORS-BADGE:START - Do not remove or modify this section -->
[![All Contributors](https://img.shields.io/badge/all_contributors-10-orange.svg?style=flat-square)](#contributors-)

1595
all.sas

File diff suppressed because it is too large Load Diff

View File

@@ -15,6 +15,7 @@
@li /data
@li /jobs
@li /services
@li /tests
@li /tests/jobs
@li /tests/services
@li /tests/macros
@@ -46,9 +47,13 @@
/**
* First check we are not in the tests/macros folder (which has no subfolders)
* or specifically in the testsetup or testteardown services
*/
%if %index(&pgm,/tests/macros/) %then %do;
%let root=%substr(&pgm,1,%index(&pgm,/tests/macros)-1);
%if %index(&pgm,/tests/macros/)
or %index(&pgm,/tests/testsetup)
or %index(&pgm,/tests/testteardown)
%then %do;
%let root=%substr(&pgm,1,%index(&pgm,/tests)-1);
&root
%return;
%end;

View File

@@ -22,6 +22,12 @@
)/*/STORE SOURCE*/;
%local a b c;
%if &switch.NONE=NONE %then %do;
%if %symexist(sasjsprocessmode) %then %do;
%if &sasjsprocessmode=Stored Program %then %do;
SASJS
%return;
%end;
%end;
%if %symexist(sysprocessmode) %then %do;
%if "&sysprocessmode"="SAS Object Server"
or "&sysprocessmode"= "SAS Compute Server" %then %do;

View File

@@ -1,6 +1,6 @@
/**
@file
@brief Checks if a set of macro variables exist / contain values.
@brief Checks if a set of macro variables exist AND contain values.
@details Writes ERROR to log if abortType is SOFT, else will call %mf_abort.
Usage:
@@ -14,10 +14,11 @@
<h4> SAS Macros </h4>
@li mf_abort.sas
@param verifyvars space separated list of macro variable names
@param makeupcase= set to YES to convert all variable VALUES to
@param [in] verifyvars Space separated list of macro variable names
@param [in] makeupcase= (NO) Set to YES to convert all variable VALUES to
uppercase.
@param mAbort= Abort Type. Default is SOFT (writes err to log).
@param [in] mAbort= (SOFT) Abort Type. When SOFT, simply writes an err
message to the log.
Set to any other value to call mf_abort (which can be configured to abort in
various fashions according to context).
@@ -58,8 +59,14 @@
%goto exit_success;
%exit_err:
%if &mAbort=SOFT %then %put %str(ERR)OR: &abortmsg;
%else %mf_abort(mac=mf_verifymacvars,type=&mabort,msg=&abortmsg);
%put %str(ERR)OR: &abortmsg;
%mf_abort(iftrue=(&mabort ne SOFT),
mac=mf_verifymacvars,
msg=%str(&abortmsg)
)
0
%return;
%exit_success:
1
%mend mf_verifymacvars;

View File

@@ -66,7 +66,10 @@
%if %length(&mac)>0 %then %put NOTE- called by &mac;
%put NOTE - &msg;
%if %symexist(_SYSINCLUDEFILEDEVICE) %then %do;
%if %symexist(_SYSINCLUDEFILEDEVICE)
/* abort cancel FILE does not restart outside the INCLUDE on Viya 3.5 */
and "&SYSPROCESSNAME " ne "Compute Server "
%then %do;
%if "*&_SYSINCLUDEFILEDEVICE*" ne "**" %then %do;
data &errds;
iftrue='1=1';

View File

@@ -74,7 +74,8 @@
outds=work.test_results
)/*/STORE SOURCE*/;
%local ds test_result test_comments del add mod ilist;
%let ilist=%upcase(&sasjs_prefix._FUNCTIONS &ignorelist);
%let ilist=%upcase(&sasjs_prefix._FUNCTIONS SYS_PROCHTTP_STATUS_CODE
SYS_PROCHTTP_STATUS_CODE SYS_PROCHTTP_STATUS_PHRASE &ignorelist);
/**
* this sets up the global vars, it will also enter STRICT mode. If this
@@ -89,7 +90,7 @@
create table &scopeds as
select name,offset,value
from dictionary.macros
where scope="&scope" and name not in (%mf_getquotedstr(&ilist))
where scope="&scope" and upcase(name) not in (%mf_getquotedstr(&ilist))
order by name,offset;
%end;
%else %if &action=COMPARE %then %do;
@@ -98,7 +99,7 @@
create table _data_ as
select name,offset,value
from dictionary.macros
where scope="&scope" and name not in (%mf_getquotedstr(&ilist))
where scope="&scope" and upcase(name) not in (%mf_getquotedstr(&ilist))
order by name,offset;
%let ds=&syslast;
@@ -141,4 +142,4 @@
drop table &ds;
%end;
%mend mp_assertscope;
%mend mp_assertscope;

85
base/mp_cntlout.sas Normal file
View File

@@ -0,0 +1,85 @@
/**
@file mp_cntlout.sas
@brief Creates a cntlout dataset in a consistent format
@details The dataset produced by proc format in the cntlout option will vary
according to its contents.
When dealing with formats from an ETL perspective (eg in [Data Controller for
SAS](https://datacontroller.io)), it is important that the output dataset
has a consistent model (and compariable values).
This macro makes use of mddl_sas_cntlout.sas to provide the consistent model,
and will left-align the start and end values when dealing with numeric ranges
to enable consistency when checking for differences.
usage:
%mp_cntlout(libcat=yourlib.cat,cntlout=work.formatexport)
@param [in] libcat The library.catalog reference
@param [in] fmtlist= (0) provide a space separated list of specific formats to
extract
@param [in] iftrue= (1=1) A condition under which the macro should be executed
@param [out] cntlout= (work.fmtextract) Libds reference for the output dataset
<h4> SAS Macros </h4>
@li mddl_sas_cntlout.sas
@li mf_getuniquename.sas
<h4> Related Macros </h4>
@li mf_getvarformat.sas
@li mp_getformats.sas
@li mp_loadformat.sas
@li mp_ds2fmtds.sas
@version 9.2
@author Allan Bowe
@cond
**/
%macro mp_cntlout(
iftrue=(1=1)
,libcat=
,cntlout=work.fmtextract
,fmtlist=0
)/*/STORE SOURCE*/;
%local ddlds cntlds i;
%if not(%eval(%unquote(&iftrue))) %then %return;
%let ddlds=%mf_getuniquename();
%let cntlds=%mf_getuniquename();
%mddl_sas_cntlout(libds=&ddlds)
%if %index(&libcat,-)>0 and %scan(&libcat,2,-)=FC %then %do;
%let libcat=%scan(&libcat,1,-);
%end;
proc format lib=&libcat cntlout=&cntlds;
%if "&fmtlist" ne "0" %then %do;
select
%do i=1 %to %sysfunc(countw(&fmtlist));
%scan(&fmtlist,&i,%str( ))
%end;
;
%end;
run;
data &cntlout;
if 0 then set &ddlds;
set &cntlds;
if type="N" then do;
start=cats(start);
end=cats(end);
end;
run;
proc sort;
by fmtname start;
run;
proc sql;
drop table &ddlds,&cntlds;
%mend mp_cntlout;
/** @endcond */

View File

@@ -5,24 +5,32 @@
make use of permanent tables. To avoid duplication in definitions, this
macro provides a central location for managing the corresponding DDL.
Note - this macro is likely to be deprecated in future in favour of a
dedicated "datamodel" folder (prefix mddl)
Any corresponding data would go in a seperate repo, to avoid this one
ballooning in size!
Example usage:
%mp_coretable(LOCKTABLE,libds=work.locktable)
@param [in] table_ref The type of table to create. Example values:
@li DIFFTABLE - Used to store changes to tables. Used by mp_storediffs.sas
and mp_stackdiffs.sas
@li FILTER_DETAIL - For storing detailed filter values. Used by
mp_filterstore.sas.
@li FILTER_SUMMARY - For storing summary filter values. Used by
mp_filterstore.sas.
@li LOCKANYTABLE - For "locking" tables prior to multipass loads. Used by
mp_lockanytable.sas
@li MAXKEYTABLE - For storing the maximum retained key information. Used
by mp_retainedkey.sas
@li DIFFTABLE
@li FILTER_DETAIL
@li FILTER_SUMMARY
@li LOCKANYTABLE
@li MAXKEYTABLE
@param [in] libds= (0) The library.dataset reference used to create the table.
If not provided, then the DDL is simply printed to the log.
<h4> SAS Macros </h4>
@li mddl_dc_difftable.sas
@li mddl_dc_filterdetail.sas
@li mddl_dc_filtersummary.sas
@li mddl_dc_locktable.sas
@li mddl_dc_maxkeytable.sas
<h4> Related Macros </h4>
@li mp_filterstore.sas
@li mp_lockanytable.sas
@@ -41,76 +49,21 @@
%let outds=%sysfunc(ifc(&libds=0,_data_,&libds));
proc sql;
%if &table_ref=DIFFTABLE %then %do;
create table &outds(
load_ref char(36) label='unique load reference',
processed_dttm num format=E8601DT26.6 label='Processed at timestamp',
libref char(8) label='Library Reference (8 chars)',
dsn char(32) label='Dataset Name (32 chars)',
key_hash char(32) label=
'MD5 Hash of primary key values (pipe seperated)',
move_type char(1) label='Either (A)ppended, (D)eleted or (M)odified',
is_pk num label='Is Primary Key Field? (1/0)',
is_diff num label=
'Did value change? (1/0/-1). Always -1 for appends and deletes.',
tgtvar_type char(1) label='Either (C)haracter or (N)umeric',
tgtvar_nm char(32) label='Target variable name (32 chars)',
oldval_num num format=best32. label='Old (numeric) value',
newval_num num format=best32. label='New (numeric) value',
oldval_char char(32765) label='Old (character) value',
newval_char char(32765) label='New (character) value',
constraint pk_mpe_audit
primary key(load_ref,libref,dsn,key_hash,tgtvar_nm)
);
%mddl_dc_difftable(libds=&outds)
%end;
%else %if &table_ref=LOCKTABLE %then %do;
create table &outds(
lock_lib char(8),
lock_ds char(32),
lock_status_cd char(10) not null,
lock_user_nm char(100) not null ,
lock_ref char(200),
lock_pid char(10),
lock_start_dttm num format=E8601DT26.6,
lock_end_dttm num format=E8601DT26.6,
constraint pk_mp_lockanytable primary key(lock_lib,lock_ds));
%mddl_dc_locktable(libds=&outds)
%end;
%else %if &table_ref=FILTER_SUMMARY %then %do;
create table &outds(
filter_rk num not null,
filter_hash char(32) not null,
filter_table char(41) not null,
processed_dttm num not null format=E8601DT26.6,
constraint pk_mpe_filteranytable
primary key(filter_rk));
%mddl_dc_filtersummary(libds=&outds)
%end;
%else %if &table_ref=FILTER_DETAIL %then %do;
create table &outds(
filter_hash char(32) not null,
filter_line num not null,
group_logic char(3) not null,
subgroup_logic char(3) not null,
subgroup_id num not null,
variable_nm varchar(32) not null,
operator_nm varchar(12) not null,
raw_value varchar(4000) not null,
processed_dttm num not null format=E8601DT26.6,
constraint pk_mpe_filteranytable
primary key(filter_hash,filter_line));
%mddl_dc_filterdetail(libds=&outds)
%end;
%else %if &table_ref=MAXKEYTABLE %then %do;
create table &outds(
keytable varchar(41) label='Base table in libref.dataset format',
keycolumn char(32) format=$32.
label='The Retained key field containing the key values.',
max_key num label=
'Integer representing current max RK or SK value in the KEYTABLE',
processed_dttm num format=E8601DT26.6
label='Datetime this value was last updated',
constraint pk_mpe_maxkeyvalues
primary key(keytable));
%mddl_dc_maxkeytable(libds=&outds)
%end;
%if &libds=0 %then %do;
describe table &syslast;
drop table &syslast;

View File

@@ -3,10 +3,28 @@
@brief Export a dataset to a CSV file WITH leading blanks
@details Export a dataset to a file or fileref, retaining leading blanks.
When using SASJS headerformat, the input statement is provided in the first
row of the CSV.
Usage:
%mp_ds2csv(sashelp.class,outref="%sysfunc(pathname(work))/file.csv")
filename example temp;
%mp_ds2csv(sashelp.air,outref=example,headerformat=SASJS)
data; infile example; input;put _infile_; if _n_>5 then stop;run;
data _null_;
infile example;
input;
call symputx('stmnt',_infile_);
stop;
run;
data work.want;
infile example dsd firstobs=2;
input &stmnt;
run;
Why use mp_ds2csv over, say, proc export?
1. Ability to retain leading blanks (this is a major one)
@@ -23,17 +41,24 @@
@li LABEL - Use the variable label (or name, if blank)
@li NAME - Use the variable name
@li SASJS - Used to create sasjs-formatted input CSVs, eg for use in
mp_testservice.sas
mp_testservice.sas. This format will supply an input statement in the
first row, making ingestion by datastep a breeze. Special misisng values
will be prefixed with a period (eg `.A`) to enable ingestion on both SAS 9
and Viya. Dates / Datetimes etc are identified by the format type (lookup
with mcf_getfmttype.sas) and converted to human readable formats (not
numbers).
@param [out] outfile= The output filename - should be quoted.
@param [out] outref= (0) The output fileref (takes precedence if provided)
@param [in] outencoding= (0) The output encoding to use (unquoted)
@param [in] outencoding= (0) The (quoted) output encoding to use, eg `"UTF-8"`
@param [in] termstr= (CRLF) The line seperator to use. For SASJS, will
always be CRLF. Valid values:
@li CRLF
@li LF
<h4> SAS Macros </h4>
@li mcf_getfmttype.sas
@li mf_getuniquename.sas
@li mf_getvarformat.sas
@li mf_getvarlist.sas
@li mf_getvartype.sas
@@ -50,7 +75,7 @@
,termstr=CRLF
)/*/STORE SOURCE*/;
%local outloc delim i varlist var vcnt vat dsv vcom vmiss;
%local outloc delim i varlist var vcnt vat dsv vcom vmiss fmttype vfmt;
%if not %sysfunc(exist(&ds)) %then %do;
%put %str(WARN)ING: &ds does not exist;
@@ -60,7 +85,7 @@
%if %index(&ds,.)=0 %then %let ds=WORK.&ds;
%if &outencoding=0 %then %let outencoding=;
%else %let outencoding=encoding="&outencoding";
%else %let outencoding=encoding=&outencoding;
%if &outref=0 %then %let outloc=&outfile;
%else %let outloc=&outref;
@@ -68,6 +93,7 @@
%if &headerformat=SASJS %then %do;
%let delim=",";
%let termstr=CRLF;
%mcf_getfmttype(wrap=YES)
%end;
%else %if &dlm=COMMA %then %let delim=",";
%else %let delim=";";
@@ -77,7 +103,8 @@
/* first get headers */
data _null_;
file &outloc &outencoding lrecl=32767 termstr=&termstr;
length header $ 2000 varnm $32 dlm $1;
length header $ 2000 varnm vfmt $32 dlm $1 fmttype $8;
call missing(of _all_);
dsid=open("&ds.","i");
num=attrn(dsid,"nvars");
dlm=&delim;
@@ -92,7 +119,14 @@ data _null_;
%end;
%else %if &headerformat=SASJS %then %do;
if vartype(dsid,i)='C' then header=cats(varnm,':$char',varlen(dsid,i),'.');
else header=cats(varnm,':best.');
else do;
vfmt=coalescec(varfmt(dsid,i),'0');
fmttype=mcf_getfmttype(vfmt);
if fmttype='DATE' then header=cats(varnm,':date9.');
else if fmttype='DATETIME' then header=cats(varnm,':E8601DT26.6');
else if fmttype='TIME' then header=cats(varnm,':TIME12.');
else header=cats(varnm,':best.');
end;
%end;
%else %do;
%put &sysmacroname: Invalid headerformat value (&headerformat);
@@ -147,14 +181,29 @@ data _null_;
%let vcom=;
%end;
%if %mf_getvartype(&ds,&var)=N %then %do;
%if &headerformat = SASJS %then %do;
%let vcom=&delim;
%let fmttype=%sysfunc(mcf_getfmttype(%mf_getvarformat(&ds,&var)0));
%if &fmttype=DATE %then %let vfmt=DATE9.;
%else %if &fmttype=DATETIME %then %let vfmt=E8601DT26.6;
%else %if &fmttype=TIME %then %let vfmt=TIME12.;
%else %do;
%let vfmt=;
%let vcom=;
%end;
%end;
%else %let vcom=;
/* must use period - in order to work in both 9.4 and Viya 3.5 */
if missing(&var) and &var ne %sysfunc(getoption(MISSING)) then do;
&vmiss=cats('.',&var);
put &vmiss &vat;
end;
else put &var &vat;
else put &var &vfmt &vcom &vat;
%end;
%else %do;
%if &i ne &vcnt %then %let vcom=&delim;
put &var &&vlen&i &vcom &vat;
%end;
%end;

30
base/mp_ds2ddl.sas Normal file
View File

@@ -0,0 +1,30 @@
/**
@file
@brief A wrapper for mp_getddl.sas
@details In the next release, this will be the main version.
<h4> SAS Macros </h4>
@li mp_getddl.sas
**/
%macro mp_ds2ddl(libds,fref=getddl,flavour=SAS,showlog=YES,schema=
,applydttm=NO
)/*/STORE SOURCE*/;
%local libref;
%let libds=%upcase(&libds);
%let libref=%scan(&libds,1,.);
%if &libref=&libds %then %let libds=WORK.&libds;
%mp_getddl(%scan(&libds,1,.)
,%scan(&libds,2,.)
,fref=&fref
,flavour=SAS
,showlog=&showlog
,schema=&schema
,applydttm=&applydttm
)
%mend mp_ds2ddl;

View File

@@ -6,7 +6,8 @@
SYSCC to 1008 if bad records are found, and call mp_abort.sas for a
graceful service exit (configurable).
Used for dynamic filtering in [Data Controller for SAS&reg;](https://datacontroller.io).
Used for dynamic filtering in [Data Controller for SAS&reg;](
https://datacontroller.io).
Usage:
@@ -125,7 +126,7 @@ data &outds;
output;
end;
if OPERATOR_NM not in
('=','>','<','<=','>=','BETWEEN','IN','NOT IN','NE','CONTAINS')
('=','>','<','<=','>=','BETWEEN','IN','NOT IN','NE','CONTAINS','GE','LE')
then do;
REASON_CD='Invalid OPERATOR_NM: '!!cats(OPERATOR_NM);
putlog REASON_CD= OPERATOR_NM=;

View File

@@ -8,8 +8,13 @@
https://sasapps.io)). This macro is also used in [Data Controller for SAS](
https://datacontroller.io).
A more recent feature of this macro is the ability to support filter queries
on Format Catalogs. This is achieved by adding a `-FC` suffix to the `libds`
parameter - where the "ds" in this case is the catalog name.
@param [in] libds= The target dataset to be filtered (lib should be assigned)
@param [in] libds= The target dataset to be filtered (lib should be assigned).
If filtering a format catalog, add the following suffix: `-FC`.
@param [in] queryds= (WORK.FILTERQUERY) The temporary input query dataset to
be validated. Has the following format:
|GROUP_LOGIC:$3|SUBGROUP_LOGIC:$3|SUBGROUP_ID:8.|VARIABLE_NM:$32|OPERATOR_NM:$10|RAW_VALUE:$32767|
@@ -45,6 +50,7 @@
<h4> SAS Macros </h4>
@li mddl_sas_cntlout.sas
@li mf_getuniquename.sas
@li mf_getvalue.sas
@li mf_islibds.sas
@@ -78,7 +84,10 @@
%put &sysmacroname entry vars:;
%put _local_;
%local ds1 ds2 ds3 ds4 filter_hash;
%local ds0 ds1 ds2 ds3 ds4 filter_hash orig_libds;
%let libds=%upcase(&libds);
%let orig_libds=&libds;
%mp_abort(iftrue= (&syscc ne 0)
,mac=mp_filterstore
,msg=%str(syscc=&syscc on macro entry)
@@ -96,12 +105,27 @@
,msg=%str(Invalid lock_table value: &lock_table)
)
/* validate query */
/**
* validate query
* use format catalog export, if a format
*/
%if "%substr(&libds,%length(&libds)-2,3)"="-FC" %then %do;
%let libds=%scan(&libds,1,-); /* chop off -FC extension */
%let ds0=%mf_getuniquename(prefix=fmtds_);
%let libds=&ds0;
/*
There is no need to export the entire format catalog here - the validations
are done against the data model, not the data values. So we can simply
hardcode the structure based on the cntlout dataset.
*/
%mddl_sas_cntlout(libds=&ds0)
%end;
%mp_filtercheck(&queryds,targetds=&libds,abort=YES)
/* hash the result */
%let ds1=%mf_getuniquename(prefix=hashds);
%mp_hashdataset(&queryds,outds=&ds1,salt=&libds)
%mp_hashdataset(&queryds,outds=&ds1,salt=&orig_libds)
%let filter_hash=%upcase(%mf_getvalue(&ds1,hashkey));
%if &mdebug=1 %then %do;
data _null_;
@@ -132,7 +156,7 @@ run;
%let ds3=%mf_getuniquename(prefix=filtersum);
data work.&ds3;
if 0 then set &filter_summary;
filter_table=symget('libds');
filter_table="&orig_libds";
filter_hash="&filter_hash";
PROCESSED_DTTM=%sysfunc(datetime());
output;

View File

@@ -96,8 +96,7 @@ filename &fref1 clear;
run;
%mp_abort(
mac=&sysmacroname,
msg=%str(Filter validation issues. ERR=%superq(SYSERRORTEXT)
, WARN=%superq(SYSWARNINGTEXT) )
msg=%str(Filter validation issues.)
)
%end;
%let syscc=1008;

View File

@@ -40,6 +40,7 @@ https://support.sas.com/documentation/cdl/en/proc/61895/HTML/default/viewer.htm#
|`WHICHPATH `|`**OTHER** `|`**OTHER** `|`big fat problem if not path1 `|`1 `|`40 `|`28 `|`28 `|`1E-12 `|` `|`0 `|` `|`0 `|`N `|`N `|`N `|`O `|` `|` `|` `|` `|
<h4> SAS Macros </h4>
@li mddl_sas_cntlout.sas
@li mf_dedup.sas
@li mf_getfmtlist.sas
@li mf_getfmtname.sas
@@ -94,30 +95,7 @@ create table &outsummary as
%if "&outdetail" ne "0" %then %do;
/* ensure base table always exists */
proc sql;
create table &outdetail(
FMTNAME char(32) label='Format name'
,START char(16) label='Starting value for format'
,END char(16) label='Ending value for format'
,LABEL char(256) label='Format value label'
,MIN num length=3 label='Minimum length'
,MAX num length=3 label='Maximum length'
,DEFAULT num length=3 label='Default length'
,LENGTH num length=3 label='Format length'
,FUZZ num label='Fuzz value'
,PREFIX char(2) label='Prefix characters'
,MULT num label='Multiplier'
,FILL char(1) label='Fill character'
,NOEDIT num length=3 label='Is picture string noedit?'
,TYPE char(1) label='Type of format'
,SEXCL char(1) label='Start exclusion'
,EEXCL char(1) label='End exclusion'
,HLO char(13) label='Additional information'
,DECSEP char(1) label='Decimal separator'
,DIG3SEP char(1) label='Three-digit separator'
,DATATYPE char(8) label='Date/time/datetime?'
,LANGUAGE char(8) label='Language for date strings'
);
%mddl_sas_cntlout(libds=&outdetail)
/* grab the location of each format */
%let fmtcnt=0;
data _null_;
@@ -134,7 +112,11 @@ create table &outsummary as
proc format library=&&fmtloc&i CNTLOUT=&tempds;
select &&fmtname&i;
run;
proc append base=&outdetail data=&tempds;
data &tempds;
if 0 then set &outdetail;
set &tempds;
run;
proc append base=&outdetail data=&tempds ;
run;
%end;
%end;

View File

@@ -51,9 +51,10 @@ https://documentation.sas.com/doc/en/pgmsascdc/9.4_3.5/mcrolref/n1j5tcc0n2xczyn1
this dataset.
It will then run an abort cancel FILE to stop the include running, and pass
the dataset back.
NOTE - it is NOT possible to read this dataset as part of _this_ macro -
when running abort cancel FILE, ALL macros are closed, so instead it is
necessary to invoke "%mp_abort(mode=INCLUDE)" OUTSIDE of any macro wrappers.
IMPORTANT NOTE - it is NOT possible to read this dataset as part of _this_
macro! When running abort cancel FILE, ALL macros are closed, so instead it
is necessary to invoke "%mp_abort(mode=INCLUDE)" OUTSIDE of macro wrappers.
@version 9.4

View File

@@ -131,7 +131,7 @@
%put &sysmacroname: Switching to DATASTEP engine;
%goto datastep;
%end;
data &tempds /view=&tempds;set &ds;
data &tempds;set &ds;
%if &fmt=N %then format _numeric_ best32.;;
/* PRETTY is necessary to avoid line truncation in large files */
proc json out=&jref pretty
@@ -182,7 +182,7 @@
%end;
other = [best.];
data &tempds/view=&tempds;
data &tempds;
attrib _all_ label='';
%do i=1 %to &numcols;
%if &&typelong&i=char or &fmt=Y %then %do;
@@ -244,8 +244,7 @@
%end;
proc sql;
drop view &tempds;
drop table &colinfo;
drop table &colinfo, &tempds;
%if &showmeta=YES %then %do;
data _null_; file &jref encoding='utf-8' mod;

298
base/mp_loadformat.sas Normal file
View File

@@ -0,0 +1,298 @@
/**
@file
@brief Loads a format catalog from a staging dataset
@details When loading staged data, it is common to receive only the records
that have actually changed. However, when loading a format catalog, if
records are missing they are presumed to be no longer required.
This macro will augment a staging dataset with other records from the same
format, to prevent loss of data - UNLESS the input dataset contains a marker
column, specifying that a particular row needs to be deleted (`delete_col=`).
This macro can also be used to identify which records would be (or were)
considered new, modified or deleted (`loadtarget=`) by creating the following
tables:
@li work.outds_add
@li work.outds_del
@li work.outds_mod
For example usage, see mp_loadformat.test.sas
@param [in] libcat The format catalog to be loaded
@param [in] libds The staging table to load
@param [in] loadtarget= (NO) Set to YES to actually load the target catalog
@param [in] delete_col= (_____DELETE__THIS__RECORD_____) The column used to
mark a record for deletion. Values should be "Yes" or "No".
@param [out] auditlibds= (0) For change tracking, set to the libds of an audit
table as defined in mddl_dc_difftable.sas
@param [in] locklibds= (0) For multi-user (parallel) situations, set to the
libds of the DC lock table as defined in the mddl_dc_locktable.sas macro.
@param [out] outds_add= (0) Set a libds here to see the new records added
@param [out] outds_del= (0) Set a libds here to see the records deleted
@param [out] outds_mod= (0) Set a libds here to see the modified records
@param [in] mdebug= (0) Set to 1 to enable DEBUG messages and preserve outputs
<h4> SAS Macros </h4>
@li mddl_sas_cntlout.sas
@li mf_getuniquename.sas
@li mf_nobs.sas
@li mp_abort.sas
@li mp_cntlout.sas
@li mp_lockanytable.sas
<h4> Related Macros </h4>
@li mddl_dc_difftable.sas
@li mddl_dc_locktable.sas
@li mp_loadformat.test.sas
@li mp_lockanytable.sas
@li mp_storediffs.sas
@li mp_stackdiffs.sas
@version 9.2
@author Allan Bowe
**/
%macro mp_loadformat(libcat,libds
,loadtarget=NO
,auditlibds=0
,locklibds=0
,delete_col=_____DELETE__THIS__RECORD_____
,outds_add=0
,outds_del=0
,outds_mod=0
,mdebug=0
);
/* set up local macro variables and temporary tables (with a prefix) */
%local err msg prefix dslist i var fmtlist ibufsize;
%let dslist=base_fmts template inlibds ds1 stagedata storediffs;
%if &outds_add=0 %then %let dslist=&dslist outds_add;
%if &outds_del=0 %then %let dslist=&dslist outds_del;
%if &outds_mod=0 %then %let dslist=&dslist outds_mod;
%let prefix=%substr(%mf_getuniquename(),1,21);
%do i=1 %to %sysfunc(countw(&dslist));
%let var=%scan(&dslist,&i);
%local &var;
%let &var=%upcase(&prefix._&var);
%end;
/*
format values can be up to 32767 wide. SQL joins on such a wide column can
cause buffer issues. Update ibufsize and reset at the end.
*/
%let ibufsize=%sysfunc(getoption(ibufsize));
options ibufsize=32767 ;
/* in DC, format catalogs maybe specified in the libds with a -FC extension */
%let libcat=%scan(&libcat,1,-);
/* perform input validations */
%let err=0;
%let msg=0;
data _null_;
if _n_=1 then putlog "&sysmacroname entry vars:";
set sashelp.vmacro;
where scope="&sysmacroname";
value=upcase(value);
if &mdebug=0 then put name '=' value;
if name=:'LOAD' and value not in ('YES','NO') then do;
call symputx('msg',"invalid value for "!!name!!":"!!value);
call symputx('err',1);
stop;
end;
else if name='LIBCAT' then do;
if exist(value,'CATALOG') le 0 then do;
call symputx('msg',"Unable to open catalog: "!!value);
call symputx('err',1);
stop;
end;
end;
else if name='LIBDS' then do;
if exist(value) le 0 then do;
call symputx('msg',"Unable to open staging table: "!!value);
call symputx('err',1);
stop;
end;
end;
else if (name=:'OUTDS' or name in ('DELETE_COL','LOCKLIBDS','AUDITLIBDS'))
and missing(value) then do;
call symputx('msg',"missing value in var: "!!name);
call symputx('err',1);
stop;
end;
run;
%mp_abort(
iftrue=(&err ne 0)
,mac=&sysmacroname
,msg=%str(&msg)
)
/**
* First, extract only relevant formats from the catalog
*/
proc sql noprint;
select distinct fmtname into: fmtlist separated by ' ' from &libds;
%mp_cntlout(libcat=&libcat,fmtlist=&fmtlist,cntlout=&base_fmts)
/**
* Ensure input table and base_formats have consistent lengths and types
*/
%mddl_sas_cntlout(libds=&template)
data &inlibds;
if 0 then set &template;
set &libds;
if missing(type) then do;
if substr(fmtname,1,1)='$' then type='C';
else type='N';
end;
if type='N' then do;
start=cats(start);
end=cats(end);
end;
run;
/**
* Identify new records
*/
proc sql;
create table &outds_add(drop=&delete_col) as
select a.*
from &inlibds a
left join &base_fmts b
on a.fmtname=b.fmtname
and a.start=b.start
where b.fmtname is null
and upcase(a.&delete_col) ne "YES"
order by fmtname, start;;
/**
* Identify deleted records
*/
create table &outds_del(drop=&delete_col) as
select a.*
from &inlibds a
inner join &base_fmts b
on a.fmtname=b.fmtname
and a.start=b.start
where upcase(a.&delete_col)="YES"
order by fmtname, start;
/**
* Identify modified records
*/
create table &outds_mod (drop=&delete_col) as
select a.*
from &inlibds a
inner join &base_fmts b
on a.fmtname=b.fmtname
and a.start=b.start
where upcase(a.&delete_col) ne "YES"
order by fmtname, start;
options ibufsize=&ibufsize;
%mp_abort(
iftrue=(&syscc ne 0)
,mac=&sysmacroname
,msg=%str(SYSCC=&syscc prior to load prep)
)
%if &loadtarget=YES %then %do;
data &ds1;
merge &base_fmts(in=base)
&outds_mod(in=mod)
&outds_add(in=add)
&outds_del(in=del);
if not del and not mod;
by fmtname start;
run;
data &stagedata;
set &ds1 &outds_mod;
run;
proc sort;
by fmtname start;
run;
%end;
/* mp abort needs to run outside of conditional blocks */
%mp_abort(
iftrue=(&syscc ne 0)
,mac=&sysmacroname
,msg=%str(SYSCC=&syscc prior to actual load)
)
%if &loadtarget=YES %then %do;
%if %mf_nobs(&stagedata)=0 %then %do;
%put There are no changes to load in &libcat!;
%return;
%end;
%if &locklibds ne 0 %then %do;
/* prevent parallel updates */
%mp_lockanytable(LOCK
,lib=%scan(&libcat,1,.)
,ds=%scan(&libcat,2,.)-FC
,ref=MP_LOADFORMAT commencing format load
,ctl_ds=&locklibds
)
%end;
/* do the actual load */
proc format lib=&libcat cntlin=&stagedata;
run;
%if &locklibds ne 0 %then %do;
/* unlock the table */
%mp_lockanytable(UNLOCK
,lib=%scan(&libcat,1,.)
,ds=%scan(&libcat,2,.)-FC
,ref=MP_LOADFORMAT completed format load
,ctl_ds=&locklibds
)
%end;
/* track the changes */
%if &auditlibds ne 0 %then %do;
%if &locklibds ne 0 %then %do;
%mp_lockanytable(LOCK
,lib=%scan(&auditlibds,1,.)
,ds=%scan(&auditlibds,2,.)
,ref=MP_LOADFORMAT commencing audit table load
,ctl_ds=&locklibds
)
%end;
%mp_storediffs(&libcat-FC
,&inlibds
,FMTNAME START
,delds=&outds_del
,modds=&outds_mod
,appds=&outds_add
,outds=&storediffs
,mdebug=&mdebug
)
%if &locklibds ne 0 %then %do;
%mp_lockanytable(UNLOCK
,lib=%scan(&auditlibds,1,.)
,ds=%scan(&auditlibds,2,.)
,ref=MP_LOADFORMAT commencing audit table load
,ctl_ds=&locklibds
)
%end;
%end;
%end;
%mp_abort(
iftrue=(&syscc ne 0)
,mac=&sysmacroname
,msg=%str(SYSCC=&syscc after load)
)
%if &mdebug=0 %then %do;
proc datasets lib=work;
delete &prefix:;
run;
%put &sysmacroname exit vars:;
%put _local_;
%end;
%mend mp_loadformat;

View File

@@ -1,7 +1,8 @@
/**
@file
@brief Mechanism for locking tables to prevent parallel modifications
@details Uses a control table to enable ANY table to be locked for updates.
@details Uses a control table to enable ANY table to be locked for updates
(not just SAS datasets).
Only useful if every update uses the macro! Used heavily within
[Data Controller for SAS](https://datacontroller.io).
@@ -15,7 +16,7 @@
length is 200 characters.
@param [out] ctl_ds= (0) The control table which controls the actual locking.
Should already be assigned and available. The definition is available by
running mp_coretable.sas as follows: `%mp_coretable(LOCKTABLE)`.
running the mddl_dc_locktable.sas macro.
@param [in] loops= (25) Number of times to check for a lock.
@param [in] loop_secs= (1) Seconds to wait between each lock attempt
@@ -48,7 +49,7 @@ data _null_;
put name '=' value;
run;
%mp_abort(iftrue= (&ds=0 and &action ne MAKETABLE)
%mp_abort(iftrue= ("&ds"="0" and &action ne MAKETABLE)
,mac=&sysmacroname
,msg=%str(dataset was not provided)
)
@@ -85,7 +86,7 @@ run;
/* do not proceed if no observations can be processed */
%mp_abort(iftrue= (%sysfunc(getoption(OBS))=0)
,mac=&sysmacroname
,msg=%str(options obs = 0. syserrortext=&syserrortext)
,msg=%str(cannot continue when options obs = 0)
)
%if &ACTION=LOCK %then %do;

View File

@@ -37,7 +37,7 @@ run;
,mac=checklock.sas
,msg=Aborting with syscc=&syscc on entry.
)
%mp_abort(iftrue= (&libds=0)
%mp_abort(iftrue= ("&libds"="0")
,mac=&sysmacroname
,msg=%str(libds not provided)
)
@@ -46,6 +46,12 @@ run;
%let lib=%upcase(%scan(&libds,1,.));
%let ds=%upcase(%scan(&libds,2,.));
/* in DC, format catalogs are passed with a -FC suffix. No saslock here! */
%if %scan(&libds,2,-)=FC %then %do;
%put &sysmacroname: Format Catalog detected, no lockfile applied to &libds;
%return;
%end;
/* do not proceed if no observations can be processed */
%let msg=options obs = 0. syserrortext=%superq(syserrortext);
%mp_abort(iftrue= (%sysfunc(getoption(OBS))=0)

58
base/mp_md5.sas Normal file
View File

@@ -0,0 +1,58 @@
/**
@file
@brief Generates an md5 expression for hashing a set of variables
@details This is the same algorithm used to hash records in
[Data Controller for SAS](https://datacontroller.io) (free for up
to 5 users).
It is not designed to be efficient - it is designed to be effective,
given the range of edge cases (large floating points, special missing
numerics, thousands of columns, very wide columns).
It can be used only in data step, eg as follows:
data _null_;
set sashelp.class;
hashvar=%mp_md5(cvars=name sex, nvars=age height weight);
put hashvar=;
run;
Unfortunately it will not run in SQL - it fails with the following message:
> The width value for HEX is out of bounds. It should be between 1 and 16
The macro will also cause errors if the data contains (non-special) missings
and the (undocumented) `options dsoptions=nonote2err;` is in effect.
This can be avoided in two ways:
@li Global option: `options dsoptions=nonote2err;`
@li Data step option: `data YOURLIB.YOURDATASET /nonote2err;`
@param cvars= Space seperated list of character variables
@param nvars= Space seperated list of numeric variables
<h4> Related Programs </h4>
@li mp_init.sas
@version 9.2
@author Allan Bowe
**/
%macro mp_md5(cvars=,nvars=);
%local i var sep;
put(md5(
%do i=1 %to %sysfunc(countw(&cvars));
%let var=%scan(&cvars,&i,%str( ));
&sep put(md5(trim(&var)),$hex32.)
%let sep=!!;
%end;
%do i=1 %to %sysfunc(countw(&nvars));
%let var=%scan(&nvars,&i,%str( ));
/* multiply by 1 to strip precision errors (eg 0 != 0) */
/* but ONLY if not missing, else will lose any special missing values */
&sep put(md5(trim(put(ifn(missing(&var),&var,&var*1),binary64.))),$hex32.)
%let sep=!!;
%end;
),hex32.)
%mend mp_md5;

View File

@@ -90,7 +90,7 @@
%else %let dbg=*;
/* set up unique and temporary vars */
%local ds1 ds2 ds3 ds4 hashkey inds_auto inds_keep dslist;
%local ds1 ds2 ds3 ds4 hashkey inds_auto inds_keep dslist vlist;
%let ds1=%upcase(work.%mf_getuniquename(prefix=mpsd_ds1));
%let ds2=%upcase(work.%mf_getuniquename(prefix=mpsd_ds2));
%let ds3=%upcase(work.%mf_getuniquename(prefix=mpsd_ds3));
@@ -144,12 +144,21 @@ proc transpose data=&ds1
by &inds_keep &hashkey;
var _character_;
run;
%if %index(&libds,-)>0 and %scan(&libds,2,-)=FC %then %do;
/* this is a format catalog - cannot query cols directly */
%let vlist="FMTNAME","START","END","LABEL","MIN","MAX","DEFAULT","LENGTH"
,"FUZZ","PREFIX","MULT","FILL","NOEDIT","TYPE","SEXCL","EEXCL","HLO"
,"DECSEP","DIG3SEP","DATATYPE","LANGUAGE";
%end;
%else %let vlist=%mf_getvarlist(&libds,dlm=%str(,),quote=DOUBLE);
data &ds4;
length &inds_keep $41 tgtvar_nm $32;
set &ds2 &ds3 indsname=&inds_auto;
tgtvar_nm=upcase(tgtvar_nm);
if tgtvar_nm in (%upcase(%mf_getvarlist(&libds,dlm=%str(,),quote=DOUBLE)));
if tgtvar_nm in (%upcase(&vlist));
if &inds_auto="&ds2" then tgtvar_type='N';
else if &inds_auto="&ds3" then tgtvar_type='C';

View File

@@ -12,17 +12,20 @@
%mp_streamfile(contenttype=csv,inloc=/some/where.txt,outname=myfile.txt)
@param [in] contenttype= (TEXT) Either TEXT, ZIP, CSV, EXCEL
@param [in] inloc= /path/to/file.ext to be sent
@param [in] inref= fileref of file to be sent (if provided, overrides `inloc`)
@param [in] iftrue= (1=1) Provide a condition under which to execute.
@param [out] outname= the name of the file, as downloaded by the browser
@param [out] outref= (_webout) The destination where the file should be
streamed.
<h4> SAS Macros </h4>
@li mf_getplatform.sas
@li mfs_httpheader.sas
@li mp_binarycopy.sas
@param contenttype= Either TEXT, ZIP, CSV, EXCEL (default TEXT)
@param inloc= /path/to/file.ext to be sent
@param inref= fileref of file to be sent (if provided, overrides `inloc`)
@param outname= the name of the file, as downloaded by the browser
@author Allan Bowe
@source https://github.com/sasjs/core
**/
@@ -30,12 +33,16 @@
contenttype=TEXT
,inloc=
,inref=0
,iftrue=%str(1=1)
,outname=
,outref=_webout
)/*/STORE SOURCE*/;
%let contentype=%upcase(&contenttype);
%local platform; %let platform=%mf_getplatform();
%if not(%eval(%unquote(&iftrue))) %then %return;
%let contentype=%upcase(&contenttype);
%let outref=%upcase(&outref);
%local platform; %let platform=%mf_getplatform();
/**
* check engine type to avoid the below err message:
@@ -44,39 +51,101 @@
%local streamweb;
%let streamweb=0;
data _null_;
set sashelp.vextfl(where=(upcase(fileref)="_WEBOUT"));
set sashelp.vextfl(where=(upcase(fileref)="&outref"));
if xengine='STREAM' then call symputx('streamweb',1,'l');
run;
%if &contentype=ZIP %then %do;
%if &platform=SASMETA and &streamweb=1 %then %do;
%if &contentype=CSV %then %do;
%if (&platform=SASMETA and &streamweb=1) %then %do;
data _null_;
rc=stpsrv_header('Content-type','application/zip');
rc=stpsrv_header('Content-type','application/csv');
rc=stpsrv_header('Content-disposition',"attachment; filename=&outname");
run;
%end;
%else %if &platform=SASVIYA %then %do;
filename _webout filesrvc parenturi="&SYS_JES_JOB_URI" name='_webout.zip'
contenttype='application/zip'
filename &outref filesrvc parenturi="&SYS_JES_JOB_URI" name='_webout.txt'
contenttype='application/csv'
contentdisp="attachment; filename=&outname";
%end;
%else %if &platform=SASJS %then %do;
%mfs_httpheader(Content-type,application/csv)
%mfs_httpheader(Content-disposition,%str(attachment; filename=&outname))
%end;
%end;
%else %if &contentype=EXCEL %then %do;
/* suitable for XLS format */
%if &platform=SASMETA and &streamweb=1 %then %do;
%if (&platform=SASMETA and &streamweb=1) %then %do;
data _null_;
rc=stpsrv_header('Content-type','application/vnd.ms-excel');
rc=stpsrv_header('Content-disposition',"attachment; filename=&outname");
run;
%end;
%else %if &platform=SASVIYA %then %do;
filename _webout filesrvc parenturi="&SYS_JES_JOB_URI" name='_webout.xls'
filename &outref filesrvc parenturi="&SYS_JES_JOB_URI" name='_webout.xls'
contenttype='application/vnd.ms-excel'
contentdisp="attachment; filename=&outname";
%end;
%else %if &platform=SASJS %then %do;
%mfs_httpheader(Content-type,application/vnd.ms-excel)
%mfs_httpheader(Content-disposition,%str(attachment; filename=&outname))
%end;
%end;
%else %if &contentype=GIF or &contentype=JPEG or &contentype=PNG %then %do;
%if (&platform=SASMETA and &streamweb=1) %then %do;
data _null_;
rc=stpsrv_header('Content-type',"image/%lowcase(&contenttype)");
run;
%end;
%else %if &platform=SASVIYA %then %do;
filename &outref filesrvc parenturi="&SYS_JES_JOB_URI"
contenttype="image/%lowcase(&contenttype)";
%end;
%else %if &platform=SASJS %then %do;
%mfs_httpheader(Content-type,image/%lowcase(&contenttype))
%end;
%end;
%else %if &contentype=HTML %then %do;
%if &platform=SASVIYA %then %do;
filename &outref filesrvc parenturi="&SYS_JES_JOB_URI" name="_webout.json"
contenttype="text/html";
%end;
%else %if &platform=SASJS %then %do;
%mfs_httpheader(Content-type,text/html)
%end;
%end;
%else %if &contentype=TEXT %then %do;
%if (&platform=SASMETA and &streamweb=1) %then %do;
data _null_;
rc=stpsrv_header('Content-type','application/text');
rc=stpsrv_header('Content-disposition',"attachment; filename=&outname");
run;
%end;
%else %if &platform=SASVIYA %then %do;
filename &outref filesrvc parenturi="&SYS_JES_JOB_URI" name='_webout.txt'
contenttype='application/text'
contentdisp="attachment; filename=&outname";
%end;
%else %if &platform=SASJS %then %do;
%mfs_httpheader(Content-type,application/text)
%mfs_httpheader(Content-disposition,%str(attachment; filename=&outname))
%end;
%end;
%else %if &contentype=WOFF or &contentype=WOFF2 or &contentype=TTF %then %do;
%if (&platform=SASMETA and &streamweb=1) %then %do;
data _null_;
rc=stpsrv_header('Content-type',"font/%lowcase(&contenttype)");
run;
%end;
%else %if &platform=SASVIYA %then %do;
filename &outref filesrvc parenturi="&SYS_JES_JOB_URI"
contenttype="font/%lowcase(&contenttype)";
%end;
%else %if &platform=SASJS %then %do;
%mfs_httpheader(Content-type,font/%lowcase(&contenttype))
%end;
%end;
%else %if &contentype=XLSX %then %do;
%if &platform=SASMETA and &streamweb=1 %then %do;
%if (&platform=SASMETA and &streamweb=1) %then %do;
data _null_;
rc=stpsrv_header('Content-type',
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
@@ -84,54 +153,44 @@ run;
run;
%end;
%else %if &platform=SASVIYA %then %do;
filename _webout filesrvc parenturi="&SYS_JES_JOB_URI" name='_webout.xls'
filename &outref filesrvc parenturi="&SYS_JES_JOB_URI" name='_webout.xls'
contenttype=
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
contentdisp="attachment; filename=&outname";
%end;
%else %if &platform=SASJS %then %do;
%mfs_httpheader(Content-type
,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
)
%mfs_httpheader(Content-disposition,%str(attachment; filename=&outname))
%end;
%end;
%else %if &contentype=TEXT %then %do;
%if &platform=SASMETA and &streamweb=1 %then %do;
%else %if &contentype=ZIP %then %do;
%if (&platform=SASMETA and &streamweb=1) %then %do;
data _null_;
rc=stpsrv_header('Content-type','application/text');
rc=stpsrv_header('Content-type','application/zip');
rc=stpsrv_header('Content-disposition',"attachment; filename=&outname");
run;
%end;
%else %if &platform=SASVIYA %then %do;
filename _webout filesrvc parenturi="&SYS_JES_JOB_URI" name='_webout.txt'
contenttype='application/text'
filename &outref filesrvc parenturi="&SYS_JES_JOB_URI" name='_webout.zip'
contenttype='application/zip'
contentdisp="attachment; filename=&outname";
%end;
%end;
%else %if &contentype=CSV %then %do;
%if &platform=SASMETA and &streamweb=1 %then %do;
data _null_;
rc=stpsrv_header('Content-type','application/csv');
rc=stpsrv_header('Content-disposition',"attachment; filename=&outname");
run;
%end;
%else %if &platform=SASVIYA %then %do;
filename _webout filesrvc parenturi="&SYS_JES_JOB_URI" name='_webout.txt'
contenttype='application/csv'
contentdisp="attachment; filename=&outname";
%end;
%end;
%else %if &contentype=HTML %then %do;
%if &platform=SASVIYA %then %do;
filename _webout filesrvc parenturi="&SYS_JES_JOB_URI" name="_webout.json"
contenttype="text/html";
%else %if &platform=SASJS %then %do;
%mfs_httpheader(Content-type,application/zip)
%mfs_httpheader(Content-disposition,%str(attachment; filename=&outname))
%end;
%end;
%else %do;
%put %str(ERR)OR: Content Type &contenttype NOT SUPPORTED by &sysmacroname!;
%return;
%end;
%if &inref ne 0 %then %do;
%mp_binarycopy(inref=&inref,outref=_webout)
%mp_binarycopy(inref=&inref,outref=&outref)
%end;
%else %do;
%mp_binarycopy(inloc="&inloc",outref=_webout)
%mp_binarycopy(inloc="&inloc",outref=&outref)
%end;
%mend mp_streamfile;

View File

@@ -4,9 +4,7 @@
@details Loops with a `sleep()` command until a file arrives or the max wait
period expires.
@example
Wait 3 minutes OR for /tmp/flag.txt to appear
Example: Wait 3 minutes OR for /tmp/flag.txt to appear
%mp_wait4file(/tmp/flag.txt , maxwait=60*3)

View File

@@ -84,7 +84,7 @@ options noquotelenmax;
"""
f = open('all.sas', "w") # r / r+ / rb / rb+ / w / wb
f.write(header)
folders=['base','meta','metax','server','viya','lua','fcmp']
folders=['base','ddl','meta','metax','server','viya','lua','fcmp']
for folder in folders:
filenames = [fn for fn in Path('./' + folder).iterdir() if fn.match("*.sas")]
filenames.sort()

34
ddl/mddl_dc_difftable.sas Normal file
View File

@@ -0,0 +1,34 @@
/**
@file
@brief Difftable DDL
@details Used to store changes to tables. Used by mp_storediffs.sas
and mp_stackdiffs.sas
**/
%macro mddl_dc_difftable(libds=WORK.DIFFTABLE);
proc sql;
create table &libds(
load_ref char(36) label='unique load reference',
processed_dttm num format=E8601DT26.6 label='Processed at timestamp',
libref char(8) label='Library Reference (8 chars)',
dsn char(32) label='Dataset Name (32 chars)',
key_hash char(32) label=
'MD5 Hash of primary key values (pipe seperated)',
move_type char(1) label='Either (A)ppended, (D)eleted or (M)odified',
is_pk num label='Is Primary Key Field? (1/0)',
is_diff num label=
'Did value change? (1/0/-1). Always -1 for appends and deletes.',
tgtvar_type char(1) label='Either (C)haracter or (N)umeric',
tgtvar_nm char(32) label='Target variable name (32 chars)',
oldval_num num format=best32. label='Old (numeric) value',
newval_num num format=best32. label='New (numeric) value',
oldval_char char(32765) label='Old (character) value',
newval_char char(32765) label='New (character) value',
constraint pk_mpe_audit
primary key(load_ref,libref,dsn,key_hash,tgtvar_nm)
);
%mend mddl_dc_difftable;

View File

@@ -0,0 +1,27 @@
/**
@file
@brief Filtertable DDL
@details For storing detailed filter values. Used by
mp_filterstore.sas.
**/
%macro mddl_dc_filterdetail(libds=WORK.FILTER_DETAIL);
proc sql;
create table &libds(
filter_hash char(32) not null,
filter_line num not null,
group_logic char(3) not null,
subgroup_logic char(3) not null,
subgroup_id num not null,
variable_nm varchar(32) not null,
operator_nm varchar(12) not null,
raw_value varchar(4000) not null,
processed_dttm num not null format=E8601DT26.6,
constraint pk_mpe_filteranytable
primary key(filter_hash,filter_line)
);
%mend mddl_dc_filterdetail;

View File

@@ -0,0 +1,22 @@
/**
@file
@brief Filtersummary DDL
@details For storing summary filter values. Used by
mp_filterstore.sas.
**/
%macro mddl_dc_filtersummary(libds=WORK.FILTER_SUMMARY);
proc sql;
create table &libds(
filter_rk num not null,
filter_hash char(32) not null,
filter_table char(41) not null,
processed_dttm num not null format=E8601DT26.6,
constraint pk_mpe_filteranytable
primary key(filter_rk)
);
%mend mddl_dc_filtersummary;

25
ddl/mddl_dc_locktable.sas Normal file
View File

@@ -0,0 +1,25 @@
/**
@file
@brief Locktable DDL
@details For "locking" tables prior to multipass loads. Used by
mp_lockanytable.sas
**/
%macro mddl_dc_locktable(libds=WORK.LOCKTABLE);
proc sql;
create table &libds(
lock_lib char(8),
lock_ds char(32),
lock_status_cd char(10) not null,
lock_user_nm char(100) not null ,
lock_ref char(200),
lock_pid char(10),
lock_start_dttm num format=E8601DT26.6,
lock_end_dttm num format=E8601DT26.6,
constraint pk_mp_lockanytable primary key(lock_lib,lock_ds)
);
%mend mddl_dc_locktable;

View File

@@ -0,0 +1,24 @@
/**
@file
@brief Maxkeytable DDL
@details For storing the maximum retained key information. Used
by mp_retainedkey.sas
**/
%macro mddl_dc_maxkeytable(libds=WORK.MAXKEYTABLE);
proc sql;
create table &libds(
keytable varchar(41) label='Base table in libref.dataset format',
keycolumn char(32) format=$32.
label='The Retained key field containing the key values.',
max_key num label=
'Integer representing current max RK or SK value in the KEYTABLE',
processed_dttm num format=E8601DT26.6
label='Datetime this value was last updated',
constraint pk_mpe_maxkeyvalues
primary key(keytable));
%mend mddl_dc_maxkeytable;

43
ddl/mddl_sas_cntlout.sas Normal file
View File

@@ -0,0 +1,43 @@
/**
@file
@brief The CNTLOUT table generated by proc format
@details This table will actually change format depending on the data values,
therefore the max possible lengths are described here to enable consistency
when dealing with format data.
**/
%macro mddl_sas_cntlout(libds=WORK.CNTLOUT);
proc sql;
create table &libds(
FMTNAME char(32) label='Format name'
/*
to accomodate larger START values, mp_loadformat.sas will need the
SQL dependency removed (proc sql needs to accomodate 3 index values in
a 32767 ibufsize limit)
*/
,START char(10000) label='Starting value for format'
,END char(32767) label='Ending value for format'
,LABEL char(32767) label='Format value label'
,MIN num length=3 label='Minimum length'
,MAX num length=3 label='Maximum length'
,DEFAULT num length=3 label='Default length'
,LENGTH num length=3 label='Format length'
,FUZZ num label='Fuzz value'
,PREFIX char(2) label='Prefix characters'
,MULT num label='Multiplier'
,FILL char(1) label='Fill character'
,NOEDIT num length=3 label='Is picture string noedit?'
,TYPE char(1) label='Type of format'
,SEXCL char(1) label='Start exclusion'
,EEXCL char(1) label='End exclusion'
,HLO char(13) label='Additional information'
,DECSEP char(1) label='Decimal separator'
,DIG3SEP char(1) label='Three-digit separator'
,DATATYPE char(8) label='Date/time/datetime?'
,LANGUAGE char(8) label='Language for date strings'
);
%mend mddl_sas_cntlout;

118
fcmp/mcf_getfmttype.sas Normal file
View File

@@ -0,0 +1,118 @@
/**
@file
@brief Returns the type of the format
@details
Returns the type, eg DATE / DATETIME / TIME (based on hard-coded lookup)
else CHAR / NUM.
This macro may be extended in the future to support custom formats - this
would necessitate a call to `dosubl()` for running a proc format with cntlout.
The function itself takes the following (positional) parameters:
| PARAMETER | DESCRIPTION |
|---|---|
|fmtnm| Format name to be tested. Can be with or without the w.d extension.|
Usage:
%mcf_getfmttype(wrap=YES, insert_cmplib=YES)
data _null_;
fmt1=mcf_getfmttype('DATE9.');
fmt2=mcf_getfmttype('DATETIME');
put (fmt:)(=);
run;
%put fmt3=%sysfunc(mcf_getfmttype(TIME9.));
Returns:
> fmt1=DATE fmt2=DATETIME
> fmt3=TIME
@param [out] wrap= (NO) Choose YES to add the proc fcmp wrapper.
@param [out] lib= (work) The output library in which to create the catalog.
@param [out] cat= (sasjs) The output catalog in which to create the package.
@param [out] pkg= (utils) The output package in which to create the function.
Uses a 3 part format: libref.catalog.package
@param [out] insert_cmplib= DEPRECATED - The CMPLIB option is checked and
values inserted only if needed.
<h4> SAS Macros </h4>
@li mcf_init.sas
<h4> Related Programs </h4>
@li mcf_getfmttype.test.sas
@li mp_init.sas
@todo "Custom Format Lookups" To enable site-specific formats, make
use of a set of SASJS_FMTLIST_(DATATYPE) global variables.
**/
%macro mcf_getfmttype(wrap=NO
,insert_cmplib=DEPRECATED
,lib=WORK
,cat=SASJS
,pkg=UTILS
)/*/STORE SOURCE*/;
%local i var cmpval found;
%if %mcf_init(mcf_getfmttype)=1 %then %return;
%if &wrap=YES %then %do;
proc fcmp outlib=&lib..&cat..&pkg;
%end;
function mcf_getfmttype(fmtnm $) $8;
if substr(fmtnm,1,1)='$' then return('CHAR');
else do;
/* extract NAME */
length fmt $32;
fmt=scan(fmtnm,1,'.');
do while (
substr(fmt,length(fmt),1) in ('1','2','3','4','5','6','7','8','9','0')
);
if length(fmt)=1 then fmt='W';
else fmt=substr(fmt,1,length(fmt)-1);
end;
/* apply lookups */
if cats(fmt) in ('DATETIME','B8601DN','B8601DN','B8601DT','B8601DT'
,'B8601DZ','B8601DZ','DATEAMPM','DTDATE','DTMONYY','DTWKDATX','DTYEAR'
,'DTYYQC','E8601DN','E8601DN','E8601DT','E8601DT','E8601DZ','E8601DZ')
then return('DATETIME');
else if fmt in ('DATE','YYMMDD','B8601DA','B8601DA','DAY','DDMMYY'
,'DDMMYYB','DDMMYYC','DDMMYYD','DDMMYYN','DDMMYYP','DDMMYYS','DDMMYYx'
,'DOWNAME','E8601DA','E8601DA','JULDAY','JULIAN','MMDDYY','MMDDYYB'
,'MMDDYYC','MMDDYYD','MMDDYYN','MMDDYYP','MMDDYYS','MMDDYYx','MMYY'
,'MMYYC','MMYYD','MMYYN','MMYYP','MMYYS','MMYYx','MONNAME','MONTH'
,'MONYY','PDJULG','PDJULI','QTR','QTRR','WEEKDATE','WEEKDATX','WEEKDAY'
,'WEEKU','WEEKV','WEEKW','WORDDATE','WORDDATX','YEAR','YYMM','YYMMC'
,'YYMMD','YYMMDDB','YYMMDDC','YYMMDDD','YYMMDDN','YYMMDDP','YYMMDDS'
,'YYMMDDx','YYMMN','YYMMP','YYMMS','YYMMx','YYMON','YYQ','YYQC','YYQD'
,'YYQN','YYQP','YYQR','YYQRC','YYQRD','YYQRN','YYQRP','YYQRS','YYQRx'
,'YYQS','YYQx','YYQZ') then return('DATE');
else if fmt in ('TIME','B8601LZ','B8601LZ','B8601TM','B8601TM','B8601TZ'
,'B8601TZ','E8601LZ','E8601LZ','E8601TM','E8601TM','E8601TZ','E8601TZ'
,'HHMM','HOUR','MMSS','TIMEAMPM','TOD') then return('TIME');
else return('NUM');
end;
endsub;
%if &wrap=YES %then %do;
quit;
%end;
/* insert the CMPLIB if not already there */
%let cmpval=%sysfunc(getoption(cmplib));
%let found=0;
%do i=1 %to %sysfunc(countw(&cmpval,%str( %(%))));
%let var=%scan(&cmpval,&i,%str( %(%)));
%if &var=&lib..&cat %then %let found=1;
%end;
%if &found=0 %then %do;
options insert=(CMPLIB=(&lib..&cat));
%end;
%mend mcf_getfmttype;

View File

@@ -32,12 +32,12 @@
> outa=3 outb=4 outc=5 outd=0
@param [out] wrap= (NO) Choose YES to add the proc fcmp wrapper.
@param [out] insert_cmplib= (NO) Choose YES to insert the package into the
CMPLIB reference.
@param [out] lib= (work) The output library in which to create the catalog.
@param [out] cat= (sasjs) The output catalog in which to create the package.
@param [out] pkg= (utils) The output package in which to create the function.
Uses a 3 part format: libref.catalog.package
@param [out] insert_cmplib= DEPRECATED - The CMPLIB option is checked and
values inserted only if needed.
<h4> SAS Macros </h4>
@li mcf_init.sas
@@ -49,12 +49,12 @@
**/
%macro mcf_length(wrap=NO
,insert_cmplib=NO
,insert_cmplib=DEPRECATED
,lib=WORK
,cat=SASJS
,pkg=UTILS
)/*/STORE SOURCE*/;
%local i var cmpval found;
%if %mcf_init(mcf_length)=1 %then %return;
%if &wrap=YES %then %do;
@@ -76,7 +76,14 @@ endsub;
quit;
%end;
%if &insert_cmplib=YES %then %do;
/* insert the CMPLIB if not already there */
%let cmpval=%sysfunc(getoption(cmplib));
%let found=0;
%do i=1 %to %sysfunc(countw(&cmpval,%str( %(%))));
%let var=%scan(&cmpval,&i,%str( %(%)));
%if &var=&lib..&cat %then %let found=1;
%end;
%if &found=0 %then %do;
options insert=(CMPLIB=(&lib..&cat));
%end;

View File

@@ -47,12 +47,12 @@
@param [out] wrap= (NO) Choose YES to add the proc fcmp wrapper.
@param [out] insert_cmplib= (NO) Choose YES to insert the package into the
CMPLIB reference.
@param [out] lib= (work) The output library in which to create the catalog.
@param [out] cat= (sasjs) The output catalog in which to create the package.
@param [out] pkg= (utils) The output package in which to create the function.
Uses a 3 part format: libref.catalog.package
@param [out] insert_cmplib= DEPRECATED - The CMPLIB option is checked and
values inserted only if needed.
<h4> SAS Macros </h4>
@li mcf_init.sas
@@ -64,12 +64,12 @@
**/
%macro mcf_stpsrv_header(wrap=NO
,insert_cmplib=NO
,insert_cmplib=DEPRECATED
,lib=WORK
,cat=SASJS
,pkg=UTILS
)/*/STORE SOURCE*/;
%local i var cmpval found;
%if %mcf_init(stpsrv_header)=1 %then %return;
%if &wrap=YES %then %do;
@@ -96,7 +96,14 @@ endsub;
quit;
%end;
%if &insert_cmplib=YES %then %do;
/* insert the CMPLIB if not already there */
%let cmpval=%sysfunc(getoption(cmplib));
%let found=0;
%do i=1 %to %sysfunc(countw(&cmpval,%str( %(%))));
%let var=%scan(&cmpval,&i,%str( %(%)));
%if &var=&lib..&cat %then %let found=1;
%end;
%if &found=0 %then %do;
options insert=(CMPLIB=(&lib..&cat));
%end;

View File

@@ -32,12 +32,12 @@
run;
@param [out] wrap= (NO) Choose YES to add the proc fcmp wrapper.
@param [out] insert_cmplib= (NO) Choose YES to insert the package into the
CMPLIB reference.
@param [out] lib= (work) The output library in which to create the catalog.
@param [out] cat= (sasjs) The output catalog in which to create the package.
@param [out] pkg= (utils) The output package in which to create the function.
Uses a 3 part format: libref.catalog.package
@param [out] insert_cmplib= DEPRECATED - The CMPLIB option is checked and
values inserted only if needed.
<h4> SAS Macros </h4>
@li mcf_init.sas
@@ -49,12 +49,12 @@
**/
%macro mcf_string2file(wrap=NO
,insert_cmplib=NO
,insert_cmplib=DEPRECATED
,lib=WORK
,cat=SASJS
,pkg=UTILS
)/*/STORE SOURCE*/;
%local i var cmpval found;
%if %mcf_init(mcf_string2file)=1 %then %return;
%if &wrap=YES %then %do;
@@ -81,7 +81,14 @@ endsub;
quit;
%end;
%if &insert_cmplib=YES %then %do;
/* insert the CMPLIB if not already there */
%let cmpval=%sysfunc(getoption(cmplib));
%let found=0;
%do i=1 %to %sysfunc(countw(&cmpval,%str( %(%))));
%let var=%scan(&cmpval,&i,%str( %(%)));
%if &var=&lib..&cat %then %let found=1;
%end;
%if &found=0 %then %do;
options insert=(CMPLIB=(&lib..&cat));
%end;

View File

@@ -40,6 +40,12 @@
/* now try and assign it */
if libname("&libref",,'meta',cats('liburi="',liburi,'";')) ne 0 then do;
putlog "&libref could not be assigned";
putlog liburi=;
/**
* Fetch the system message for display in the abort modal. This is
* not always helpful though. One example, previously received:
* NOTE: Libref XX refers to the same library metadata as libref XX.
*/
call symputx('msg',sysmsg(),'l');
if "&mabort"='HARD' then call symputx('mp_abort',1,'l');
end;
@@ -61,7 +67,7 @@
%if &mp_abort=1 %then %do;
%mp_abort(iftrue= (&mp_abort=1)
,mac=&sysmacroname
,mac=mm_assignlib.sas
,msg=&msg
)
%return;

View File

@@ -59,7 +59,10 @@
%&mD.put Executing &sysmacroname..sas;
%&mD.put _local_;
%mf_verifymacvars(tree name)
%mp_abort(iftrue= (%mf_verifymacvars(tree name)=0)
,mac=&sysmacroname
,msg=%str(Empty inputs: tree name)
)
/**
* check tree exists

View File

@@ -47,7 +47,10 @@
%&mD.put Executing &sysmacroname..sas;
%&mD.put _local_;
%mf_verifymacvars(tree name)
%mp_abort(iftrue= (%mf_verifymacvars(tree name)=0)
,mac=&sysmacroname
,msg=%str(Empty inputs: tree name)
)
/**
* check tree exists

View File

@@ -12,13 +12,14 @@
The macro is idempotent - if you run it twice, it will only create a folder
once.
usage:
Usage:
%mm_createfolder(path=/some/meta/folder)
@param [in] path= Name of the folder to create.
@param [in] mdebug= set DBG to 1 to disable DEBUG messages
@version 9.4
@author Allan Bowe

View File

@@ -133,12 +133,14 @@ run;
filename &frefin temp;
filename &frefout temp;
%mp_abort(iftrue= (
&engine=BASE & %mf_verifymacvars(libname libref engine servercontext tree)=0
)
,mac=&sysmacroname
,msg=%str(Empty inputs: libname libref engine servercontext tree)
)
%if &engine=BASE %then %do;
%mf_verifymacvars(libname libref engine servercontext tree)
/**
* Check that the ServerContext exists
*/

View File

@@ -12,7 +12,7 @@
This macro is idempotent - if you run it twice, it will only create an STP
once.
usage (type 1 STP):
Usage (type 1 STP):
%mm_createstp(stpname=MyNewSTP
,filename=mySpecialProgram.sas
@@ -31,7 +31,8 @@
putlog (_all_)(=);
run;
usage (type 2 STP):
Usage (type 2 STP):
%mm_createstp(stpname=MyNewType2STP
,filename=mySpecialProgram.sas
,directory=SASEnvironment/SASCode/STPs
@@ -74,8 +75,9 @@
@li mf_verifymacvars.sas
@li mm_getdirectories.sas
@li mm_updatestpsourcecode.sas
@li mp_dropmembers.sas
@li mm_getservercontexts.sas
@li mp_abort.sas
@li mp_dropmembers.sas
<h4> Related Macros </h4>
@li mm_createwebservice.sas
@@ -108,7 +110,12 @@
%&mD.put Executing mm_CreateSTP.sas;
%&mD.put _local_;
%mf_verifymacvars(stpname filename directory tree)
%mp_abort(
iftrue=(%mf_verifymacvars(stpname filename directory tree)=0)
,mac=&sysmacroname
,msg=%str(Empty inputs: stpname filename directory tree)
)
%mp_dropmembers(%scan(&outds,2,.))
/**

View File

@@ -164,7 +164,7 @@ data _null_;
put ' %put &sysmacroname: Switching to DATASTEP engine; ';
put ' %goto datastep; ';
put ' %end; ';
put ' data &tempds /view=&tempds;set &ds; ';
put ' data &tempds;set &ds; ';
put ' %if &fmt=N %then format _numeric_ best32.;; ';
put ' /* PRETTY is necessary to avoid line truncation in large files */ ';
put ' proc json out=&jref pretty ';
@@ -215,7 +215,7 @@ data _null_;
put ' %end; ';
put ' other = [best.]; ';
put ' ';
put ' data &tempds/view=&tempds; ';
put ' data &tempds; ';
put ' attrib _all_ label=''''; ';
put ' %do i=1 %to &numcols; ';
put ' %if &&typelong&i=char or &fmt=Y %then %do; ';
@@ -277,8 +277,7 @@ data _null_;
put ' %end; ';
put ' ';
put ' proc sql; ';
put ' drop view &tempds; ';
put ' drop table &colinfo; ';
put ' drop table &colinfo, &tempds; ';
put ' ';
put ' %if &showmeta=YES %then %do; ';
put ' data _null_; file &jref encoding=''utf-8'' mod; ';

View File

@@ -1,16 +1,21 @@
/**
@file mm_getauthinfo.sas
@brief extracts authentication info
@details usage:
@brief Extracts authentication info for each user in metadata
@details
Usage:
%mm_getauthinfo(outds=auths)
%mm_getauthinfo(outds=auths)
@param outds= the ONE LEVEL work dataset to create
@param [in] mdebug= (0) Set to 1 to enable DEBUG messages and preserve outputs
@param [out] outds= (mm_getauthinfo) The output dataset to create
<h4> SAS Macros </h4>
@li mm_getobjects.sas
@li mf_getuniquefileref.sas
@li mf_getuniquename.sas
@li mm_getdetails.sas
@li mm_getobjects.sas
@version 9.4
@author Allan Bowe
@@ -18,67 +23,69 @@
**/
%macro mm_getauthinfo(outds=mm_getauthinfo
,mdebug=0
)/*/STORE SOURCE*/;
%local prefix fileref;
%let prefix=%substr(%mf_getuniquename(),1,25);
%if %length(&outds)>30 %then %do;
%put %str(ERR)OR: Temp tables are created with the &outds prefix, which
therefore needs to be 30 characters or less;
%return;
%end;
%if %index(&outds,'.')>0 %then %do;
%put %str(ERR)OR: Table &outds should be ONE LEVEL (no library);
%return;
%end;
%mm_getobjects(type=Login,outds=&outds.0)
%mm_getobjects(type=Login,outds=&prefix.0)
%local fileref;
%let fileref=%mf_getuniquefileref();
data _null_;
file &fileref;
set &outds.0 end=last;
set &prefix.0 end=last;
/* run macro */
str=cats('%mm_getdetails(uri=',id,",outattrs=&outds.d",_n_
,",outassocs=&outds.a",_n_,")");
str=cats('%mm_getdetails(uri=',id,",outattrs=&prefix.d",_n_
,",outassocs=&prefix.a",_n_,")");
put str;
/* transpose attributes */
str=cats("proc transpose data=&outds.d",_n_,"(drop=type) out=&outds.da"
str=cats("proc transpose data=&prefix.d",_n_,"(drop=type) out=&prefix.da"
,_n_,"(drop=_name_);var value;id name;run;");
put str;
/* add extra info to attributes */
str=cats("data &outds.da",_n_,";length login_id login_name $256; login_id="
,quote(trim(id)),";set &outds.da",_n_
str=cats("data &prefix.da",_n_,";length login_id login_name $256; login_id="
,quote(trim(id)),";set &prefix.da",_n_
,";login_name=trim(subpad(name,1,256));drop name;run;");
put str;
/* add extra info to associations */
str=cats("data &outds.a",_n_,";length login_id login_name $256; login_id="
str=cats("data &prefix.a",_n_,";length login_id login_name $256; login_id="
,quote(trim(id)),";login_name=",quote(trim(name))
,";set &outds.a",_n_,";run;");
,";set &prefix.a",_n_,";run;");
put str;
if last then do;
/* collate attributes */
str=cats("data &outds._logat; set &outds.da1-&outds.da",_n_,";run;");
str=cats("data &prefix._logat; set &prefix.da1-&prefix.da",_n_,";run;");
put str;
/* collate associations */
str=cats("data &outds._logas; set &outds.a1-&outds.a",_n_,";run;");
str=cats("data &prefix._logas; set &prefix.a1-&prefix.a",_n_,";run;");
put str;
/* tidy up */
str=cats("proc delete data=&outds.da1-&outds.da",_n_,";run;");
str=cats("proc delete data=&prefix.da1-&prefix.da",_n_,";run;");
put str;
str=cats("proc delete data=&outds.d1-&outds.d",_n_,";run;");
str=cats("proc delete data=&prefix.d1-&prefix.d",_n_,";run;");
put str;
str=cats("proc delete data=&outds.a1-&outds.a",_n_,";run;");
str=cats("proc delete data=&prefix.a1-&prefix.a",_n_,";run;");
put str;
end;
run;
%if &mdebug=1 %then %do;
data _null_;
infile &fileref;
if _n_=1 then putlog // "Now executing the following code:" //;
input; putlog _infile_;
run;
%end;
%inc &fileref;
filename &fileref clear;
/* get libraries */
proc sort data=&outds._logas(where=(assoc='Libraries')) out=&outds._temp;
proc sort data=&prefix._logas(where=(assoc='Libraries')) out=&prefix._temp;
by login_id;
data &outds._temp;
set &outds._temp;
data &prefix._temp;
set &prefix._temp;
by login_id;
length library_list $32767;
retain library_list;
@@ -86,31 +93,27 @@ data &outds._temp;
else library_list=catx(' !! ',library_list,name);
proc sql;
/* get auth domain */
create table &outds._dom as
create table &prefix._dom as
select login_id,name as domain
from &outds._logas
from &prefix._logas
where assoc='Domain';
create unique index login_id on &outds._dom(login_id);
create unique index login_id on &prefix._dom(login_id);
/* join it all together */
create table &outds._logins as
create table &outds as
select a.*
,c.domain
,b.library_list
from &outds._logat (drop=ishidden lockedby usageversion publictype) a
left join &outds._temp b
from &prefix._logat (drop=ishidden lockedby usageversion publictype) a
left join &prefix._temp b
on a.login_id=b.login_id
left join &outds._dom c
left join &prefix._dom c
on a.login_id=c.login_id;
drop table &outds._temp;
drop table &outds._logat;
drop table &outds._logas;
data _null_;
infile &fileref;
if _n_=1 then putlog // "Now executing the following code:" //;
input; putlog _infile_;
run;
%if &mdebug=0 %then %do;
proc datasets lib=work;
delete &prefix:;
run;
%end;
filename &fileref clear;
%mend mm_getauthinfo;

444
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -33,6 +33,7 @@
"prepare": "git rev-parse --git-dir && git config core.hooksPath ./.git-hooks || true"
},
"devDependencies": {
"@sasjs/cli": "3.6.0"
"@sasjs/cli": "3.6.0",
"@sasjs/core": "4.4.7"
}
}

View File

@@ -2,13 +2,15 @@
"$schema": "https://raw.githubusercontent.com/sasjs/utils/main/src/types/sasjsconfig-schema.json",
"macroFolders": [
"base",
"ddl",
"fcmp",
"meta",
"metax",
"server",
"viya",
"lua",
"tests/crossplatform"
"tests/crossplatform",
"tests/ddl"
],
"docConfig": {
"displayMacroCore": false,
@@ -59,6 +61,7 @@
"tests/sas9only"
],
"programFolders": [],
"binaryFolders": [],
"deployConfig": {
"deployServicePack": true,
"deployScripts": []
@@ -70,17 +73,24 @@
"name": "server",
"serverUrl": "https://sas.analytium.co.uk:5000",
"serverType": "SASJS",
"appLoc": "/Shared Data/temp/macrocore",
"httpsAgentOptions": {
"allowInsecureRequests": false
},
"appLoc": "/sasjs/core",
"macroFolders": [
"tests/serveronly"
],
"programFolders": [],
"binaryFolders": [],
"deployConfig": {
"deployServicePack": true
"deployServicePack": true,
"deployScripts": []
}
},
{
"name": "docsonly",
"serverType": "SAS9",
"appLoc": "dummy",
"macroFolders": [
"tests/sas9only",
"tests/viyaonly"

52
server/mfs_httpheader.sas Normal file
View File

@@ -0,0 +1,52 @@
/**
@file
@brief Sets the http headers in the SASjs/server response
@details For GET requests, SASjs server will use the file generated by this
macro for setting the appropriate http headers in the response.
It works by writing a file to the session directory, that is then ingested by
the server.
The location of this file is driven by the global variable
`sasjs_stpsrv_header_loc` which is made available in the autoexec.
Usage:
%mfs_httpheader(Content-type,application/csv)
@param [in] header_name Name of the http header to set
@param [in] header_value Value of the http header to set
<h4> Related Macros </h4>
@li mcf_stpsrv_header.sas
@version 9.3
@author Allan Bowe
**/
%macro mfs_httpheader(header_name
,header_value
)/*/STORE SOURCE*/;
%local fref fid i;
%if %sysfunc(filename(fref,&sasjs_stpsrv_header_loc)) ne 0 %then %do;
%put &=fref &=sasjs_stpsrv_header_loc;
%put %str(ERR)OR: %sysfunc(sysmsg());
%return;
%end;
%let fid=%sysfunc(fopen(&fref,A));
%if &fid=0 %then %do;
%put %str(ERR)OR: %sysfunc(sysmsg());
%return;
%end;
%let rc=%sysfunc(fput(&fid,%str(&header_name): %str(&header_value)));
%let rc=%sysfunc(fwrite(&fid));
%let rc=%sysfunc(fclose(&fid));
%let rc=%sysfunc(filename(&fref));
%mend mfs_httpheader;

89
server/ms_createfile.sas Normal file
View File

@@ -0,0 +1,89 @@
/**
@file
@brief Creates a file on SASjs Drive
@details Creates a file on SASjs Drive. To use the file as a Stored Program,
it must have a ".sas" extension.
Example:
filename stpcode temp;
data _null_;
file stpcode;
put '%put hello world;';
run;
%ms_createfile(/some/stored/program.sas, inref=stpcode)
@param [in] driveloc The full path to the file in SASjs Drive
@param [in] inref= (0) The fileref containing the file to create.
@param [in] mdebug= (0) Set to 1 to enable DEBUG messages
<h4> SAS Macros </h4>
@li mf_getuniquefileref.sas
@li mf_getuniquename.sas
@li mp_abort.sas
**/
%macro ms_createfile(driveloc
,inref=0
,mdebug=0
);
%local fname0 fname1 boundary fname statcd msg;
%let fname0=%mf_getuniquefileref();
%let fname1=%mf_getuniquefileref();
%let boundary=%mf_getuniquename();
data _null_;
file &fname0 termstr=crlf;
infile &inref end=eof;
if _n_ = 1 then do;
put "--&boundary.";
put 'Content-Disposition: form-data; name="filePath"';
put ;
put "&driveloc";
put "--&boundary";
put 'Content-Disposition: form-data; name="file"; filename="ignore.sas"';
put "Content-Type: text/plain";
put ;
end;
input;
put _infile_; /* add the actual file to be sent */
if eof then do;
put ;
put "--&boundary--";
end;
run;
%if &mdebug=1 %then %do;
data _null_;
infile &fname0;
input;
put _infile_;
run;
%end;
proc http method='POST' in=&fname0 out=&fname1
url="&_sasjs_apiserverurl/SASjsApi/drive/file";
headers "Content-Type"="multipart/form-data; boundary=&boundary";
%if &mdebug=1 %then %do;
debug level=1;
%end;
run;
%let statcd=0;
data _null_;
infile &fname1;
input;
putlog _infile_;
if _infile_='{"status":"success"}' then call symputx('statcd',1,'l');
else call symputx('msg',_infile_,'l');
run;
%mp_abort(
iftrue=(&statcd=0)
,mac=ms_createfile.sas
,msg=%superq(msg)
)
%mend ms_createfile;

77
server/ms_runstp.sas Normal file
View File

@@ -0,0 +1,77 @@
/**
@file
@brief Executes a SASjs Server Stored Program
@details Runs a Stored Program (using POST method) and extracts the webout and
log from the response JSON.
Example:
%ms_runstp(/some/stored/program
,debug=131
,outref=weboot
)
@param [in] pgm The full path to the Stored Program in SASjs Drive (_program
parameter)
@param [in] debug= (131) The value to supply to the _debug URL parameter
@param [in] mdebug= (0) Set to 1 to enable DEBUG messages
@param [out] outref= (outweb) The output fileref to contain the response JSON
(will be created using temp engine)
<h4> SAS Macros </h4>
@li mf_getuniquefileref.sas
@li mp_abort.sas
**/
%macro ms_runstp(pgm
,debug=131
,outref=outweb
,mdebug=0
);
%local dbg fname1;
%if &mdebug=1 %then %do;
%put &sysmacroname entry vars:;
%put _local_;
%end;
%else %let dbg=*;
%let fname1=%mf_getuniquefileref();
%mp_abort(iftrue=("&pgm"="")
,mac=&sysmacroname
,msg=%str(Program not provided)
)
data _null_;
file &fname1;
infile "&_sasjs_tokenfile";
input;
put 'Authorization: Bearer' _infile_;
run;
filename &outref temp;
/* prepare request*/
proc http method='POST' headerin=&fname1 out=&outref
url="&_sasjs_apiserverurl.&_sasjs_apipath?_program=&pgm%str(&)_debug=131";
run;
%if (&SYS_PROCHTTP_STATUS_CODE ne 200 and &SYS_PROCHTTP_STATUS_CODE ne 201)
or &mdebug=1 %then %do;
data _null_;infile &outref;input;putlog _infile_;run;
%end;
%mp_abort(
iftrue=(&SYS_PROCHTTP_STATUS_CODE ne 200 and &SYS_PROCHTTP_STATUS_CODE ne 201)
,mac=&sysmacroname
,msg=%str(&SYS_PROCHTTP_STATUS_CODE &SYS_PROCHTTP_STATUS_PHRASE)
)
%if &mdebug=1 %then %do;
%put &sysmacroname exit vars:;
%put _local_;
%end;
%else %do;
/* clear refs */
filename &fname1 clear;
%end;
%mend ms_runstp;

View File

@@ -33,8 +33,9 @@
`,"$tablename":{"formats":{"col1":"$CHAR1"},"types":{"COL1":"C"}}`
<h4> SAS Macros </h4>
@li mp_jsonout.sas
@li mf_getuser.sas
@li mp_jsonout.sas
@li mfs_httpheader.sas
<h4> Related Macros </h4>
@li mv_webout.sas
@@ -86,11 +87,11 @@
/* fix encoding */
OPTIONS NOBOMFILE;
/* set the header */
%mfs_httpheader(Content-type,application/json)
/* setup json */
data _null_;file &fref encoding='utf-8' termstr=lf;
%if %str(&_debug) ge 131 %then %do;
put '>>weboutBEGIN<<';
%end;
put '{"SYSDATE" : "' "&SYSDATE" '"';
put ',"SYSTIME" : "' "&SYSTIME" '"';
run;
@@ -170,9 +171,6 @@
memsize=quote(cats(memsize));
put ',"MEMSIZE" : ' memsize;
put "}" @;
%if %str(&_debug) ge 131 %then %do;
put '>>weboutEND<<';
%end;
run;
%end;

View File

@@ -0,0 +1,39 @@
/**
@file
@brief Testing mcf_getfmttype.sas macro
<h4> SAS Macros </h4>
@li mcf_getfmttype.sas
@li mp_assert.sas
@li mp_assertscope.sas
**/
%mp_assertscope(SNAPSHOT)
%mcf_getfmttype(wrap=YES, insert_cmplib=YES)
%mp_assertscope(COMPARE,ignorelist=SASJS_FUNCTIONS)
%mp_assert(
iftrue=(%sysfunc(mcf_getfmttype(DATE9.))=DATE),
desc=Check DATE format
)
%mp_assert(
iftrue=(%sysfunc(mcf_getfmttype($6))=CHAR),
desc=Check CHAR format
)
%mp_assert(
iftrue=(%sysfunc(mcf_getfmttype(8.))=NUM),
desc=Check NUM format
)
%mp_assert(
iftrue=(%sysfunc(mcf_getfmttype(E8601DT))=DATETIME),
desc=Check DATETIME format
)
/* test 2 - compile again test for warnings */
%mcf_getfmttype(wrap=YES, insert_cmplib=YES)
%mp_assert(
iftrue=(&syscc=0),
desc=Check syscc=0 after re-initialisation
)

View File

@@ -46,4 +46,20 @@
),
desc=Checking tests/macros appLoc matches (which has no subfolder),
outds=work.test_results
)
%mp_assert(
iftrue=(
"%mf_getapploc(/some/area/tests/testsetup)"="/some/area"
),
desc=Checking tests/testsetup operation,
outds=work.test_results
)
%mp_assert(
iftrue=(
"%mf_getapploc(/some/area/tests/testteardown)"="/some/area"
),
desc=Checking tests/teardown operation,
outds=work.test_results
)

View File

@@ -0,0 +1,22 @@
/**
@file
@brief Testing mf_verifymacvars macro
<h4> SAS Macros </h4>
@li mf_verifymacvars.sas
@li mp_assert.sas
@li mp_assertscope.sas
**/
%let var1=x;
%let var2=y;
%mp_assertscope(SNAPSHOT)
%mp_assert(
iftrue=(%mf_verifymacvars(var1 var2)=1),
desc=Checking macvars exist,
outds=work.test_results
)
%mp_assertscope(COMPARE)

View File

@@ -0,0 +1,38 @@
/**
@file
@brief Testing mp_cntlout.sas macro
<h4> SAS Macros </h4>
@li mp_cntlout.sas
@li mp_assert.sas
@li mp_assertscope.sas
**/
libname perm (work);
data work.loadfmts;
length fmtname $32;
eexcl='Y';
type='N';
do i=1 to 100;
fmtname=cats('SASJS_',i,'X');
do j=1 to 100;
start=cats(j);
end=cats(j+1);
label= cats('Dummy ',start);
output;
end;
end;
run;
proc format cntlin=work.loadfmts library=perm.testcat;
run;
%mp_assertscope(SNAPSHOT)
%mp_cntlout(libcat=perm.testcat,cntlout=work.cntlout)
%mp_assertscope(COMPARE)
%mp_assert(
iftrue=(%mf_nobs(work.cntlout)=10000),
desc=Checking first hash diff,
outds=work.test_results
)

View File

@@ -93,4 +93,74 @@ run;
iftrue=("&test3b"="PASS"),
desc=Checking data row Test 3,
outds=work.test_results
)
)
/* test 4 - sasjs with compare */
filename example temp;
%mp_ds2csv(sashelp.air,outref=example,headerformat=SASJS)
data _null_; infile example; input;put _infile_; if _n_>5 then stop;run;
data _null_;
infile example;
input;
call symputx('stmnt',_infile_);
stop;
run;
data work.want;
infile example dsd firstobs=2;
input &stmnt;
run;
%mp_assert(
iftrue=(&syscc =0),
desc=Checking syscc prior to compare of sashelp.air,
outds=work.test_results
)
proc compare base=want compare=sashelp.air;
run;
%mp_assert(
iftrue=(&sysinfo le 41),
desc=Checking compare of sashelp.air,
outds=work.test_results
)
/* test 5 - sasjs with time/datetime/date */
filename f2 temp;
data work.test5;
do x=1 to 5;
y=x;
z=x;
end;
format x date9. y datetime19. z time.;
run;
%mp_ds2csv(work.test5,outref=f2,headerformat=SASJS)
data _null_; infile example; input;put _infile_; if _n_>5 then stop;run;
data _null_;
infile f2;
input;
putlog _infile_;
call symputx('stmnt2',_infile_);
stop;
run;
data work.want5;
infile f2 dsd firstobs=2;
input &stmnt2;
putlog _infile_;
run;
%mp_assert(
iftrue=(&syscc=0),
desc=Checking syscc prior to compare of test5,
outds=work.test_results
)
proc compare base=want5 compare=work.test5;
run;
%mp_assert(
iftrue=(&sysinfo le 41),
desc=Checking compare of work.test5,
outds=work.test_results
)

View File

@@ -0,0 +1,112 @@
/**
@file
@brief Testing mp_filterstore macro with a format catalog
<h4> SAS Macros </h4>
@li mp_assert.sas
@li mp_assertdsobs.sas
@li mp_assertscope.sas
@li mp_coretable.sas
@li mp_filterstore.sas
**/
libname permlib (work);
%mp_coretable(LOCKTABLE,libds=permlib.locktable)
%mp_coretable(FILTER_SUMMARY,libds=permlib.filtsum)
%mp_coretable(FILTER_DETAIL,libds=permlib.filtdet)
%mp_coretable(MAXKEYTABLE,libds=permlib.maxkey)
/* valid filter */
data work.inds;
infile datalines4 dsd;
input GROUP_LOGIC:$3. SUBGROUP_LOGIC:$3. SUBGROUP_ID:8. VARIABLE_NM:$32.
OPERATOR_NM:$12. RAW_VALUE:$4000.;
datalines4;
AND,AND,1,Start,>,"'2'"
AND,AND,1,Fmtname,=,"'MORDOR'"
OR,OR,2,Label,IN,"('Dragon1','Dragon2')"
OR,OR,2,End,=,"'6'"
OR,OR,2,Start,GE,"'10'"
;;;;
run;
/* make some formats */
PROC FORMAT library=permlib.testfmts;
picture MyMSdt other='%0Y-%0m-%0dT%0H:%0M:%0S' (datatype=datetime);
RUN;
data work.fmts;
length fmtname $32;
do fmtname='SMAUG','MORDOR','GOLLUM';
do start=1 to 10;
label= cats('Dragon ',start);
output;
end;
end;
run;
proc sort data=work.fmts nodupkey;
by fmtname start;
run;
proc format cntlin=work.fmts library=permlib.testfmts;
run;
proc format library=permlib.testfmts;
invalue indays (default=13) other=42;
run;
%mp_assertscope(SNAPSHOT)
%mp_filterstore(libds=permlib.testfmts-fc,
queryds=work.inds,
filter_summary=permlib.filtsum,
filter_detail=permlib.filtdet,
lock_table=permlib.locktable,
maxkeytable=permlib.maxkey,
outresult=work.result,
outquery=work.query,
mdebug=1
)
%mp_assertscope(COMPARE)
%mp_assert(iftrue=(&syscc=0),
desc=Ensure macro runs without errors,
outds=work.test_results
)
/* ensure only one record created */
%mp_assertdsobs(permlib.filtsum,
desc=Initial query,
test=ATMOST 1,
outds=work.test_results
)
/* check RK is correct */
proc sql noprint;
select max(filter_rk) into: test1 from work.result;
%mp_assert(iftrue=(&test1=1),
desc=Ensure filter rk is correct,
outds=work.test_results
)
/* Test 2 - load same table again and ensure we get the same RK */
%mp_filterstore(libds=permlib.testfmts-fc,
queryds=work.inds,
filter_summary=permlib.filtsum,
filter_detail=permlib.filtdet,
lock_table=permlib.locktable,
maxkeytable=permlib.maxkey,
outresult=work.result,
outquery=work.query,
mdebug=1
)
/* ensure only one record created */
%mp_assertdsobs(permlib.filtsum,
desc=Initial query - same obs,
test=ATMOST 1,
outds=work.test_results
)
/* check RK is correct */
proc sql noprint;
select max(filter_rk) into: test2 from work.result;
%mp_assert(iftrue=(&test2=1),
desc=Ensure filter rk is correct for second run,
outds=work.test_results
)

View File

@@ -0,0 +1,76 @@
/**
@file
@brief Testing mp_loadformat.sas macro
<h4> SAS Macros </h4>
@li mp_loadformat.sas
@li mp_assert.sas
@li mp_assertscope.sas
**/
/* prep format catalog */
libname perm (work);
data work.loadfmts;
length fmtname $32;
eexcl='Y';
type='N';
do i=1 to 100;
fmtname=cats('SASJS_',i,'X');
do j=1 to 100;
start=cats(j);
end=cats(j+1);
label= cats('Dummy ',start);
output;
end;
end;
run;
proc format cntlin=work.loadfmts library=perm.testcat;
run;
/* make some test data */
data work.stagedata;
set work.loadfmts;
type='N';
eexcl='Y';
if _n_<150 then deleteme='Yes';
else if _n_<250 then label='mod'!!cats(_n_);
else if _n_<350 then do;
start=cats(_n_);
end=cats(_n_+1);
label='newval'!!cats(_N_);
end;
else stop;
run;
/* load the above */
%mp_assertscope(SNAPSHOT)
%mp_loadformat(perm.testcat
,work.stagedata
,loadtarget=YES
,auditlibds=0
,locklibds=0
,delete_col=deleteme
,outds_add=add_test1
,outds_del=del_test1
,outds_mod=mod_test1
,mdebug=1
)
%mp_assertscope(COMPARE)
%mp_assert(
iftrue=(%mf_nobs(del_test1)=149),
desc=Test 1 - delete obs,
outds=work.test_results
)
%mp_assert(
iftrue=(%mf_nobs(add_test1)=100),
desc=Test 1 - add obs,
outds=work.test_results
)
%mp_assert(
iftrue=(%mf_nobs(mod_test1)=100),
desc=Test 1 - mod obs,
outds=work.test_results
)

View File

@@ -0,0 +1,41 @@
/**
@file
@brief Testing mp_md5.sas macro
<h4> SAS Macros </h4>
@li mp_md5.sas
@li mp_assert.sas
@li mp_assertscope.sas
**/
%global hash1 hash2 hash3;
%mp_assertscope(SNAPSHOT)
data work.test1 /nonote2err;
c1='';
c2=repeat('x',32767);
c3=' f';
n1=.a;
n2=.;
n3=1.0000000001;
hash=%mp_md5(cvars=c1 c2 c3,nvars=n1 n2 n3);
call symputx('hash1',hash);
n1=.b;
hash=%mp_md5(cvars=c1 c2 c3,nvars=n1 n2 n3);
call symputx('hash2',hash);
c3='f';
hash=%mp_md5(cvars=c1 c2 c3,nvars=n1 n2 n3);
call symputx('hash3',hash);
run;
%mp_assertscope(COMPARE,ignorelist=HASH1 HASH2 HASH3)
%mp_assert(
iftrue=("&hash1" ne "&hash2"),
desc=Checking first hash diff,
outds=work.test_results
)
%mp_assert(
iftrue=("&hash2" ne "&hash3"),
desc=Checking first hash diff,
outds=work.test_results
)

View File

@@ -42,6 +42,7 @@ run;
,mdebug=1
)
%mp_assertscope(SNAPSHOT)
/**

View File

@@ -0,0 +1,28 @@
/**
@file
@brief Testing mp_streamfile.sas macro
@details This is tricky to test as it streams to webout. For now just
check the compilation, and for macro leakage.
<h4> SAS Macros </h4>
@li mp_assert.sas
@li mp_assertscope.sas
@li mp_streamfile.sas
**/
%mp_assertscope(SNAPSHOT)
%mp_streamfile(iftrue=(1=0)
,contenttype=csv,inloc=/some/where.txt
,outname=myfile.txt
)
%mp_assertscope(COMPARE)
%mp_assert(
iftrue=(&syscc=0),
desc=Checking error condition,
outds=work.test_results
)

View File

@@ -0,0 +1,19 @@
/**
@file
@brief Difftable DDL test
<h4> SAS Macros </h4>
@li mddl_dc_difftable.sas
@li mf_existds.sas
@li mp_assert.sas
**/
%mddl_dc_difftable(libds=WORK.DIFFTABLE)
%mp_assert(
iftrue=(%mf_existds(WORK.DIFFTABLE)=1),
desc=Checking table was created,
outds=work.test_results
)

View File

@@ -0,0 +1,18 @@
/**
@file
@brief Filtertable DDL test
<h4> SAS Macros </h4>
@li mddl_dc_filterdetail.sas
@li mf_existds.sas
@li mp_assert.sas
**/
%mddl_dc_filterdetail(libds=WORK.TEST)
%mp_assert(
iftrue=(%mf_existds(WORK.TEST)=1),
desc=Checking table was created,
outds=work.test_results
)

View File

@@ -0,0 +1,18 @@
/**
@file
@brief Filtersummary DDL test
<h4> SAS Macros </h4>
@li mddl_dc_filtersummary.sas
@li mf_existds.sas
@li mp_assert.sas
**/
%mddl_dc_filtersummary(libds=WORK.TEST)
%mp_assert(
iftrue=(%mf_existds(WORK.TEST)=1),
desc=Checking table was created,
outds=work.test_results
)

View File

@@ -0,0 +1,18 @@
/**
@file
@brief Locktable DDL test
<h4> SAS Macros </h4>
@li mddl_dc_locktable.sas
@li mf_existds.sas
@li mp_assert.sas
**/
%mddl_dc_locktable(libds=WORK.TEST)
%mp_assert(
iftrue=(%mf_existds(WORK.TEST)=1),
desc=Checking table was created,
outds=work.test_results
)

View File

@@ -0,0 +1,18 @@
/**
@file
@brief Maxkeytable DDL test
<h4> SAS Macros </h4>
@li mddl_dc_maxkeytable.sas
@li mf_existds.sas
@li mp_assert.sas
**/
%mddl_dc_maxkeytable(libds=WORK.TEST)
%mp_assert(
iftrue=(%mf_existds(WORK.TEST)=1),
desc=Checking table was created,
outds=work.test_results
)

View File

@@ -0,0 +1,18 @@
/**
@file
@brief mddl_sas_cntlout ddl test
<h4> SAS Macros </h4>
@li mddl_sas_cntlout.sas
@li mf_existds.sas
@li mp_assert.sas
**/
%mddl_sas_cntlout(libds=WORK.TEST)
%mp_assert(
iftrue=(%mf_existds(WORK.TEST)=1),
desc=Checking table was created,
outds=work.test_results
)

View File

@@ -0,0 +1,21 @@
/**
@file
@brief Testing mm_getauthinfo macro
<h4> SAS Macros </h4>
@li mf_existds.sas
@li mm_getauthinfo.sas
@li mp_assertscope.sas
**/
%mp_assertscope(SNAPSHOT)
%mm_getauthinfo(outds=auths)
%mp_assertscope(COMPARE)
%mp_assert(
iftrue=(%mf_existds(work.auths)=1),
desc=Check if the auths dataset was created
)

View File

@@ -0,0 +1,52 @@
/**
@file
@brief Testing mfs_httpheader.sas macro
<h4> SAS Macros </h4>
@li mfs_httpheader.sas
@li mp_assert.sas
@li mp_assertscope.sas
**/
%let sasjs_stpsrv_header_loc=%sysfunc(pathname(work))/header.txt;
%mp_assertscope(SNAPSHOT)
%mfs_httpheader(Content-type,application/csv)
%mp_assertscope(COMPARE)
data _null_;
infile "&sasjs_stpsrv_header_loc";
input;
if _n_=1 then call symputx('test1',_infile_);
run;
%mp_assert(
iftrue=(&syscc=0),
desc=Check code ran without errors,
outds=work.test_results
)
%mp_assert(
iftrue=("&test1"="Content-type: application/csv"),
desc=Checking line was created,
outds=work.test_results
)
%mfs_httpheader(Content-type,application/text)
%let test2=0;
data _null_;
infile "&sasjs_stpsrv_header_loc";
input;
if _n_=2 then call symputx('test2',_infile_);
run;
%mp_assert(
iftrue=(&syscc=0),
desc=Check code ran without errors for test2,
outds=work.test_results
)
%mp_assert(
iftrue=("&test2"="Content-type: application/text"),
desc=Checking line was created,
outds=work.test_results
)

View File

@@ -0,0 +1,30 @@
/**
@file
@brief Testing ms_createfile.sas macro
<h4> SAS Macros </h4>
@li ms_createfile.sas
@li mp_assert.sas
@li mp_assertscope.sas
**/
filename stpcode temp;
data _null_;
file stpcode;
put '%put hello world;';
run;
options mprint;
%let fname=%mf_getuniquename();
%mp_assertscope(SNAPSHOT)
%ms_createfile(/sasjs/tests/&fname..sas
,inref=stpcode
,mdebug=1
)
%mp_assertscope(COMPARE)

View File

@@ -0,0 +1,44 @@
/**
@file
@brief Testing ms_runstp.sas macro
<h4> SAS Macros </h4>
@li ms_runstp.sas
@li mp_assert.sas
@li mp_assertscope.sas
**/
%mp_assertscope(SNAPSHOT)
%ms_runstp(/Public/app/frs/allan/services/common/appinit
,debug=131
,outref=weboot
)
%mp_assertscope(COMPARE)
libname webeen json (weboot);
data _null_;
infile weboot;
input;
putlog _infile_;
run;
data work.httpheaders;
set webeen.httpheaders;
call symputx('test1',content_type);
run;
data work.log;
set webeen.log;
put (_all_)(=);
if _n_>10 then stop;
run;
%mp_assert(
iftrue=("&test1"="application/json"),
desc=Checking line was created,
outds=work.test_results
)

View File

@@ -19,7 +19,7 @@
%macro loglevel();
%if "&_debug"="2477" or "&_debug"="fields,log,trace" %then %do;
%put debug mode activated;
options mprint;
options mprint mprintnest;
%end;
%mend loglevel;

View File

@@ -2,8 +2,18 @@
@file
@brief term file for tests
<h4> SAS Macros </h4>
@li mp_assert.sas
**/
%mp_assert(
iftrue=(&syscc=0),
desc=Checking final error condition,
outds=work.test_results
)
%webout(OPEN)
%webout(OBJ, TEST_RESULTS)
%webout(CLOSE)

View File

@@ -76,3 +76,6 @@ run;
iftrue=(&syscc ne 0),
desc=Check that non zero return code is returned if called job fails
)
/* set syscc to zero for final check in testterm */
%let syscc=0;

View File

@@ -308,7 +308,7 @@ data _null_;
put ' %put &sysmacroname: Switching to DATASTEP engine; ';
put ' %goto datastep; ';
put ' %end; ';
put ' data &tempds /view=&tempds;set &ds; ';
put ' data &tempds;set &ds; ';
put ' %if &fmt=N %then format _numeric_ best32.;; ';
put ' /* PRETTY is necessary to avoid line truncation in large files */ ';
put ' proc json out=&jref pretty ';
@@ -359,7 +359,7 @@ data _null_;
put ' %end; ';
put ' other = [best.]; ';
put ' ';
put ' data &tempds/view=&tempds; ';
put ' data &tempds; ';
put ' attrib _all_ label=''''; ';
put ' %do i=1 %to &numcols; ';
put ' %if &&typelong&i=char or &fmt=Y %then %do; ';
@@ -421,8 +421,7 @@ data _null_;
put ' %end; ';
put ' ';
put ' proc sql; ';
put ' drop view &tempds; ';
put ' drop table &colinfo; ';
put ' drop table &colinfo, &tempds; ';
put ' ';
put ' %if &showmeta=YES %then %do; ';
put ' data _null_; file &jref encoding=''utf-8'' mod; ';

View File

@@ -1,10 +1,10 @@
/**
@file mv_registerclient.sas
@brief Register Client and Secret (admin task)
@details When building apps on SAS Viya, an client id and secret are sometimes
required. In order to generate them, filesystem access to the Consul Token
is needed (it is not enough to be in the SASAdministrator group in SAS
Environment Manager).
@details When building apps on SAS Viya, a client id and secret are usually
required. In order to generate them, the Consul Token is required. To access
this token, you need to be a system administrator (it is not enough to be in
the SASAdministrator group in SAS Environment Manager).
If you are registering a lot of clients / secrets, you may find it more
convenient to use the [Viya Token Generator]
@@ -25,51 +25,56 @@
"https://raw.githubusercontent.com/sasjs/core/main/all.sas";
%inc mc;
%* generate random client using consul token as input parameter;
%mv_registerclient(consul_token=12x34sa43v2345n234lasd)
%* generate random client details with all scopes;
%mv_registerclient(scopes=openid *)
%* specific client with just openid scope;
%mv_registerclient(client_id=YourClient
,client_secret=YourSecret
,scopes=openid
)
%* generate random client details with all scopes;
%mv_registerclient(scopes=openid *)
%* generate random client with 90/180 second access/refresh token expiry;
%mv_registerclient(scopes=openid *
,access_token_validity=90
,refresh_token_validity=180
)
@param client_id= The client name. Auto generated if blank.
@param client_secret= Client secret. Auto generated if client is blank.
@param scopes=(openid) List of space-seperated unquoted scopes
@param grant_type=(authorization_code|refresh_token) Valid values are
"password" or "authorization_code" (unquoted)
@param outds=(mv_registerclient) The dataset to contain the registered client
id and secret
@param access_token_validity=(DEFAULT) The duration of validity of the access
token in seconds. A value of DEFAULT will omit the entry (and use system
default)
@param refresh_token_validity=(DEFAULT) The duration of validity of the
@param [in,out] client_id= The client name. Auto generated if blank.
@param [in,out] client_secret= Client secret. Auto generated if client is
blank.
@param [in] consul_token= (0) Provide the actual consul token value here if
using Viya 4 or above.
@param [in] scopes= (openid) List of space-seperated unquoted scopes
@param [in] grant_type= (authorization_code|refresh_token) Valid values are
"password" or "authorization_code" (unquoted). Pipe seperated.
@param [out] outds=(mv_registerclient) The dataset to contain the registered
client id and secret
@param [in] access_token_validity= (DEFAULT) The access token duration in
seconds. A value of DEFAULT will omit the entry (and use system default)
@param [in] refresh_token_validity= (DEFAULT) The duration of validity of the
refresh token in seconds. A value of DEFAULT will omit the entry (and use
system default)
@param name= An optional, human readable name for the client
@param required_user_groups= A list of group names. If a user does not belong
to all the required groups, the user will not be authenticated and no tokens
are issued to this client for that user. If this field is not specified,
authentication and token issuance proceeds normally.
@param autoapprove= During the auth step the user can choose which scope to
apply. Setting this to true will autoapprove all the client scopes.
@param use_session= If true, access tokens issued to this client will be
@param [in] client_name= (DEFAULT) An optional, human readable name for the
client.
@param [in] required_user_groups= A list of group names. If a user does not
belong to all the required groups, the user will not be authenticated and no
tokens are issued to this client for that user. If this field is not
specified, authentication and token issuance proceeds normally.
@param [in] autoapprove= During the auth step the user can choose which scope
to apply. Setting this to true will autoapprove all the client scopes.
@param [in] use_session= If true, access tokens issued to this client will be
associated with an HTTP session and revoked upon logout or time-out.
@param outjson= (_null_) A dataset containing the lines of JSON submitted.
Useful for debugging.
@param [out] outjson= (_null_) A dataset containing the lines of JSON
submitted. Useful for debugging.
@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_getuniquefileref.sas
@li mf_getuniquelibref.sas
@@ -81,6 +86,7 @@
%macro mv_registerclient(client_id=
,client_secret=
,consul_token=0
,client_name=DEFAULT
,scopes=openid
,grant_type=authorization_code|refresh_token
@@ -92,33 +98,40 @@
,refresh_token_validity=DEFAULT
,outjson=_null_
);
%local consul_token fname1 fname2 fname3 libref access_token url tokloc;
%local 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;
Generated by %mf_getuser() (&sysuserid) 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;
%let tokloc=%mf_loc(VIYACONFIG)&tokloc/client.token;
%if "&consul_token"="0" %then %do;
/* first, get consul token needed to get client id / secret */
%let tokloc=/etc/SASSecurityCertificateFramework/tokens/consul/default;
%let tokloc=%mf_loc(VIYACONFIG)&tokloc/client.token;
%mp_abort(iftrue=(%sysfunc(fileexist(&tokloc))=0)
,mac=&sysmacroname
,msg=%str(Unable to access the consul token at &tokloc)
)
%if %sysfunc(fileexist(&tokloc))=0 %then %do;
%put &sysmacroname: unable to access the consul token at &tokloc;
%put Try passing the value in the consul= macro parameter;
%put See docs: https://core.sasjs.io/mv__registerclient_8sas.html;
%abort;
%end;
%let consul_token=0;
data _null_;
infile "&tokloc";
input token:$64.;
call symputx('consul_token',token);
run;
data _null_;
infile "&tokloc";
input token:$64.;
call symputx('consul_token',token);
run;
%mp_abort(iftrue=("&consul_token"="0")
,mac=&sysmacroname
,msg=%str(Unable to source the consul token from &tokloc)
)
%if "&consul_token"="0" %then %do;
%put &sysmacroname: Unable to source the consul token from &tokloc;
%put It seems your account (&sysuserid) does not have admin rights;
%put Please speak with your platform adminstrator;
%put Docs: https://core.sasjs.io/mv__registerclient_8sas.html;
%abort;
%end;
%end;
%local base_uri; /* location of rest apis */
%let base_uri=%mf_getplatform(VIYARESTAPI);
@@ -131,6 +144,9 @@ proc http method='POST' out=&fname1
headers "X-Consul-Token"="&consul_token";
run;
%put &=SYS_PROCHTTP_STATUS_CODE;
%put &=SYS_PROCHTTP_STATUS_PHRASE;
%let libref=%mf_getuniquelibref();
libname &libref JSON fileref=&fname1;