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

Compare commits

...

148 Commits

Author SHA1 Message Date
Allan Bowe
59200a6e73 Merge pull request #69 from sasjs/issue68
feat: supporting postgres timestamps for mp_ds2inserts and mp_lib2inserts
2021-08-24 21:03:09 +03:00
Allan Bowe
f468f60ae1 fix: substr issue in mp_ds2inserts. Closes #68 2021-08-24 20:51:29 +03:00
Allan Bowe
9f60d827b6 feat: supporting postgres timestamps for mp_ds2inserts and mp_lib2inserts 2021-08-24 20:39:02 +03:00
Allan Bowe
5c936ddb65 Merge pull request #67 from sasjs/issue66
feat: adding APPEND option to mp_binarycopy.sas, and a new test (mp_b…
2021-08-24 14:04:25 +03:00
Allan Bowe
d0bde62594 feat: adding APPEND option to mp_binarycopy.sas, and a new test (mp_binarycopy.test.sas). Closes #66 2021-08-24 13:43:31 +03:00
Allan Bowe
ada9192337 Merge pull request #65 from sasjs/issue64
fix: increasing limit for mv_getfoldermember.sas, also adding a test,…
2021-08-20 22:50:27 +03:00
Allan Bowe
6161f588a9 fix: increasing limit for mv_getfoldermember.sas, also adding a test, and updating mf_getapploc to search _program by default (and fixing it to work with macro tests, and updating the test for that also) 2021-08-20 22:38:56 +03:00
Allan Bowe
67079d8c17 fix: adding default value for exists in mf_existfunction 2021-08-19 00:20:49 +03:00
Allan Bowe
75bd39adb0 Merge pull request #63 from sasjs/issue62
feat: adding FCMP capability
2021-08-19 00:00:15 +03:00
Allan Bowe
078bdbeecf chore: bumping devDependency (sasjs cli) 2021-08-18 23:47:17 +03:00
Allan Bowe
8ddb86785c feat: new fcmp stpsrv_header function 2021-08-18 23:45:45 +03:00
Allan Bowe
005af0ecf8 feat: new mf_existfunction macro 2021-08-18 23:36:12 +03:00
Allan Bowe
bc410a9135 chore: fixing docs for tests 2021-08-18 19:55:05 +03:00
Allan Bowe
fc8ba2e36c chore: moving files to tidy up docs 2021-08-18 19:43:38 +03:00
Allan Bowe
756441384a feat: adding first fcmp macro, mcf_string2file.sas 2021-08-18 18:35:51 +03:00
Allan Bowe
10f9eecf9e Merge pull request #60 from sasjs/vpn-fix
chore: vpn fix
2021-08-13 14:41:25 +03:00
470ebb50a7 chore: vpn fix 2021-08-13 12:57:31 +02:00
Allan Bowe
26cd5d9d31 Merge pull request #59 from Stefan-Dimitrov-Stoyanov/patch-1
Update README.md
2021-08-12 11:59:25 +03:00
Stefan-Dimitrov-Stoyanov
0b694bb878 Update README.md
Fix the missing space at the end of the first line under the Installation header:  https://github.com/sasjs/core/blob/main/README.md#installation
2021-08-11 13:34:44 +01:00
Allan Bowe
b403c02bba chore: docs for mm_createfolder 2021-08-06 15:36:55 +03:00
Allan Bowe
0b555bb31c Merge pull request #58 from sasjs/apploc
feat: new mf_getapploc macro
2021-08-04 22:04:55 +03:00
Allan Bowe
40b513a9e3 feat: new mf_getapploc macro 2021-08-04 22:00:18 +03:00
Allan Bowe
4eacf4deae Merge pull request #57 from sasjs/mf_existfref
fix: showing filerefs that exist (even when underlying does not) in m…
2021-08-03 14:32:23 +03:00
Allan Bowe
5824423c13 fix: showing filerefs that exist (even when underlying does not) in mf_existfileref, along with 3 tests 2021-08-03 14:16:52 +03:00
Allan Bowe
ce5bfd41dc chore(docs): updating header for mm_gettables 2021-08-02 10:37:01 +03:00
Allan Bowe
0c67a07e42 Merge pull request #56 from sasjs/ddlworkz
feat: new mp_lib2inserts macro.  In addition, modified mp_getddl to i…
2021-07-30 10:11:27 +03:00
Allan Bowe
187504600a chore: fixing test 2021-07-30 00:21:02 +03:00
Allan Bowe
658d67feaa chore: fixing comments 2021-07-30 00:17:44 +03:00
Allan Bowe
5207a77591 feat: new mp_lib2inserts macro. In addition, modified mp_getddl to ignore views, closing #5. Created a test, which highlighted another issue in mp_getddl (labels were being double quoted which caused macro resolution attempts when %including). Changed to single quotes. Switched 'outlib' to 'outschema' in mp_ds2inserts to harmonise with mp_getddl. Added a maxobs option, to speed up testing. 2021-07-30 00:14:29 +03:00
Allan Bowe
4456adf1dc fix: switching default flavour from BASE to SAS to be consistent with mp_getddl 2021-07-29 20:35:57 +03:00
Allan Bowe
03962c2a50 fix: for PGSQL DDL generation, ignore tables with over 1600 columns (as they are not supported in Postgres) 2021-07-29 15:45:16 +03:00
Allan Bowe
6d2fc7e265 fix: removing bug introduced to mp_getddl and adding a test to prevent regression 2021-07-29 13:02:58 +03:00
Allan Bowe
39b2e7c5f9 fix: supporting salts over 32 chars in mp_hashdataset() 2021-07-28 23:22:43 +03:00
Allan Bowe
f99adf5c3e Merge pull request #55 from sasjs/insertforpg
feat: updating mp_ds2inserts to support postgres database
2021-07-28 19:11:38 +03:00
Allan Bowe
69f8e91a2d feat: adding salt as option for mp_hashdataset 2021-07-28 17:04:41 +03:00
Allan Bowe
5b5d01993f fix: updating mp_getddl to enable append to a fileref 2021-07-28 17:01:49 +03:00
Allan Bowe
00fa464a7c feat: updating mp_ds2inserts to support postgres database 2021-07-27 11:07:12 +03:00
Allan Bowe
a5baf46233 chore: removing unnecessary proc format and generating the all.sas file 2021-07-26 22:30:02 +03:00
Allan Bowe
d63d2a4ec1 Merge pull request #54 from sasjs/mp_ds2inserts
feat: mp_ds2inserts macro
2021-07-26 22:10:44 +03:00
Allan Bowe
900f694065 chore: removing extra period 2021-07-26 21:59:48 +03:00
Allan Bowe
838324c15e chore: updating header 2021-07-26 19:06:53 +03:00
Allan Bowe
e3205ec06c feat: mp_ds2inserts macro for creating programs for inserting data (and corresponding test) 2021-07-26 19:04:49 +03:00
Allan Bowe
154a33434e chore: more contributors 2021-07-24 21:06:30 +03:00
Allan Bowe
bfa1bbaeb1 chore: all contributors update in README 2021-07-24 21:03:49 +03:00
Allan Bowe
1f0128aec4 Merge pull request #53 from sasjs/base64doublebytefix
fix: mp_base64copy.sas fixes, removed renegade % symbol and issue wit…
2021-07-18 17:16:58 +03:00
Allan Bowe
69f03f4e14 fix: mp_base64copy.sas fixes, removed renegade % symbol and issue with truncation at character 76. Added two tests, including one to test double byte encoded characters. 2021-07-18 17:05:05 +03:00
Allan Bowe
a932f321d8 Merge pull request #52 from sasjs/sasjs-cli-version-bump
fix: bump sasjs/cli version + 'prepare' support windows CMD/Powershell
2021-07-10 09:40:48 +03:00
Saad Jutt
21200c11c1 fix: bump sasjs/cli version + 'prepare' support windows CMD/Powershell 2021-07-10 03:43:58 +05:00
Allan Bowe
825c97c49c fix: switch postinstall to prepare 2021-06-30 19:50:20 +03:00
Allan Bowe
f301899269 Merge pull request #51 from sasjs/issue50
fix: setting syscc to zero to prevent error state in response.  Close…
2021-06-29 00:09:42 +03:00
Allan Bowe
fc81f62d2f fix: setting syscc to zero to prevent error state in response. Closes #50 2021-06-28 23:52:12 +03:00
Allan Bowe
93aea5ed02 Merge pull request #49 from sasjs/logfix
Context fixes on mv_jobflow and mp_testservice
2021-06-27 00:35:24 +03:00
Allan Bowe
55d4c7238a fix: updating mp_testservice.sas and mv_jobflow to use the provided context. Also updating mv_getjobresult to fetch byte by byte (as some inputs are very wide). 2021-06-27 00:22:53 +03:00
Allan Bowe
cd75bf263a fix: removing redundant parameter from mv_getjoblog 2021-06-26 21:11:26 +03:00
Allan Bowe
929a1a9974 chore: updating docs 2021-06-24 00:39:09 +03:00
Allan Bowe
7cafb4fb36 Merge pull request #48 from sasjs/base64
feat: adding mp_base64copy macro
2021-06-24 00:30:21 +03:00
Allan Bowe
a8d222a0f8 chore: automated commit 2021-06-24 00:29:54 +03:00
Allan Bowe
ac0ddf38b0 chore: automated commit 2021-06-24 00:28:41 +03:00
Allan Bowe
ecd389c935 feat: adding mp_base64copy macro 2021-06-24 00:26:41 +03:00
Allan Bowe
06a5ea06f8 Merge pull request #46 from sasjs/mendfixes
sasjs lint fix for macro name in MEND statement
2021-06-23 22:27:36 +03:00
Allan Bowe
955471ed3c Merge branch 'main' into mendfixes 2021-06-23 21:55:44 +03:00
Allan Bowe
c8d3b43b12 fix: adding lrecl to mv_createfile to support lines 1 million characters wide. Closes #47 2021-06-23 21:53:32 +03:00
Allan Bowe
3e313b06a9 fix: adding mend in python lua build 2021-06-21 17:25:58 +03:00
Allan Bowe
d7371a4505 fix: adding mend to every macro statement using sasjs lint fix 2021-06-21 17:25:01 +03:00
Allan Bowe
32a6d15c2e Merge pull request #44 from sasjs/vpn-connection
chore: added vpn connection
2021-06-21 14:35:19 +03:00
Allan Bowe
b109e7cead fix: bumping core 2021-06-21 11:27:06 +00:00
d291d3e287 chore: added vpn connection 2021-06-21 11:49:37 +02:00
Allan Bowe
5a2968e798 fix: supporting LATIN1 as well as WLATIN1 in mm_webout 2021-06-17 16:31:29 +03:00
Allan Bowe
120ad9a7da fix: supporting bell cand escape characters when creating viya jobs / services with macro 2021-06-11 00:09:16 +03:00
Allan Bowe
67a81b2690 chore: updating the docs for mm_spkexport.sas 2021-06-08 20:09:23 +03:00
Allan Bowe
506cf1812f fix: deal with dashes in sysencoding 2021-06-08 16:59:43 +03:00
Allan Bowe
8cc0eb0dd7 Merge pull request #42 from sasjs/issue41
closes #42 Issue41
2021-06-03 22:44:16 +03:00
Allan Bowe
4c1f69da3a fix: using PROC JSON for JSON where SYSENCODING=wlatin1 2021-06-01 18:24:04 +03:00
Allan Bowe
f160ebe705 chore: updating all.sas from previous pushes 2021-06-01 18:01:01 +03:00
Allan Bowe
3f49925d01 Merge pull request #40 from sasjs/dependabot/npm_and_yarn/ws-7.4.6
chore(deps): bump ws from 7.4.5 to 7.4.6
2021-06-01 17:58:49 +03:00
Allan Bowe
53ed5dc916 fix: adding extra debugging to mf_getvarlist 2021-06-01 09:32:19 +03:00
Allan Bowe
808b24e31b chore: adding mend statement 2021-05-31 15:15:17 +03:00
dependabot[bot]
c51c9c2ca9 chore(deps): bump ws from 7.4.5 to 7.4.6
Bumps [ws](https://github.com/websockets/ws) from 7.4.5 to 7.4.6.
- [Release notes](https://github.com/websockets/ws/releases)
- [Commits](https://github.com/websockets/ws/compare/7.4.5...7.4.6)

Signed-off-by: dependabot[bot] <support@github.com>
2021-05-31 02:08:52 +00:00
Allan Bowe
4d6edf5566 feat: additional options for mv_createfile.sas, also a log message to enable file to be easily opened 2021-05-30 13:22:50 +03:00
Allan Bowe
41a24677f5 chore: automated commit 2021-05-27 09:42:15 +03:00
Allan Bowe
e7d8d8ffb3 chore: updating docs 2021-05-27 09:40:38 +03:00
Allan Bowe
60d23dd618 Merge pull request #39 from sasjs/newviyafeatures
Newviyafeatures
2021-05-27 00:31:44 +03:00
Allan Bowe
d2764c3cd1 chore: updating all.sas 2021-05-26 23:37:38 +03:00
Allan Bowe
f9f4355143 feat: adding mv_createfile.sas and tests 2021-05-26 23:37:24 +03:00
Allan Bowe
18bc6c889d feat: adding mfv_existfolder.sas and tests 2021-05-26 23:37:06 +03:00
Allan Bowe
42a16ef496 feat: adding mfv_existfile.sas and tests 2021-05-26 23:36:43 +03:00
Allan Bowe
8178b801fb fix: using mfv_existfolder macro in mv_createfolder to save requests 2021-05-26 23:36:09 +03:00
Allan Bowe
ce331a23c8 fix: ensuring obs is updated when the table has zero columns 2021-05-26 23:35:25 +03:00
Allan Bowe
1cc9213467 feat: refreshing mf_abort (it's now an actual macro function) 2021-05-26 23:34:43 +03:00
Allan Bowe
aabbf4d0f9 Merge pull request #38 from sasjs/weboutfixes
fix: changing replace to YES in mm_createwebservice.sas, also setting…
2021-05-26 18:29:08 +03:00
Allan Bowe
2bea8be70d chore: logging in test 2021-05-26 18:19:04 +03:00
Allan Bowe
9b2368443e fix: changing replace to YES in mm_createwebservice.sas, also setting a default for code to ft15f001. More debugging added to mv_createfolder.sas 2021-05-26 10:36:45 +03:00
Allan Bowe
7915ba2c41 fix: running python to rebuild all.sas and mx_createwebservice macros 2021-05-25 15:50:06 +03:00
Yury Shkoda
cc61e48868 Merge pull request #37 from sasjs/createfolderfix
fix: addressed issue when creating recursive folers in mv_createfolde…
2021-05-25 15:40:27 +03:00
Allan Bowe
62db83dcf6 fix: addressed issue when creating recursive folers in mv_createfolder.sas 2021-05-25 15:33:02 +03:00
Allan Bowe
7ea9e0f8e9 Merge pull request #36 from sasjs/filter_json_fixes
Filter json fixes
2021-05-21 16:30:53 +03:00
Allan Bowe
1c852515f5 fix: adding more tests to mp_filtervalidate 2021-05-21 16:27:10 +03:00
Allan Bowe
b7e677bd8e fix: adding utf8 to mp_jsonout 2021-05-21 16:26:53 +03:00
Allan Bowe
f47f0d2cee chore: removing ghooks reference from package.json 2021-05-20 11:56:41 +03:00
Allan Bowe
3d0f426a98 Merge pull request #35 from sasjs/issue33
fix: adding sysvlong to mp_abort also
2021-05-20 11:40:29 +03:00
Allan Bowe
2cb51f6164 fix: adding sysvlong to mp_abort also 2021-05-20 11:39:28 +03:00
Allan Bowe
b73bf998da Merge pull request #34 from sasjs/issue33
feat: adding sysvlong to the webout macros, also updating documentati…
2021-05-20 11:25:43 +03:00
Allan Bowe
c9ad38ee98 feat: adding sysvlong to the webout macros, also updating documentation and adding tests. Closes #33 2021-05-20 11:23:10 +03:00
Allan Bowe
76b1b951c0 chore: adding SECURITY.md 2021-05-19 19:05:46 +03:00
Allan Bowe
996054b17a Merge pull request #32 from sasjs/git-commit-hook
feat(git): enabled pre-commit hook enforcing conventional commits
2021-05-19 18:55:35 +03:00
Allan Bowe
7fca3d4e3f chore: readme update for star graph 2021-05-19 18:24:02 +03:00
Allan Bowe
c4e599c861 chore: readme update (badges for issue counts) 2021-05-19 18:19:35 +03:00
Allan Bowe
0f6ff2cc1e chore: updating devdependency version for sasjs/cli 2021-05-19 18:16:00 +03:00
Allan Bowe
7cac4c71fb chore: updating package.json 2021-05-19 18:14:04 +03:00
Allan Bowe
1322bdab92 fix: deprecating ghooks, adding conventional commit hook also 2021-05-19 18:10:57 +03:00
Allan Bowe
f201df606a chore: automated commit 2021-05-19 17:42:43 +03:00
Yury Shkoda
a56fce86b1 feat(git): enabled pre-commit hook enforcing conventional commits 2021-05-19 15:03:02 +03:00
Allan Bowe
41ccc5fdd9 Merge pull request #30 from sasjs/travis
chore: travis yaml
2021-05-16 00:25:49 +03:00
Allan Bowe
b2877bd493 chore: automated commit 2021-05-16 00:25:04 +03:00
Allan Bowe
df8f8893e7 chore: automated commit 2021-05-16 00:16:59 +03:00
Allan Bowe
27fbdf193b chore: automated commit 2021-05-16 00:15:52 +03:00
Allan Bowe
6ae892989d chore: automated commit 2021-05-16 00:14:37 +03:00
Allan Bowe
39a7b332da chore: automated commit 2021-05-16 00:12:35 +03:00
Allan Bowe
c81794b542 chore: automated commit 2021-05-16 00:08:10 +03:00
Allan Bowe
e456da846a chore: automated commit 2021-05-16 00:03:13 +03:00
Allan Bowe
5c144be05b chore: automated commit 2021-05-16 00:00:01 +03:00
Allan Bowe
055669c133 fix: adding test action 2021-05-15 23:41:41 +03:00
Allan Bowe
4b67e13b24 chore: travis yaml 2021-05-15 23:31:50 +03:00
Allan Bowe
f1ec3eda81 fix: more badges for README 2021-05-15 22:20:53 +03:00
Allan Bowe
f2d5859675 fix: adding CHANGELOG (as a stub) 2021-05-15 21:35:54 +03:00
Allan Bowe
ea057d4655 fix: adding .npmignore to reduce bundle size and updating homepage in README 2021-05-15 21:05:51 +03:00
Allan Bowe
26c085b354 fix: adding badges to README 2021-05-15 20:41:27 +03:00
Allan Bowe
d13ac52739 fix: adding test command to package.json 2021-05-15 16:55:57 +03:00
Allan Bowe
bbbc28ad6d Merge branch 'main' of github.com:sasjs/core 2021-05-15 16:31:13 +03:00
Allan Bowe
530cd6e95c feat: adding two more test types to mp_assertdsobs.sas, also a test for the assertion macro itself 2021-05-15 16:30:57 +03:00
Allan Bowe
c4e17e43e8 Create CODE_OF_CONDUCT.md 2021-05-15 15:03:59 +03:00
Allan Bowe
fed217eec3 Merge pull request #29 from sasjs/issue10
fix: adding checks for consul token access, and two tests to ensure t…
2021-05-15 14:42:53 +03:00
Allan Bowe
1934dc8332 fix: adding checks for consul token access, and two tests to ensure the macro is working. closes #10 2021-05-15 14:41:10 +03:00
Allan Bowe
9de512cfc7 Merge pull request #28 from sasjs/issue7
fix: adding licence info.  Closes #7
2021-05-13 21:50:36 +03:00
Allan Bowe
cadafcc86b fix: adding licence info. Closes #7 2021-05-13 21:49:54 +03:00
Allan Bowe
5f805b006f Merge pull request #27 from sasjs/issue14
feat: adding MATCH parameter to mp_searchcols.sas to enable fuzzy mat…
2021-05-13 21:40:03 +03:00
Allan Bowe
c6b65366b7 feat: adding MATCH parameter to mp_searchcols.sas to enable fuzzy matching on columns. Closes #14 2021-05-13 21:38:38 +03:00
Allan Bowe
51ddd9c1e5 chore: automated commit 2021-05-13 10:38:02 +03:00
Allan Bowe
20bf3b86af chore: automated commit 2021-05-13 10:34:54 +03:00
Allan Bowe
de67cd329b chore: automated commit 2021-05-12 16:32:24 +03:00
Allan Bowe
779e4942c7 Merge pull request #26 from tmoody/fix/clean_exit_mv_jobflow_on_syscc
fix: early exit, with syscc, when submitted jobs fail within a flow
2021-05-12 16:31:06 +03:00
Trevor Moody
a69a1ac7f0 fix: removed invisible hexchars on blank lines 2021-05-12 14:18:44 +01:00
Trevor Moody
2a644d6c2b fix: corrected asser description 2021-05-12 14:01:49 +01:00
Trevor Moody
843930c666 chore: added tests for mv_jobflow 2021-05-12 13:59:21 +01:00
Trevor Moody
90d69af7ee feat: early exit, with syscc, when submitted jobs fail within a flow 2021-05-12 12:06:02 +01:00
Allan Bowe
b7bafb49f4 Merge pull request #25 from sasjs/dcfixes
fix: more logging in mp_abort, fixing job test, better return values …
2021-05-11 23:37:25 +03:00
Allan Bowe
2fa9e48286 chore: automated commit 2021-05-11 23:36:40 +03:00
Allan Bowe
5cee93c7bd fix: more logging in mp_abort, fixing job test, better return values in mp_filtervalidate and mp_filtercheck, further fixes in mp_jsonout 2021-05-11 23:08:54 +03:00
208 changed files with 6926 additions and 1826 deletions

104
.all-contributorsrc Normal file
View File

@@ -0,0 +1,104 @@
{
"projectName": "core",
"projectOwner": "sasjs",
"repoType": "github",
"repoHost": "https://github.com",
"files": [
"README.md"
],
"imageSize": 100,
"commit": false,
"commitConvention": "angular",
"contributors": [
{
"login": "allanbowe",
"name": "Allan Bowe",
"avatar_url": "https://avatars.githubusercontent.com/u/4420615?v=4",
"profile": "https://github.com/allanbowe",
"contributions": [
"business",
"code",
"content",
"doc",
"infra",
"maintenance",
"mentoring",
"question",
"review",
"test"
]
},
{
"login": "rafgag",
"name": "rafgag",
"avatar_url": "https://avatars.githubusercontent.com/u/69139928?v=4",
"profile": "https://github.com/rafgag",
"contributions": [
"code"
]
},
{
"login": "tmoody",
"name": "Trevor Moody",
"avatar_url": "https://avatars.githubusercontent.com/u/79837106?v=4",
"profile": "https://github.com/tmoody",
"contributions": [
"code"
]
},
{
"login": "krishna-acondy",
"name": "Krishna Acondy",
"avatar_url": "https://avatars.githubusercontent.com/u/2980428?v=4",
"profile": "https://krishna-acondy.io/",
"contributions": [
"code",
"infra",
"blog",
"content",
"ideas",
"video"
]
},
{
"login": "saadjutt01",
"name": "Muhammad Saad ",
"avatar_url": "https://avatars.githubusercontent.com/u/8914650?v=4",
"profile": "https://github.com/saadjutt01",
"contributions": [
"code",
"ideas"
]
},
{
"login": "YuryShkoda",
"name": "Yury Shkoda",
"avatar_url": "https://avatars.githubusercontent.com/u/25773492?v=4",
"profile": "https://www.erudicat.com/",
"contributions": [
"code",
"infra",
"video"
]
},
{
"login": "medjedovicm",
"name": "Mihajlo Medjedovic",
"avatar_url": "https://avatars.githubusercontent.com/u/18329105?v=4",
"profile": "https://github.com/medjedovicm",
"contributions": [
"infra"
]
},
{
"login": "kkchandok",
"name": "kkchandok",
"avatar_url": "https://avatars.githubusercontent.com/u/46090627?v=4",
"profile": "https://github.com/kkchandok",
"contributions": [
"ideas"
]
}
],
"contributorsPerLine": 7
}

18
.git-hooks/commit-msg Executable file
View File

@@ -0,0 +1,18 @@
#!/bin/sh
RED="\033[1;31m"
GREEN="\033[1;32m"
# Get the commit message (the parameter we're given is just the path to the
# temporary file which holds the message).
commit_message=$(cat "$1")
if (echo "$commit_message" | grep -Eq "^(build|chore|ci|docs|feat|fix|perf|refactor|revert|style|test)(\([a-z \-]+\))?!?: .+$") then
echo "${GREEN} ✔ Commit message meets Conventional Commit standards"
exit 0
fi
echo "${RED}❌ Commit message does not meet the Conventional Commit standard!"
echo "An example of a valid message is:"
echo " feat(login): add the 'remember me' button"
echo " More details at: https://www.conventionalcommits.org/en/v1.0.0/#summary"
exit 1

2
.git-hooks/pre-commit Executable file
View File

@@ -0,0 +1,2 @@
#!/bin/sh
sasjs lint

30
.github/vpn/config.ovpn vendored Normal file
View File

@@ -0,0 +1,30 @@
cipher AES-256-CBC
setenv FORWARD_COMPATIBLE 1
client
server-poll-timeout 4
nobind
remote vpn.analytium.co.uk 1194 udp
remote vpn.analytium.co.uk 1194 udp
remote vpn.analytium.co.uk 443 tcp
remote vpn.analytium.co.uk 1194 udp
remote vpn.analytium.co.uk 1194 udp
remote vpn.analytium.co.uk 1194 udp
remote vpn.analytium.co.uk 1194 udp
remote vpn.analytium.co.uk 1194 udp
dev tun
dev-type tun
ns-cert-type server
setenv opt tls-version-min 1.0 or-highest
reneg-sec 604800
sndbuf 0
rcvbuf 0
# NOTE: LZO commands are pushed by the Access Server at connect time.
# NOTE: The below line doesn't disable LZO.
comp-lzo no
verb 3
setenv PUSH_PEER_INFO
ca ca.crt
cert user.crt
key user.key
tls-auth tls.key 1

84
.github/workflows/run-tests.yml vendored Normal file
View File

@@ -0,0 +1,84 @@
# This workflow will do a clean install of node dependencies, build the source code and run tests across different versions of node
# For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions
name: Node.js CI
on:
pull_request:
jobs:
test:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [12.x]
steps:
- uses: actions/checkout@v2
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v1
with:
node-version: ${{ matrix.node-version }}
- name: Write VPN Files
run: |
echo "$CA_CRT" > .github/vpn/ca.crt
echo "$USER_CRT" > .github/vpn/user.crt
echo "$USER_KEY" > .github/vpn/user.key
echo "$TLS_KEY" > .github/vpn/tls.key
shell: bash
env:
CA_CRT: ${{ secrets.CA_CRT}}
USER_CRT: ${{ secrets.USER_CRT }}
USER_KEY: ${{ secrets.USER_KEY }}
TLS_KEY: ${{ secrets.TLS_KEY }}
- name: Install Open VPN
run: |
sudo apt install apt-transport-https
sudo wget https://swupdate.openvpn.net/repos/openvpn-repo-pkg-key.pub
sudo apt-key add openvpn-repo-pkg-key.pub
sudo wget -O /etc/apt/sources.list.d/openvpn3.list https://swupdate.openvpn.net/community/openvpn3/repos/openvpn3-focal.list
sudo apt update
sudo apt install openvpn3
- name: Start Open VPN 3
run: openvpn3 session-start --config .github/vpn/config.ovpn
- name: Install Doxygen
run: sudo apt-get install doxygen
- name: Install dependencies
run: npm ci
- name: Check code style
run: npm run lint
- name: Add client
run: echo "CLIENT=${{secrets.CLIENT}}"> .env.viya
- name: Add secret
run: echo "SECRET=${{secrets.SECRET}}" >> .env.viya
- name: Add access token
run: echo "ACCESS_TOKEN=${{secrets.ACCESS_TOKEN}}" >> .env.viya
- name: Add refresh token
run: echo "REFRESH_TOKEN=${{secrets.REFRESH_TOKEN}}" >> .env.viya
- name: Build Project
run: npm run build
- name: Run SASjs tests
run: npm run test
env:
CI: true
CLIENT: ${{secrets.CLIENT}}
SECRET: ${{secrets.SECRET}}
SAS_USERNAME: ${{secrets.SAS_USERNAME}}
SAS_PASSWORD: ${{secrets.SAS_PASSWORD}}
SERVER_URL: ${{secrets.SERVER_URL}}
SERVER_TYPE: ${{secrets.SERVER_TYPE}}
ACCESS_TOKEN: ${{secrets.ACCESS_TOKEN}}
REFRESH_TOKEN: ${{secrets.REFRESH_TOKEN}}

View File

@@ -1,5 +1,5 @@
tasks:
- init: npm i -g @sasjs/cli
- init: nvm install --latest-npm && npm i -g @sasjs/cli
image:
file: .gitpod.dockerfile

11
.npmignore Normal file
View File

@@ -0,0 +1,11 @@
all.sas
build.py
.gitpod*
tests/
sasjs/
.github/
.git-hooks/
.vscode/
main.dox
make_singlefile.sh
*.md

View File

@@ -2,11 +2,11 @@
"noTrailingSpaces": true,
"noEncodedPasswords": true,
"hasDoxygenHeader": true,
"hasMacroNameInMend": false,
"hasMacroNameInMend": true,
"hasMacroParentheses": true,
"noNestedMacros": false,
"noSpacesInFileNames": true,
"maxLineLength": 135,
"maxLineLength": 230,
"lowerCaseFileNames": true,
"noTabIndentation": true,
"indentationMultiple": 2

5
CHANGELOG.md Normal file
View File

@@ -0,0 +1,5 @@
# CHANGELOG
As the changes are managed automatically in github, we don't generate an additional changelog. To view the fixes/features in each release, check out the releases page below:
[https://github.com/sasjs/core/releases](https://github.com/sasjs/core/releases)

128
CODE_OF_CONDUCT.md Normal file
View File

@@ -0,0 +1,128 @@
# Contributor Covenant Code of Conduct
## Our Pledge
We as members, contributors, and leaders pledge to make participation in our
community a harassment-free experience for everyone, regardless of age, body
size, visible or invisible disability, ethnicity, sex characteristics, gender
identity and expression, level of experience, education, socio-economic status,
nationality, personal appearance, race, religion, or sexual identity
and orientation.
We pledge to act and interact in ways that contribute to an open, welcoming,
diverse, inclusive, and healthy community.
## Our Standards
Examples of behavior that contributes to a positive environment for our
community include:
* Demonstrating empathy and kindness toward other people
* Being respectful of differing opinions, viewpoints, and experiences
* Giving and gracefully accepting constructive feedback
* Accepting responsibility and apologizing to those affected by our mistakes,
and learning from the experience
* Focusing on what is best not just for us as individuals, but for the
overall community
Examples of unacceptable behavior include:
* The use of sexualized language or imagery, and sexual attention or
advances of any kind
* Trolling, insulting or derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or email
address, without their explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Enforcement Responsibilities
Community leaders are responsible for clarifying and enforcing our standards of
acceptable behavior and will take appropriate and fair corrective action in
response to any behavior that they deem inappropriate, threatening, offensive,
or harmful.
Community leaders have the right and responsibility to remove, edit, or reject
comments, commits, code, wiki edits, issues, and other contributions that are
not aligned to this Code of Conduct, and will communicate reasons for moderation
decisions when appropriate.
## Scope
This Code of Conduct applies within all community spaces, and also applies when
an individual is officially representing the community in public spaces.
Examples of representing our community include using an official e-mail address,
posting via an official social media account, or acting as an appointed
representative at an online or offline event.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported to the community leaders responsible for enforcement at
https://sasapps.io/contact-us.
All complaints will be reviewed and investigated promptly and fairly.
All community leaders are obligated to respect the privacy and security of the
reporter of any incident.
## Enforcement Guidelines
Community leaders will follow these Community Impact Guidelines in determining
the consequences for any action they deem in violation of this Code of Conduct:
### 1. Correction
**Community Impact**: Use of inappropriate language or other behavior deemed
unprofessional or unwelcome in the community.
**Consequence**: A private, written warning from community leaders, providing
clarity around the nature of the violation and an explanation of why the
behavior was inappropriate. A public apology may be requested.
### 2. Warning
**Community Impact**: A violation through a single incident or series
of actions.
**Consequence**: A warning with consequences for continued behavior. No
interaction with the people involved, including unsolicited interaction with
those enforcing the Code of Conduct, for a specified period of time. This
includes avoiding interactions in community spaces as well as external channels
like social media. Violating these terms may lead to a temporary or
permanent ban.
### 3. Temporary Ban
**Community Impact**: A serious violation of community standards, including
sustained inappropriate behavior.
**Consequence**: A temporary ban from any sort of interaction or public
communication with the community for a specified period of time. No public or
private interaction with the people involved, including unsolicited interaction
with those enforcing the Code of Conduct, is allowed during this period.
Violating these terms may lead to a permanent ban.
### 4. Permanent Ban
**Community Impact**: Demonstrating a pattern of violation of community
standards, including sustained inappropriate behavior, harassment of an
individual, or aggression toward or disparagement of classes of individuals.
**Consequence**: A permanent ban from any sort of public interaction within
the community.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
version 2.0, available at
https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
Community Impact Guidelines were inspired by [Mozilla's code of conduct
enforcement ladder](https://github.com/mozilla/diversity).
[homepage]: https://www.contributor-covenant.org
For answers to common questions about this code of conduct, see the FAQ at
https://www.contributor-covenant.org/faq. Translations are available at
https://www.contributor-covenant.org/translations.

View File

@@ -1,4 +1,25 @@
# Macro Core
[![npm package][npm-image]][npm-url]
[![Github Workflow][githubworkflow-image]][githubworkflow-url]
[![Dependency Status][dependency-image]][dependency-url]
[![npm](https://img.shields.io/npm/dt/@sasjs/core)]()
![Snyk Vulnerabilities for npm package](https://img.shields.io/snyk/vulnerabilities/npm/@sasjs/core)
[![License](https://img.shields.io/apm/l/atomic-design-ui.svg)](/LICENSE)
![GitHub top language](https://img.shields.io/github/languages/top/sasjs/core)
[![GitHub closed issues](https://img.shields.io/github/issues-closed-raw/sasjs/core)](https://github.com/sasjs/core/issues?q=is%3Aissue+is%3Aclosed)
[![GitHub issues](https://img.shields.io/github/issues-raw/sasjs/core)](https://github.com/sasjs/core/issues)
![total lines](https://tokei.rs/b1/github/sasjs/core)
[![Gitpod ready-to-code](https://img.shields.io/badge/Gitpod-ready--to--code-908a85?logo=gitpod)](https://gitpod.io/#https://github.com/sasjs/core)
[npm-image]:https://img.shields.io/npm/v/@sasjs/core.svg
[npm-url]:http://npmjs.org/package/@sasjs/core
[githubworkflow-image]:https://github.com/sasjs/core/actions/workflows/main.yml/badge.svg
[githubworkflow-url]:https://github.com/sasjs/core/blob/main/.github/workflows/main.yml
[dependency-image]:https://david-dm.org/sasjs/core.svg
[dependency-url]:https://github.com/sasjs/core/blob/main/package.json
Much quality. Many standards. The **Macro Core** library exists to save time and development effort! Herein ye shall find a veritable host of MIT-licenced, production quality SAS macros. These are a mix of tools, utilities, functions and code generators that are useful in the context of [Application Development](https://sasapps.io) on the SAS platform (eg https://datacontroller.io). [Contributions](https://github.com/sasjs/core/blob/main/CONTRIBUTING.md) are welcomed.
@@ -20,6 +41,13 @@ Documentation: https://core.sasjs.io
- No X command
- Prefixes: _mf_, _mp_
**fcmp** library (SAS9/Viya)
- 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.
**meta** library (SAS9 only)
- OS independent
@@ -63,7 +91,7 @@ run;
# Installation
First, download the repo to a location your SAS system can access. Then update your sasautos path to include the components you wish to have available,eg:
First, download the repo to a location your SAS system can access. Then update your sasautos path to include the components you wish to have available, eg:
```sas
options insert=(sasautos="/your/path/macrocore/base");
@@ -149,3 +177,43 @@ When contributing to this library, it is therefore important to ensure that all
# General Notes
- All macros should be compatible with SAS versions from support level B and above (so currently 9.2 and later). If an earlier version is not supported, then the macro should say as such in the header documentation, and exit gracefully (eg `%if %sysevalf(&sysver<9.3) %then %return`).
## Star Gazing
If you find this library useful, please leave a [star](https://github.com/sasjs/core/stargazers) and help us grow our star graph!
![](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-8-orange.svg?style=flat-square)](#contributors-)
<!-- ALL-CONTRIBUTORS-BADGE:END -->
Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)):
<!-- ALL-CONTRIBUTORS-LIST:START - Do not remove or modify this section -->
<!-- prettier-ignore-start -->
<!-- markdownlint-disable -->
<table>
<tr>
<td align="center"><a href="https://github.com/allanbowe"><img src="https://avatars.githubusercontent.com/u/4420615?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Allan Bowe</b></sub></a><br /><a href="#business-allanbowe" title="Business development">💼</a> <a href="https://github.com/sasjs/core/commits?author=allanbowe" title="Code">💻</a> <a href="#content-allanbowe" title="Content">🖋</a> <a href="https://github.com/sasjs/core/commits?author=allanbowe" title="Documentation">📖</a> <a href="#infra-allanbowe" title="Infrastructure (Hosting, Build-Tools, etc)">🚇</a> <a href="#maintenance-allanbowe" title="Maintenance">🚧</a> <a href="#mentoring-allanbowe" title="Mentoring">🧑‍🏫</a> <a href="#question-allanbowe" title="Answering Questions">💬</a> <a href="https://github.com/sasjs/core/pulls?q=is%3Apr+reviewed-by%3Aallanbowe" title="Reviewed Pull Requests">👀</a> <a href="https://github.com/sasjs/core/commits?author=allanbowe" title="Tests">⚠️</a></td>
<td align="center"><a href="https://github.com/rafgag"><img src="https://avatars.githubusercontent.com/u/69139928?v=4?s=100" width="100px;" alt=""/><br /><sub><b>rafgag</b></sub></a><br /><a href="https://github.com/sasjs/core/commits?author=rafgag" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/tmoody"><img src="https://avatars.githubusercontent.com/u/79837106?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Trevor Moody</b></sub></a><br /><a href="https://github.com/sasjs/core/commits?author=tmoody" title="Code">💻</a></td>
<td align="center"><a href="https://krishna-acondy.io/"><img src="https://avatars.githubusercontent.com/u/2980428?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Krishna Acondy</b></sub></a><br /><a href="https://github.com/sasjs/core/commits?author=krishna-acondy" title="Code">💻</a> <a href="#infra-krishna-acondy" title="Infrastructure (Hosting, Build-Tools, etc)">🚇</a> <a href="#blog-krishna-acondy" title="Blogposts">📝</a> <a href="#content-krishna-acondy" title="Content">🖋</a> <a href="#ideas-krishna-acondy" title="Ideas, Planning, & Feedback">🤔</a> <a href="#video-krishna-acondy" title="Videos">📹</a></td>
<td align="center"><a href="https://github.com/saadjutt01"><img src="https://avatars.githubusercontent.com/u/8914650?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Muhammad Saad </b></sub></a><br /><a href="https://github.com/sasjs/core/commits?author=saadjutt01" title="Code">💻</a> <a href="#ideas-saadjutt01" title="Ideas, Planning, & Feedback">🤔</a></td>
<td align="center"><a href="https://www.erudicat.com/"><img src="https://avatars.githubusercontent.com/u/25773492?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Yury Shkoda</b></sub></a><br /><a href="https://github.com/sasjs/core/commits?author=YuryShkoda" title="Code">💻</a> <a href="#infra-YuryShkoda" title="Infrastructure (Hosting, Build-Tools, etc)">🚇</a> <a href="#video-YuryShkoda" title="Videos">📹</a></td>
<td align="center"><a href="https://github.com/medjedovicm"><img src="https://avatars.githubusercontent.com/u/18329105?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Mihajlo Medjedovic</b></sub></a><br /><a href="#infra-medjedovicm" title="Infrastructure (Hosting, Build-Tools, etc)">🚇</a></td>
</tr>
<tr>
<td align="center"><a href="https://github.com/kkchandok"><img src="https://avatars.githubusercontent.com/u/46090627?v=4?s=100" width="100px;" alt=""/><br /><sub><b>kkchandok</b></sub></a><br /><a href="#ideas-kkchandok" title="Ideas, Planning, & Feedback">🤔</a></td>
</tr>
</table>
<!-- markdownlint-restore -->
<!-- prettier-ignore-end -->
<!-- ALL-CONTRIBUTORS-LIST:END -->
This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome!

18
SECURITY.md Normal file
View File

@@ -0,0 +1,18 @@
# Security Policy
Security is an extremely high priority when it comes to the SASjs product suite. We take a number of steps across all repositories to minimise risk, such as:
* Regular dependabot updates
* Snyk reports
* Minimising dependencies, especially production dependencies (sasjs/core has NONE)
* Testing & Code review process
## Supported Versions
We support only the latest version
## Reporting a Vulnerability
We welcome disclosures of all kinds in relation to all the SASjs libraries. You can submit them here: [https://sasapps.io/contact-us](https://sasapps.io/contact-us)

2262
all.sas

File diff suppressed because it is too large Load Diff

View File

@@ -1,18 +1,17 @@
/**
@file
@brief to be deprecated
@details We will deprecate this macro in 2022
@brief Abort, ungracefully
@details Will abort with a straightforward %abort if the condition is true.
As you can see, it's not a macro function.
Use mp_abort.sas instead.
<h4> Related Macros </h4>
@li mp_abort.sas
@version 9.2
@author Allan Bowe
@cond
**/
%macro mf_abort(mac=mf_abort.sas, type=, msg=, iftrue=%str(1=1)
%macro mf_abort(mac=mf_abort.sas, type=deprecated, msg=, iftrue=%str(1=1)
)/*/STORE SOURCE*/;
%if not(%eval(%unquote(&iftrue))) %then %return;
@@ -21,116 +20,8 @@
%if %length(&mac)>0 %then %put NOTE- called by &mac;
%put NOTE - &msg;
/* Stored Process Server web app context */
%if %symexist(_metaperson) or "&SYSPROCESSNAME"="Compute Server" %then %do;
options obs=max replace nosyntaxcheck mprint;
/* extract log err / warn, if exist */
%local logloc logline;
%global logmsg; /* capture global messages */
%if %symexist(SYSPRINTTOLOG) %then %let logloc=&SYSPRINTTOLOG;
%else %let logloc=%qsysfunc(getoption(LOG));
proc printto log=log;run;
%if %length(&logloc)>0 %then %do;
%let logline=0;
data _null_;
infile &logloc lrecl=5000;
input; putlog _infile_;
i=1;
retain logonce 0;
if (
_infile_=:"%str(WARN)ING" or _infile_=:"%str(ERR)OR"
) and logonce=0
then do;
call symputx('logline',_n_);
logonce+1;
end;
run;
/* capture log including lines BEFORE the err */
%if &logline>0 %then %do;
data _null_;
infile &logloc lrecl=5000;
input;
i=1;
stoploop=0;
if _n_ ge &logline-5 and stoploop=0 then do until (i>12);
call symputx('logmsg',catx('\n',symget('logmsg'),_infile_));
input;
i+1;
stoploop=1;
end;
if stoploop=1 then stop;
run;
%end;
%end;
%abort;
/* send response in SASjs JSON format */
data _null_;
file _webout mod lrecl=32000;
length msg $32767;
sasdatetime=datetime();
msg=cats(symget('msg'),'\n\nLog Extract:\n',symget('logmsg'));
/* escape the quotes */
msg=tranwrd(msg,'"','\"');
/* ditch the CRLFs as chrome complains */
msg=compress(msg,,'kw');
/* quote without quoting the quotes (which are escaped instead) */
msg=cats('"',msg,'"');
if symexist('_debug') then debug=symget('_debug');
if debug ge 131 then put '>>weboutBEGIN<<';
put '{"START_DTTM" : "' "%sysfunc(datetime(),datetime20.3)" '"';
put ',"sasjsAbort" : [{';
put ' "MSG":' msg ;
put ' ,"MAC": "' "&mac" '"}]';
put ",""SYSUSERID"" : ""&sysuserid"" ";
if symexist('_metauser') then do;
_METAUSER=quote(trim(symget('_METAUSER')));
put ",""_METAUSER"": " _METAUSER;
_METAPERSON=quote(trim(symget('_METAPERSON')));
put ',"_METAPERSON": ' _METAPERSON;
end;
_PROGRAM=quote(trim(resolve(symget('_PROGRAM'))));
put ',"_PROGRAM" : ' _PROGRAM ;
put ",""SYSCC"" : ""&syscc"" ";
put ",""SYSERRORTEXT"" : ""&syserrortext"" ";
put ",""SYSJOBID"" : ""&sysjobid"" ";
put ",""SYSWARNINGTEXT"" : ""&syswarningtext"" ";
put ',"END_DTTM" : "' "%sysfunc(datetime(),datetime20.3)" '" ';
put "}" @;
%if &_debug ge 131 %then %do;
put '>>weboutEND<<';
%end;
run;
%let syscc=0;
%if %symexist(SYS_JES_JOB_URI) %then %do;
/* refer web service output to file service in one hit */
filename _webout filesrvc parenturi="&SYS_JES_JOB_URI"
name="_webout.json";
%let rc=%sysfunc(fcopy(_web,_webout));
%end;
%else %do;
data _null_;
if symexist('sysprocessmode')
then if symget("sysprocessmode")="SAS Stored Process Server"
then rc=stpsrvset('program error', 0);
run;
%end;
/**
* endsas is reliable but kills some deployments.
* Abort variants are ungraceful (non zero return code)
* This approach lets SAS run silently until the end :-)
*/
%put _all_;
filename skip temp;
data _null_;
file skip;
put '%macro skip(); %macro skippy();';
run;
%inc skip;
%end;
%else %do;
%put _all_;
%abort cancel;
%end;
%mend;
%mend mf_abort;
/** @endcond */

View File

@@ -23,4 +23,4 @@
%if %sysfunc(exist(&libds)) ne 1 & %sysfunc(exist(&libds,VIEW)) ne 1 %then 0;
%else 1;
%mend;
%mend mf_existds;

View File

@@ -42,6 +42,6 @@
-1
%put &sysmacroname: &feature not found;
%end;
%mend;
%mend mf_existfeature;
/** @endcond */

View File

@@ -17,11 +17,17 @@
%macro mf_existfileref(fref
)/*/STORE SOURCE*/;
%if %sysfunc(fileref(&fref))=0 %then %do;
%local rc;
%let rc=%sysfunc(fileref(&fref));
%if &rc=0 %then %do;
1
%end;
%else %if &rc<0 %then %do;
%put &sysmacroname: Fileref &fref exists but the underlying file does not;
1
%end;
%else %do;
0
%end;
%mend;
%mend mf_existfileref;

37
base/mf_existfunction.sas Normal file
View File

@@ -0,0 +1,37 @@
/**
@file
@brief Checks if a function exists
@details Returns 1 if the function exists, else 0. Note that this function
can be slow as it needs to open the sashelp.vfuncs table.
Usage:
%put %mf_existfunction(CAT);
%put %mf_existfunction(DOG);
Full credit to [Bart](https://sasensei.com/user/305) for the vfunc pointer
and the tidy approach for pure macro data set filtering.
Check out his [SAS Packages](https://github.com/yabwon/SAS_PACKAGES)
framework! Where you can find the same [function](
https://github.com/yabwon/SAS_PACKAGES/blob/main/packages/baseplus.md#functionexists-macro
).
@param [in] name (positional) - function name
@author Allan Bowe
**/
/** @cond */
%macro mf_existfunction(name
)/*/STORE SOURCE*/;
%local dsid rc exist;
%let dsid=%sysfunc(open(sashelp.vfunc(where=(fncname="%upcase(&name)"))));
%let exist=1;
%let exist=%sysfunc(fetch(&dsid, NOSET));
%let rc=%sysfunc(close(&dsid));
%sysevalf(0 = &exist)
%mend mf_existfunction;
/** @endcond */

View File

@@ -30,6 +30,6 @@
%let rc=%sysfunc(close(&dsid));
%end;
%mend;
%mend mf_existvar;
/** @endcond */

View File

@@ -54,6 +54,6 @@
0
%put Vars not found: &found;
%end;
%mend;
%mend mf_existvarlist;
/** @endcond */

73
base/mf_getapploc.sas Normal file
View File

@@ -0,0 +1,73 @@
/**
@file
@brief Returns the appLoc from the _program variable
@details When working with SASjs apps, web services / tests / jobs are always
deployed to a root (app) location in the SAS logical folder tree.
When building apps for use in other environments, you do not necessarily know
where the backend services will be deployed. Therefore a function like this
is handy in order to dynamically figure out the appLoc, and enable other
services to be connected by a relative reference.
SASjs apps always have the same immediate substructure (one or more of the
following):
@li /data
@li /jobs
@li /services
@li /tests/jobs
@li /tests/services
@li /tests/macros
This function works by testing for the existence of any of the above in the
automatic _program variable, and returning the part to the left of it.
Usage:
%put %mf_getapploc(&_program)
%put %mf_getapploc(/some/location/services/admin/myservice);
%put %mf_getapploc(/some/location/jobs/extract/somejob/);
%put %mf_getapploc(/some/location/tests/jobs/somejob/);
@author Allan Bowe
**/
%macro mf_getapploc(pgm);
%if "&pgm"="" %then %do;
%if %symexist(_program) %then %let pgm=&_program;
%else %do;
%put &sysmacroname: No value provided and no _program variable available;
%return;
%end;
%end;
%local root;
/**
* First check we are not in the tests/macros folder (which has no subfolders)
*/
%if %index(&pgm,/tests/macros/) %then %do;
%let root=%substr(&pgm,1,%index(&pgm,/tests/macros)-1);
&root
%return;
%end;
/**
* Next, move up two levels to avoid matches on subfolder or service name
*/
%let root=%substr(&pgm,1,%length(&pgm)-%length(%scan(&pgm,-1,/))-1);
%let root=%substr(&root,1,%length(&root)-%length(%scan(&root,-1,/))-1);
%if %index(&root,/tests/) %then %do;
%let root=%substr(&root,1,%index(&root,/tests/)-1);
%end;
%else %if %index(&root,/services) %then %do;
%let root=%substr(&root,1,%index(&root,/services)-1);
%end;
%else %if %index(&root,/jobs) %then %do;
%let root=%substr(&root,1,%index(&root,/jobs)-1);
%end;
%else %put &sysmacroname: Could not find an app location from &pgm;
&root
%mend mf_getapploc ;

View File

@@ -31,4 +31,4 @@
%sysfunc(attrc(&dsid,&attr))
%let rc=%sysfunc(close(&dsid));
%end;
%mend;
%mend mf_getattrc;

View File

@@ -31,4 +31,4 @@
%sysfunc(attrn(&dsid,&attr))
%let rc=%sysfunc(close(&dsid));
%end;
%mend;
%mend mf_getattrn;

View File

@@ -48,6 +48,6 @@
&engine
%mend;
%mend mf_getengine;
/** @endcond */

View File

@@ -44,4 +44,4 @@
%sysfunc(INPUTN(&bytes, best.),sizekmg.)
%end;
%mend ;
%mend mf_getfilesize ;

View File

@@ -29,4 +29,4 @@
&valc
%end;
%else %put %str(ERR)OR: Unable to find key &key in ds &libds;
%mend;
%mend mf_getkeyvalue;

View File

@@ -62,4 +62,4 @@
%else %if &switch=VIYARESTAPI %then %do;
%mf_trimstr(%sysfunc(getoption(servicesbaseurl)),/)
%end;
%mend;
%mend mf_getplatform;

View File

@@ -50,4 +50,4 @@
&buffer
%mend;
%mend mf_getquotedstr;

View File

@@ -38,6 +38,6 @@
&schema
%mend;
%mend mf_getschema;
/** @endcond */

View File

@@ -34,4 +34,4 @@
%end;
%end;
%put unable to find available fileref in range &prefix.0-&maxtries;
%mend;
%mend mf_getuniquefileref;

View File

@@ -37,4 +37,4 @@
%end;
%end;
%put unable to find available libref in range &prefix.0-&maxtries;
%mend;
%mend mf_getuniquelibref;

View File

@@ -39,4 +39,4 @@
%quote(&user)
%mend;
%mend mf_getuser;

View File

@@ -30,4 +30,4 @@
%trim(&&&variable)
%end;
%mend;
%mend mf_getvalue;

View File

@@ -29,4 +29,4 @@
%let rc=%sysfunc(close(&dsid));
%end;
&nvars
%mend;
%mend mf_getvarcount;

View File

@@ -49,4 +49,4 @@
%let rc = %sysfunc(close(&dsid));
/* Return variable format */
&vlen
%mend;
%mend mf_getVarLen;

View File

@@ -66,7 +66,8 @@
%let rc=%sysfunc(close(&dsid));
%end;
%else %do;
%put unable to open &libds (rc=&dsid);
%put &sysmacroname: Unable to open &libds (rc=&dsid);
%put &sysmacroname: SYSMSG= %sysfunc(sysmsg());
%let rc=%sysfunc(close(&dsid));
%end;
&outvar

View File

@@ -51,4 +51,4 @@ returns:
/* Return variable number */
&vnum.
%mend;
%mend mf_getVarNum;

View File

@@ -40,4 +40,4 @@
&engine
%mend;
%mend mf_getxengine;

View File

@@ -24,4 +24,4 @@
%sysevalf(%superq(param)=,boolean)
%mend;
%mend mf_isblank;

View File

@@ -31,4 +31,4 @@
&is_directory
%mend;
%mend mf_isdir;

View File

@@ -26,4 +26,4 @@
&root
%end;
%mend;
%mend mf_loc;

View File

@@ -64,4 +64,4 @@ Usage:
%end;
%end;
/* exit quietly if directory did exist.*/
%mend;
%mend mf_mkdir;

View File

@@ -16,4 +16,4 @@
%if %symexist(&var) %then %do;
%superq(&var)
%end;
%mend;
%mend mf_mval;

View File

@@ -23,4 +23,4 @@
%macro mf_nobs(libds
)/*/STORE SOURCE*/;
%mf_getattrn(&libds,NLOBS)
%mend;
%mend mf_nobs;

View File

@@ -47,4 +47,4 @@
&basestr
%end;
%mend;
%mend mf_trimstr;

View File

@@ -62,4 +62,4 @@
%else %mf_abort(mac=mf_verifymacvars,type=&mabort,msg=&abortmsg);
%exit_success:
%mend;
%mend mf_verifymacvars;

View File

@@ -50,5 +50,5 @@
&outvar
%mend;
%mend mf_wordsInStr1ButNotStr2;

View File

@@ -74,7 +74,7 @@
input;
i=1;
stoploop=0;
if _n_ ge &logline-5 and stoploop=0 then do until (i>12);
if _n_ ge &logline-15 and stoploop=0 then do until (i>22);
call symputx('logmsg',catx('\n',symget('logmsg'),_infile_));
input;
i+1;
@@ -99,7 +99,7 @@
/* send response in SASjs JSON format */
data _null_;
file _webout mod lrecl=32000;
file _webout mod lrecl=32000 encoding='utf-8';
length msg $32767 debug $8;
sasdatetime=datetime();
msg=cats(symget('msg'),'\n\nLog Extract:\n',symget('logmsg'));
@@ -133,6 +133,8 @@
put ",""SYSCC"" : ""&syscc"" ";
put ",""SYSERRORTEXT"" : ""&syserrortext"" ";
put ",""SYSJOBID"" : ""&sysjobid"" ";
sysvlong=quote(trim(symget('sysvlong')));
put ',"SYSVLONG" : ' sysvlong;
put ",""SYSWARNINGTEXT"" : ""&syswarningtext"" ";
put ',"END_DTTM" : "' "%sysfunc(datetime(),datetime20.3)" '" ';
put "}" @;
@@ -163,6 +165,7 @@
sysuserid=symget('sysuserid');
iftrue=symget('iftrue');
put (_all_)(/=);
call symputx('syscc',0);
abort cancel nolist;
run;
%end;

View File

@@ -142,4 +142,4 @@
proc sql;
drop table &ds;
%mend;
%mend mp_assertcols;

View File

@@ -144,4 +144,4 @@
proc sql;
drop table &ds;
%mend;
%mend mp_assertcolvals;

View File

@@ -18,7 +18,11 @@
@param [in] test= (HASOBS) The test to apply. Valid values are:
@li HASOBS - Test is a PASS if the input dataset has any observations
@li EMPTY - Test is a PASS if input dataset is empty
@li EQUALS [integer] - Test passes if obs count matches the provided integer
@li EQUALS [integer] - Test passes if row count matches the provided integer
@LI ATLEAST [integer] - Test passes if row count is more than or equal to
the provided integer
@LI ATMOST [integer] - Test passes if row count is less than or equal to
the provided integer
@param [out] outds= (work.test_results) The output dataset to contain the
results. If it does not exist, it will be created, with the following format:
|TEST_DESCRIPTION:$256|TEST_RESULT:$4|TEST_COMMENTS:$256|
@@ -27,6 +31,8 @@
<h4> Related Macros </h4>
@li mp_assertcolvals.sas
@li mp_assert.sas
@li mp_assertcols.sas
@version 9.2
@author Allan Bowe
@@ -51,6 +57,22 @@
)
%let test=EQUALS;
%end;
%else %if %substr(&test.xxxxxxx,1,7)=ATLEAST %then %do;
%let val=%scan(&test,2,%str( ));
%mp_abort(iftrue= (%DATATYP(&val)=CHAR)
,mac=&sysmacroname
,msg=%str(Invalid test - &test, expected ATLEAST [integer])
)
%let test=ATLEAST;
%end;
%else %if %substr(&test.xxxxxxx,1,7)=ATMOST %then %do;
%let val=%scan(&test,2,%str( ));
%mp_abort(iftrue= (%DATATYP(&val)=CHAR)
,mac=&sysmacroname
,msg=%str(Invalid test - &test, expected ATMOST [integer])
)
%let test=ATMOST;
%end;
%else %if &test ne HASOBS and &test ne EMPTY %then %do;
%mp_abort(
mac=&sysmacroname,
@@ -62,7 +84,8 @@
length test_description $256 test_result $4 test_comments $256;
test_description=symget('desc');
test_result='FAIL';
test_comments="&sysmacroname: Dataset &inds has &nobs observations";
test_comments="&sysmacroname: Dataset &inds has &nobs observations.";
test_comments=test_comments!!" Test was "!!symget('test');
%if &test=HASOBS %then %do;
if &nobs>0 then test_result='PASS';
%end;
@@ -72,6 +95,12 @@
%else %if &test=EQUALS %then %do;
if &nobs=&val then test_result='PASS';
%end;
%else %if &test=ATLEAST %then %do;
if &nobs ge &val then test_result='PASS';
%end;
%else %if &test=ATMOST %then %do;
if &nobs le &val then test_result='PASS';
%end;
%else %do;
test_comments="&sysmacroname: Unsatisfied test condition - &test";
%end;
@@ -86,4 +115,4 @@
proc sql;
drop table &ds;
%mend;
%mend mp_assertdsobs;

117
base/mp_base64copy.sas Normal file
View File

@@ -0,0 +1,117 @@
/**
@file
@brief Convert a file to/from base64 format
@details Creates a new version of a file either encoded or decoded using
Base64. Inspired by this post by Michael Dixon:
https://support.selerity.com.au/hc/en-us/articles/223345708-Tip-SAS-and-Base64
Usage:
filename tmp temp;
data _null_;
file tmp;
put 'base ik ally';
run;
%mp_base64copy(inref=tmp, outref=myref, action=ENCODE)
data _null_;
infile myref;
input;
put _infile_;
run;
%mp_base64copy(inref=myref, outref=mynewref, action=DECODE)
data _null_;
infile mynewref;
input;
put _infile_;
run;
@param [in] inref= Fileref of the input file (should exist)
@param [out] outref= Output fileref. If it does not exist, it is created.
@param [in] action= (ENCODE) The action to take. Valid values:
@li ENCODE - Convert the file to base64 format
@li DECODE - Decode the file from base64 format
@version 9.2
@author Allan Bowe, source: https://github.com/sasjs/core
<h4> SAS Macros </h4>
@li mp_abort.sas
**/
%macro mp_base64copy(
inref=0,
outref=0,
action=ENCODE
)/*/STORE SOURCE*/;
%let inref=%upcase(&inref);
%let outref=%upcase(&outref);
%let action=%upcase(&action);
%local infound outfound;
%let infound=0;
%let outfound=0;
data _null_;
set sashelp.vextfl(where=(fileref="&inref" or fileref="&outref"));
if fileref="&inref" then call symputx('infound',1,'l');
if fileref="&outref" then call symputx('outfound',1,'l');
run;
%mp_abort(iftrue= (&infound=0)
,mac=&sysmacroname
,msg=%str(INREF &inref NOT FOUND!)
)
%mp_abort(iftrue= (&outref=0)
,mac=&sysmacroname
,msg=%str(OUTREF NOT PROVIDED!)
)
%mp_abort(iftrue= (&action ne ENCODE and &action ne DECODE)
,mac=&sysmacroname
,msg=%str(Invalid action! Should be ENCODE OR DECODE)
)
%if &outfound=0 %then %do;
filename &outref temp lrecl=2097088;
%end;
%if &action=ENCODE %then %do;
data _null_;
length b64 $ 76 line $ 57;
retain line "";
infile &inref recfm=F lrecl= 1 end=eof;
input @1 stream $char1.;
file &outref recfm=N;
substr(line,(_N_-(CEIL(_N_/57)-1)*57),1) = byte(rank(stream));
if mod(_N_,57)=0 or EOF then do;
if eof then b64=put(trim(line),$base64X76.);
else b64=put(line, $base64X76.);
put b64 + (-1) @;
line="";
end;
run;
%end;
%else %if &action=DECODE %then %do;
data _null_;
length filein 8 fileout 8;
filein = fopen("&inref",'I',4,'B');
fileout = fopen("&outref",'O',3,'B');
char= '20'x;
do while(fread(filein)=0);
length raw $4;
do i=1 to 4;
rc=fget(filein,char,1);
substr(raw,i,1)=char;
end;
rc = fput(fileout,input(raw,$base64X4.));
rc = fwrite(fileout);
end;
rc = fclose(filein);
rc = fclose(fileout);
run;
%end;
%mend mp_base64copy;

View File

@@ -9,10 +9,29 @@
%mp_binarycopy(inloc="/home/me/blah.txt", outref=_webout)
@param inloc full, quoted "path/and/filename.ext" of the object to be copied
@param outloc full, quoted "path/and/filename.ext" of object to be created
@param inref can override default input fileref to avoid naming clash
@param outref an override default output fileref to avoid naming clash
To append to a file, use the mode option, eg:
filename tmp1 temp;
filename tmp2 temp;
data _null_;
file tmp1;
put 'stacking';
run;
%mp_binarycopy(inref=tmp1, outref=tmp2, mode=APPEND)
%mp_binarycopy(inref=tmp1, outref=tmp2, mode=APPEND)
@param [in] inloc quoted "path/and/filename.ext" of the file to be copied
@param [out] outloc quoted "path/and/filename.ext" of the file to be created
@param [in] inref (____in) If provided, this fileref will take precedence over
the `inloc` parameter
@param [out] outref (____in) If provided, this fileref will take precedence
over the `outloc` parameter. It must already exist!
@param [in] mode (CREATE) Valid values:
@li CREATE - Create the file (even if it already exists)
@li APPEND - Append to the file (don't overwrite)
@returns nothing
@version 9.2
@@ -24,20 +43,29 @@
,outloc= /* full path and filename of object to be created */
,inref=____in /* override default to use own filerefs */
,outref=____out /* override default to use own filerefs */
,mode=CREATE
)/*/STORE SOURCE*/;
%local mod outmode;
%if &mode=APPEND %then %do;
%let mod=mod;
%let outmode='a';
%end;
%else %do;
%let outmode='o';
%end;
/* these IN and OUT filerefs can point to anything */
%if &inref = ____in %then %do;
filename &inref &inloc lrecl=1048576 ;
%end;
%if &outref=____out %then %do;
filename &outref &outloc lrecl=1048576 ;
filename &outref &outloc lrecl=1048576 &mod;
%end;
/* copy the file byte-for-byte */
data _null_;
length filein 8 fileid 8;
filein = fopen("&inref",'I',1,'B');
fileid = fopen("&outref",'O',1,'B');
fileid = fopen("&outref",&outmode,1,'B');
rec = '20'x;
do while(fread(filein)=0);
rc = fget(filein,rec,1);
@@ -53,4 +81,4 @@
%if &outref=____out %then %do;
filename &outref clear;
%end;
%mend;
%mend mp_binarycopy;

View File

@@ -67,5 +67,5 @@
else put inchar $char1.;
end;
run;
%mend;
%mend mp_cleancsv;
/** @endcond */

View File

@@ -64,4 +64,4 @@ data &outds;
output;
run;
%mend;
%mend mp_createconstraints;

View File

@@ -80,4 +80,4 @@ Usage:
)
%end;
%mend;
%mend mp_createwebservice;

View File

@@ -141,4 +141,4 @@ data &outds
%end;
run;
%mend;
%mend mp_csv2ds;

View File

@@ -49,4 +49,4 @@ data &outds;
end;
run;
%mend;
%mend mp_deleteconstraints;

View File

@@ -167,4 +167,4 @@ run;
by filepath file_or_folder filename ext ;
run;
%end;
%mend;
%mend mp_dirlist;

View File

@@ -47,4 +47,4 @@
%end;
as &outvar length=&varlen
from &libds;
%mend;
%mend mp_distinctfmtvalues;

View File

@@ -1,20 +1,24 @@
/**
@file
@brief Drops tables / views (if they exist) without warnings in the log
@details
@details Useful for dropping tables when you're not sure they exist, or if
you are not sure whether they are a dataset or view. Also efficient for
dropping multiple tables / views.
Example usage:
proc sql;
create table data1 as select * from sashelp.class;
create view view2 as select * from sashelp.class;
%mp_dropmembers(list=data1 view2)
%mp_dropmembers(data1 view2, libref=WORK)
<h4> SAS Macros </h4>
@li mf_isblank.sas
@param list space separated list of datasets / views
@param libref= can only drop from a single library at a time
@param list space separated list of datasets / views, WITHOUT libref
@param libref= (WORK) Note - you can only drop from a single library at a time
@version 9.2
@author Allan Bowe
@@ -35,4 +39,4 @@
delete &list;
delete &list /mtype=view;
run;
%mend;
%mend mp_dropmembers;

View File

@@ -251,4 +251,4 @@ quit;
%put NOTE-;%put NOTE-;
%put NOTE- %sysfunc(dequote(&cards_file.));
%put NOTE-;%put NOTE-;
%mend;
%mend mp_ds2cards;

View File

@@ -55,4 +55,4 @@ data _null_;
run;
%mend;
%mend mp_ds2csv;

179
base/mp_ds2inserts.sas Normal file
View File

@@ -0,0 +1,179 @@
/**
@file
@brief Export a dataset to SQL insert statements
@details Converts dataset values to SQL insert statements for use across
multiple database types.
Usage:
%mp_ds2inserts(sashelp.class,outref=myref,outds=class)
data class;
set sashelp.class;
stop;
proc sql;
%inc myref;
@param [in] ds The dataset to be exported
@param [in] maxobs= (max) The max number of inserts to create
@param [out] outref= (0) The output fileref. If it does not exist, it is
created. If it does exist, new records are APPENDED.
@param [out] schema= (0) The library (or schema) in which the target table is
located. If not provided, is ignored.
@param [out] outds= (0) The output table to load. If not provided, will
default to the table in the &ds parameter.
@param [in] flavour= (SAS) The SQL flavour to be applied to the output. Valid
options:
@li SAS (default) - suitable for regular proc sql
@li PGSQL - Used for Postgres databases
@param [in] applydttm= (YES) If YES, any columns using datetime formats will
be converted to native DB datetime literals
<h4> SAS Macros </h4>
@li mf_existfileref.sas
@li mf_getvarcount.sas
@li mf_getvarformat.sas
@li mf_getvarlist.sas
@li mf_getvartype.sas
@version 9.2
@author Allan Bowe (credit mjsq)
**/
%macro mp_ds2inserts(ds, outref=0,schema=0,outds=0,flavour=SAS,maxobs=max
,applydttm=YES
)/*/STORE SOURCE*/;
%if not %sysfunc(exist(&ds)) %then %do;
%put %str(WAR)NING: &ds does not exist;
%return;
%end;
%if not %sysfunc(exist(&ds)) %then %do;
%put %str(WAR)NING: &ds does not exist;
%return;
%end;
%if %index(&ds,.)=0 %then %let ds=WORK.&ds;
%let flavour=%upcase(&flavour);
%if &flavour ne SAS and &flavour ne PGSQL %then %do;
%put %str(WAR)NING: &flavour is not supported;
%return;
%end;
%if &outref=0 %then %do;
%put %str(WAR)NING: Please provide a fileref;
%return;
%end;
%if %mf_existfileref(&outref)=0 %then %do;
filename &outref temp lrecl=66000;
%end;
%if &schema=0 %then %let schema=;
%else %let schema=&schema..;
%if &outds=0 %then %let outds=%scan(&ds,2,.);
%local nobs;
proc sql noprint;
select count(*) into: nobs TRIMMED from &ds;
%if &nobs=0 %then %do;
data _null_;
file &outref mod;
put "/* No rows found in &ds */";
run;
%end;
%local vars;
%let vars=%mf_getvarcount(&ds);
%if &vars=0 %then %do;
data _null_;
file &outref mod;
put "/* No columns found in &schema.&ds */";
run;
%return;
%end;
%else %if &vars>1600 and &flavour=PGSQL %then %do;
data _null_;
file &fref mod;
put "/* &schema.&ds contains &vars vars */";
put "/* Postgres cannot handle tables with over 1600 vars */";
put "/* No inserts will be generated for this table */";
run;
%return;
%end;
%local varlist varlistcomma;
%let varlist=%mf_getvarlist(&ds);
%let varlistcomma=%mf_getvarlist(&ds,dlm=%str(,),quote=double);
/* next, export data */
data _null_;
file &outref mod ;
if _n_=1 then put "/* &schema.&outds (&nobs rows, &vars columns) */";
set &ds;
%if &maxobs ne max %then %do;
if _n_>&maxobs then stop;
%end;
length _____str $32767;
format _numeric_ best.;
format _character_ ;
%local i comma var vtype vfmt;
%do i=1 %to %sysfunc(countw(&varlist));
%let var=%scan(&varlist,&i);
%let vtype=%mf_getvartype(&ds,&var);
%let vfmt=%upcase(%mf_getvarformat(&ds,&var,force=1));
%if &i=1 %then %do;
%if &flavour=SAS %then %do;
put "insert into &schema.&outds set ";
put " &var="@;
%end;
%else %if &flavour=PGSQL %then %do;
_____str=cats(
"INSERT INTO &schema.&outds ("
,symget('varlistcomma')
,") VALUES ("
);
put _____str;
put " "@;
%end;
%end;
%else %do;
%if &flavour=SAS %then %do;
put " ,&var="@;
%end;
%else %if &flavour=PGSQL %then %do;
put " ,"@;
%end;
%end;
%if &vtype=N %then %do;
%if &flavour=SAS %then %do;
put &var;
%end;
%else %if &flavour=PGSQL %then %do;
if missing(&var) then put 'NULL';
%if &applydttm=YES and "%substr(&vfmt.xxxxxxxx,1,8)"="DATETIME"
%then %do;
else put "TIMESTAMP '" &var E8601DT25.6 "'";
%end;
%else %do;
else put &var;
%end;
%end;
%end;
%else %do;
_____str="'"!!trim(tranwrd(&var,"'","''"))!!"'";
put _____str;
%end;
%end;
%if &flavour=SAS %then %do;
put ';';
%end;
%else %if &flavour=PGSQL %then %do;
put ');';
%end;
if _n_=&nobs then put /;
run;
%mend mp_ds2inserts;

View File

@@ -33,12 +33,13 @@
@returns The &outds table containing any bad rows, plus a REASON_CD column.
@param [in] inds The table to be checked, with the format above
@param [in] targetds= The target dataset against which to verify VARIABLE_NM
@param [in] targetds= The target dataset against which to verify VARIABLE_NM.
This must be available (ie, the library must be assigned).
@param [out] abort= (YES) If YES will call mp_abort.sas on any exceptions
@param [out] outds= The output table, which is a copy of the &inds. table
plus a REASON_CD column, containing only bad records. If bad records found,
the SYSCC value will be set to 1008 (general data problem). Downstream
processes should check this table (and return code) before continuing.
plus a REASON_CD column, containing only bad records. If bad records found,
the SYSCC value will be set to 1008 (general data problem). Downstream
processes should check this table (and return code) before continuing.
<h4> SAS Macros </h4>
@li mp_abort.sas
@@ -84,41 +85,52 @@
* quotes, commas, periods and spaces.
* Only numeric values should remain
*/
%local reason_cd;
%local reason_cd nobs;
%let nobs=0;
data &outds;
/*length GROUP_LOGIC SUBGROUP_LOGIC $3 SUBGROUP_ID 8 VARIABLE_NM $32
OPERATOR_NM $10 RAW_VALUE $4000;*/
set &inds;
length reason_cd $32;
length reason_cd $4032;
/* closed list checks */
if GROUP_LOGIC not in ('AND','OR') then do;
REASON_CD='GROUP_LOGIC should be either AND or OR';
REASON_CD='GROUP_LOGIC should be AND/OR, not:'!!cats(GROUP_LOGIC);
putlog REASON_CD= GROUP_LOGIC=;
call symputx('reason_cd',reason_cd,'l');
call symputx('nobs',_n_,'l');
output;
end;
if SUBGROUP_LOGIC not in ('AND','OR') then do;
REASON_CD='SUBGROUP_LOGIC should be either AND or OR';
REASON_CD='SUBGROUP_LOGIC should be AND/OR, not:'!!cats(SUBGROUP_LOGIC);
putlog REASON_CD= SUBGROUP_LOGIC=;
call symputx('reason_cd',reason_cd,'l');
call symputx('nobs',_n_,'l');
output;
end;
if mod(SUBGROUP_ID,1) ne 0 then do;
REASON_CD='SUBGROUP_ID should be integer';
REASON_CD='SUBGROUP_ID should be integer, not '!!left(subgroup_id);
putlog REASON_CD= SUBGROUP_ID=;
call symputx('reason_cd',reason_cd,'l');
call symputx('nobs',_n_,'l');
output;
end;
if upcase(VARIABLE_NM) not in
(%upcase(%mf_getvarlist(&targetds,dlm=%str(,),quote=SINGLE)))
then do;
REASON_CD="VARIABLE_NM not in &targetds";
REASON_CD="Variable "!!cats(variable_nm)!!" not in &targetds";
putlog REASON_CD= VARIABLE_NM=;
call symputx('reason_cd',reason_cd,'l');
call symputx('nobs',_n_,'l');
output;
end;
if OPERATOR_NM not in
('=','>','<','<=','>=','BETWEEN','IN','NOT IN','NE','CONTAINS')
then do;
REASON_CD='Invalid OPERATOR_NM';
REASON_CD='Invalid OPERATOR_NM: '!!left(OPERATOR_NM);
putlog REASON_CD= OPERATOR_NM=;
call symputx('reason_cd',reason_cd,'l');
call symputx('nobs',_n_,'l');
output;
end;
@@ -128,8 +140,10 @@ data &outds;
if substr(raw_value,1,1) ne '('
or substr(cats(reverse(raw_value)),1,1) ne ')'
then do;
REASON_CD='Missing brackets in RAW_VALUE';
REASON_CD='Missing start/end bracket in RAW_VALUE';
putlog REASON_CD= OPERATOR_NM= raw_value= raw_value1= ;
call symputx('reason_cd',reason_cd,'l');
call symputx('nobs',_n_,'l');
output;
end;
else raw_value1=substr(raw_value,2,max(length(raw_value)-2,0));
@@ -150,27 +164,24 @@ data &outds;
/* output records that contain values other than digits and spaces */
if notdigit(compress(raw_value3,' '))>0 then do;
putlog raw_value3= $hex32.;
REASON_CD='Invalid RAW_VALUE';
REASON_CD=cats('Invalid RAW_VALUE:',raw_value);
putlog REASON_CD= raw_value= raw_value1= raw_value2= raw_value3=;
call symputx('reason_cd',reason_cd,'l');
call symputx('nobs',_n_,'l');
output;
end;
run;
%local nobs;
%let nobs=0;
data _null_;
set &outds end=last;
putlog (_all_)(=);
if last then do;
call symputx('REASON_CD',reason_cd,'l');
call symputx('nobs',_n_,'l');
end;
run;
%mp_abort(iftrue=(&abort=YES and &nobs>0),
mac=&sysmacroname,
msg=%str(&nobs filter issues in &inds, reason: &reason_cd, details in &outds)
msg=%str(Data issue: %superq(reason_cd))
)
%if &nobs>0 %then %do;

View File

@@ -99,4 +99,4 @@ filename &outref temp;
run;
%end;
%mend;
%mend mp_filtergenerate;

View File

@@ -78,7 +78,8 @@ run;
data &outds;
if &sqlrc or &syscc or &syserr then do;
REASON_CD=coalescec(symget('SYSERRORTEXT'),symget('SYSWARNINGTEXT'));
REASON_CD='VALIDATION_ERROR: '!!
coalescec(symget('SYSERRORTEXT'),symget('SYSWARNINGTEXT'));
output;
end;
else stop;
@@ -95,10 +96,11 @@ filename &fref1 clear;
run;
%mp_abort(
mac=&sysmacroname,
msg=%str(Filter issues in &inref: %quote(&reason_cd))
msg=%str(Filter validation issues. ERR=%superq(SYSERRORTEXT)
, WARN=%superq(SYSWARNINGTEXT) )
)
%end;
%let syscc=1008;
%end;
%mend;
%mend mp_filtervalidate;

View File

@@ -57,4 +57,4 @@ create table &outds as
%end;
;
%mend;
%mend mp_getconstraints;

View File

@@ -332,4 +332,4 @@ run;
run;
%end;
%mend;
%mend mp_getdbml;

View File

@@ -5,6 +5,8 @@
to create tables in SAS or a database. The macro can be used at table or
library level. The default behaviour is to create DDL in SAS format.
Note - views are not currently supported.
Usage:
data test(index=(pk=(x y)/unique /nomiss));
@@ -16,12 +18,14 @@
%mp_getddl(work,test,flavour=tsql,showlog=YES)
<h4> SAS Macros </h4>
@li mf_existfileref.sas
@li mf_getvarcount.sas
@li mp_getconstraints.sas
@param lib libref of the library to create DDL for. Should be assigned.
@param ds dataset to create ddl for (optional)
@param fref= the fileref to which to write the DDL. If not preassigned, will
be assigned to TEMP.
@param fref= the fileref to which to _append_ the DDL. If it does not exist,
it will be created.
@param flavour= The type of DDL to create (default=SAS). Supported=TSQL
@param showlog= Set to YES to show the DDL in the log
@param schema= Choose a preferred schema name (default is to use actual schema
@@ -37,9 +41,10 @@
)/*/STORE SOURCE*/;
/* check fileref is assigned */
%if %sysfunc(fileref(&fref)) > 0 %then %do;
filename &fref temp;
%if %mf_existfileref(&fref)=0 %then %do;
filename &fref temp ;
%end;
%if %length(&libref)=0 %then %let libref=WORK;
%let flavour=%upcase(&flavour);
@@ -47,6 +52,7 @@ proc sql noprint;
create table _data_ as
select * from dictionary.tables
where upcase(libname)="%upcase(&libref)"
and memtype='DATA' /* views not currently supported */
%if %length(&ds)>0 %then %do;
and upcase(memname)="%upcase(&ds)"
%end;
@@ -115,10 +121,10 @@ create table _data_ as
end;
run;
%put &=constraints_used;
%mend;
%mend addConst;
data _null_;
file &fref;
file &fref mod;
put "/* DDL generated by &sysuserid on %sysfunc(datetime(),datetime19.) */";
run;
@@ -141,13 +147,15 @@ run;
put "create table &libref..&curds(";
end;
else do;
/* just a placeholder - we filter out views at the top */
put "create view &libref..&curds(";
end;
put " "@@;
end;
else put " ,"@@;
if length(format)>1 then fmt=" format="!!cats(format);
if length(label)>1 then lab=" label="!!quote(trim(label));
if length(label)>1 then
lab=" label="!!cats("'",tranwrd(label,"'","''"),"'");
if notnull='yes' then notnul=' not null';
if type='char' then typ=cats('char(',length,')');
else if length ne 8 then typ='num length='!!left(length);
@@ -219,6 +227,7 @@ run;
put "create table [&schema].[&curds](";
end;
else do;
/* just a placeholder - we filter out views at the top */
put "create view [&schema].[&curds](";
end;
put " "@@;
@@ -302,71 +311,81 @@ run;
put "CREATE SCHEMA &schema;";
%do x=1 %to %sysfunc(countw(&dsnlist));
%let curds=%scan(&dsnlist,&x);
data _null_;
file &fref mod;
put "/* Postgres Flavour DDL for &schema..&curds */";
data _null_;
file &fref mod;
set &colinfo (where=(upcase(memname)="&curds")) end=last;
length fmt $32;
if _n_=1 then do;
if memtype='DATA' then do;
put "CREATE TABLE &schema..&curds (";
%local curdsvarcount;
%let curdsvarcount=%mf_getvarcount(&libref..&curds);
%if &curdsvarcount>1600 %then %do;
data _null_;
file &fref mod;
put "/* &libref..&curds contains &curdsvarcount vars */";
put "/* Postgres cannot create tables with over 1600 vars */";
put "/* No DDL will be generated for this table";
run;
%end;
%else %do;
data _null_;
file &fref mod;
put "/* Postgres Flavour DDL for &schema..&curds */";
data _null_;
file &fref mod;
set &colinfo (where=(upcase(memname)="&curds")) end=last;
length fmt $32;
if _n_=1 then do;
if memtype='DATA' then do;
put "CREATE TABLE &schema..&curds (";
end;
else do;
/* just a placeholder - we filter out views at the top */
put "CREATE VIEW &schema..&curds (";
end;
put " "@@;
end;
else do;
put "CREATE VIEW &schema..&curds (";
end;
put " "@@;
end;
else put " ,"@@;
format=upcase(format);
if 1=0 then; /* dummy if */
%if &applydttm=YES %then %do;
else if format=:'DATETIME' then fmt=' TIMESTAMP ';
%end;
else if type='num' then fmt=' DOUBLE PRECISION';
else fmt='VARCHAR('!!cats(length)!!')';
if notnull='yes' then notnul=' NOT NULL';
/* quote column names in case they represent reserved words */
name2=quote(trim(name));
put name2 fmt notnul;
run;
else put " ,"@@;
format=upcase(format);
if 1=0 then; /* dummy if */
%if &applydttm=YES %then %do;
else if format=:'DATETIME' then fmt=' TIMESTAMP ';
%end;
else if type='num' then fmt=' DOUBLE PRECISION';
else fmt='VARCHAR('!!cats(length)!!')';
if notnull='yes' then notnul=' NOT NULL';
/* quote column names in case they represent reserved words */
name2=quote(trim(name));
put name2 fmt notnul;
run;
/* Extra step for data constraints */
%addConst()
/* Extra step for data constraints */
%addConst()
data _null_;
file &fref mod;
put ');';
run;
data _null_;
file &fref mod;
put ');';
run;
/* Create Unique Indexes, but only if they were not already defined within
the Constraints section. */
data _null_;
*length ds $128;
set &idxinfo(
where=(
memname="&curds"
and unique='yes'
and indxname not in (
%sysfunc(tranwrd("&constraints_used",%str( ),%str(",")))
/* Create Unique Indexes, but only if they were not already defined within
the Constraints section. */
data _null_;
*length ds $128;
set &idxinfo(
where=(
memname="&curds"
and unique='yes'
and indxname not in (
%sysfunc(tranwrd("&constraints_used",%str( ),%str(",")))
)
)
)
);
file &fref mod;
by idxusage indxname;
/* ds=cats(libname,'.',memname); */
if first.indxname then do;
);
file &fref mod;
by idxusage indxname;
if first.indxname then do;
put 'CREATE UNIQUE INDEX "' indxname +(-1) '" ' "ON &schema..&curds(";
put ' "' name +(-1) '"' ;
end;
else put ' ,"' name +(-1) '"';
*else put ' ,' name ;
if last.indxname then do;
put ');';
end;
run;
end;
else put ' ,"' name +(-1) '"';
if last.indxname then do;
put ');';
end;
run;
%end;
%end;
%end;
%if %upcase(&showlog)=YES %then %do;
@@ -378,4 +397,4 @@ run;
run;
%end;
%mend;
%mend mp_getddl;

View File

@@ -70,4 +70,4 @@ create table &outds (rename=(
out=&outds(rename=(_name_=NAME COL1=MAXLEN));
run;
%mend;
%mend mp_getmaxvarlengths;

View File

@@ -301,4 +301,4 @@
%return;
%end;
%mend;
%mend mp_guesspk;

View File

@@ -20,6 +20,7 @@
@li mf_getvartype.sas
@param [in] libds dataset to hash
@param [in] salt= Provide a salt (could be, for instance, the dataset name)
@param [out] outds= (work.mf_hashdataset) The output dataset to create. This
will contain one column (hashkey) with one observation (a hex32.
representation of the input hash)
@@ -33,7 +34,8 @@
%macro mp_hashdataset(
libds,
outds=
outds=,
salt=
)/*/STORE SOURCE*/;
%if %mf_getattrn(&libds,NLOBS)=0 %then %do;
%put %str(WARN)ING: Dataset &libds is empty;, or is not a dataset;
@@ -53,7 +55,7 @@
%let varlist=%mf_getvarlist(&libds);
data &outds(rename=(&keyvar=hashkey) keep=&keyvar);
length &prevkeyvar &keyvar $32;
retain &prevkeyvar;
retain &prevkeyvar "%sysfunc(md5(%str(&salt)),$hex32.)";
set &libds end=&lastvar;
/* hash should include previous row */
&keyvar=put(md5(&prevkeyvar
@@ -72,4 +74,4 @@
if &lastvar then output;
run;
%end;
%mend;
%mend mp_hashdataset;

View File

@@ -61,7 +61,7 @@
)/*/STORE SOURCE*/;
%put output location=&jref;
%if &action=OPEN %then %do;
OPTIONS NOBOMFILE;
options nobomfile;
data _null_;file &jref encoding='utf-8';
put '{"START_DTTM" : "' "%sysfunc(datetime(),datetime20.3)" '"';
run;
@@ -102,7 +102,8 @@
%let fmtds=%scan(&syslast,2,.);
/* prepare formats and varnames */
data _null_;
set &fmtds end=last;
if _n_=1 then call symputx('nobs',nobs,'l');
set &fmtds end=last nobs=nobs;
name=upcase(name);
/* fix formats */
if type=2 or type=6 then do;
@@ -128,7 +129,6 @@
call symputx(cats('len',_n_),newlen,'l');
call symputx(cats('fmt',_n_),fmt,'l');
call symputx(cats('type',_n_),type,'l');
if last then call symputx('nobs',_n_,'l');
run;
data &fmtds;
/* rename on entry */
@@ -147,7 +147,7 @@
run;
%let ds=&fmtds;
%end; /* &fmt=Y */
data _null_;file &jref mod ;
data _null_;file &jref mod encoding='utf-8';
put "["; call symputx('cols',0,'l');
proc sort
data=sashelp.vcolumn(where=(libname='WORK' & memname="%upcase(&ds)"))

View File

@@ -75,4 +75,4 @@ select distinct lowcase(memname)
)
%end;
%mend;
%mend mp_lib2cards;

77
base/mp_lib2inserts.sas Normal file
View File

@@ -0,0 +1,77 @@
/**
@file
@brief Convert all data in a library to SQL insert statements
@details Gets list of members then calls the <code>%mp_ds2inserts()</code>
macro.
Usage:
%mp_getddl(sashelp, schema=work, fref=tempref)
%mp_lib2inserts(sashelp, schema=work, outref=tempref)
%inc tempref;
The output will be one file in the outref fileref.
<h4> SAS Macros </h4>
@li mp_ds2inserts.sas
@param [in] lib Library in which to convert all datasets to inserts
@param [in] flavour= (SAS) The SQL flavour to be applied to the output. Valid
options:
@li SAS (default) - suitable for regular proc sql
@li PGSQL - Used for Postgres databases
@param [in] maxobs= (max) The max number of observations (per table) to create
@param [out] outref= Output fileref in which to create the insert statements.
If it exists, it will be appended to, otherwise it will be created.
@param [out] schema= (0) The schema of the target database, or the libref.
@param [in] applydttm= (YES) If YES, any columns using datetime formats will
be converted to native DB datetime literals
@version 9.2
@author Allan Bowe
**/
%macro mp_lib2inserts(lib
,flavour=SAS
,outref=0
,schema=0
,maxobs=max
,applydttm=YES
)/*/STORE SOURCE*/;
/* Find the tables */
%local x ds memlist;
proc sql noprint;
select distinct lowcase(memname)
into: memlist
separated by ' '
from dictionary.tables
where upcase(libname)="%upcase(&lib)"
and memtype='DATA'; /* exclude views */
%let flavour=%upcase(&flavour);
%if &flavour ne SAS and &flavour ne PGSQL %then %do;
%put %str(WAR)NING: &flavour is not supported;
%return;
%end;
/* create the inserts */
%do x=1 %to %sysfunc(countw(&memlist));
%let ds=%scan(&memlist,&x);
%mp_ds2inserts(&lib..&ds
,outref=&outref
,schema=&schema
,outds=&ds
,flavour=&flavour
,maxobs=&maxobs
,applydttm=&applydttm
)
%end;
%mend mp_lib2inserts;

View File

@@ -39,4 +39,4 @@
,dttm=%sysfunc(datetime());
quit;
%mend;
%mend mp_perflog;

View File

@@ -85,4 +85,4 @@
"with record &record and " _n_=;
%end;
%mend;
%mend mp_prevobs;

View File

@@ -87,4 +87,4 @@ insert into &outds select distinct * from &append_ds;
)
%end;
%mend;
%mend mp_recursivejoin;

View File

@@ -30,4 +30,4 @@ data _null_;
end;
run;
%mend;
%mend mp_resetoption;

View File

@@ -46,4 +46,4 @@
%mend;
%mend mp_runddl;

View File

@@ -9,7 +9,15 @@
%mp_searchcols(libs=sashelp work, cols=name sex age)
@param libs=
@param libs=(SASHELP) Space separated list of libraries to search for columns
@param cols= Space separated list of column names to search for (not case
sensitive)
@param outds=(mp_searchcols) the table to create with the results. Will have
one line per table match.
@param match=(ANY) The match type. Valid values:
@li ANY - The table contains at least one of the columns
@li WILD - The table contains a column with a name that partially matches
@version 9.2
@author Allan Bowe
**/
@@ -17,6 +25,7 @@
%macro mp_searchcols(libs=sashelp
,cols=
,outds=mp_searchcols
,match=ANY
)/*/STORE SOURCE*/;
%put &sysmacroname process began at %sysfunc(datetime(),datetime19.);
@@ -38,8 +47,10 @@ create table _data_ as
%end;
order by 1,2,3;
%local tempds;
%let tempds=&syslast;
data &outds;
set &syslast;
set &tempds;
length cols matchcols $32767;
cols=upcase(symget('cols'));
colcount=countw(cols);
@@ -53,10 +64,29 @@ data &outds;
retain matchcols;
matchcols='';
end;
%if &match=ANY %then %do;
if findw(cols,name,,'spit') then do;
sumcols+1;
matchcols=cats(matchcols)!!' '!!cats(name);
end;
%end;
%else %if &match=WILD %then %do;
if _n_=1 then do;
retain wcount;
wcount=countw(cols);
drop wcount;
end;
do i=1 to wcount;
length curword $32;
curword=scan(cols,i,' ');
drop curword;
if index(name,cats(curword)) then do;
sumcols+1;
matchcols=cats(matchcols)!!' '!!cats(curword);
end;
end;
%end;
if last.memname then do;
if sumcols>0 then output;
if sumcols=colcount then putlog "Full Match: " libname memname;
@@ -66,6 +96,8 @@ run;
proc sort; by descending sumcols memname libname; run;
proc sql;
drop table &tempds;
%put &sysmacroname process finished at %sysfunc(datetime(),datetime19.);
%mend;
%mend mp_searchcols;

View File

@@ -117,4 +117,4 @@ proc sql
%put process finished at %sysfunc(datetime(),datetime19.);
%mend;
%mend mp_searchdata;

View File

@@ -49,4 +49,4 @@
quit;
%mend;
%mend mp_setkeyvalue;

View File

@@ -71,4 +71,4 @@
proc append base=&libds data=&syslast nowarn;run;
options &etls_syntaxcheck;
%mend;
%mend mp_stprequests;

View File

@@ -134,4 +134,4 @@ run;
%mp_binarycopy(inloc="&inloc",outref=_webout)
%end;
%mend;
%mend mp_streamfile;

View File

@@ -89,4 +89,4 @@ quit;
libname &lib clear;
%mend;
%mend mp_testjob;

View File

@@ -22,8 +22,11 @@
|mustbevalidname|can be anything, oops, %abort!!|
@param [in] debug= (log) Provide the _debug value
@param [in] viyaresult=(WEBOUT_JSON) The Viya result type to return. For
@param [in] mdebug= (0) Set to 1 to provide macro debugging
@param [in] viyaresult= (WEBOUT_JSON) The Viya result type to return. For
more info, see mv_getjobresult.sas
@param [in] viyacontext= (SAS Job Execution compute context) The Viya compute
context on which to run the service
@param [out] outlib= (0) Output libref to contain the final tables. Set to
0 if the service output is not in JSON format.
@param [out] outref= (0) Output fileref to create, to contain the full _webout
@@ -47,17 +50,18 @@
inputfiles=0,
inputparams=0,
debug=log,
mdebug=0,
outlib=0,
outref=0,
viyaresult=WEBOUT_JSON
viyaresult=WEBOUT_JSON,
viyacontext=SAS Job Execution compute context
)/*/STORE SOURCE*/;
%local mdebug;
%if &debug ne 0 %then %do;
%let mdebug=1;
%local dbg;
%if &mdebug=1 %then %do;
%put &sysmacroname entry vars:;
%put _local_;
%end;
%else %let mdebug=0;
%else %let dbg=*;
/* sanitise inputparams */
%local pcnt;
@@ -212,6 +216,7 @@
data &ds1;
retain _program "&program";
retain _contextname "&viyacontext";
set &ds1;
putlog "&sysmacroname inputparams:";
putlog (_all_)(=);

View File

@@ -56,4 +56,4 @@ data &outds;
duration_seconds=end_dttm-start_dttm;
run;
%mend;
%mend mp_testwritespeedlibrary;

View File

@@ -68,4 +68,4 @@ data &outds ;
rc=filename('tmp');
run;
%mend;
%mend mp_tree;

View File

@@ -63,4 +63,4 @@ data _null_;
!!'filename &fname2 clear; filename &fname3 clear;');
run;
%mend;
%mend mp_unzip;

View File

@@ -90,4 +90,4 @@ alter table &libds modify &var char(&len);
%mp_createconstraints(inds=&dsconst,outds=&dsconst._addd,execute=YES)
%mend;
%mend mp_updatevarlength;

View File

@@ -76,4 +76,4 @@ ods package publish archive properties
(archive_name="&outname..zip" archive_path="&outpath");
ods package close;
%mend;
%mend mp_zip;

View File

@@ -23,7 +23,7 @@ for file in files:
ml.write(" put '" + line.rstrip().replace("'","''") + " ';\n")
ml.write("run;\n\n")
ml.write("%inc \"%sysfunc(pathname(work))/" + name + ".lua\";\n\n")
ml.write("%mend;\n")
ml.write("%mend " + name + ";\n")
ml.close()
@@ -84,7 +84,7 @@ options noquotelenmax;
"""
f = open('all.sas', "w") # r / r+ / rb / rb+ / w / wb
f.write(header)
folders=['base','meta','metax','viya','lua']
folders=['base','meta','metax','viya','lua','fcmp']
for folder in folders:
filenames = [fn for fn in Path('./' + folder).iterdir() if fn.match("*.sas")]
filenames.sort()

View File

@@ -0,0 +1,94 @@
/**
@file
@brief Provides a replacement for the stpsrv_header function
@details The stpsrv_header is normally a built-in function, used to set the
headers for SAS 9 Stored Processes as documented here:
https://go.documentation.sas.com/doc/en/itechcdc/9.4/stpug/srvhead.htm
The purpose of this custom function is to provide a replacement when running
similar code as a web service against
[sasjs/server](https://github.com/sasjs/server). It operates by creating a
text file with the headers. The location of this text file is determined by
a macro variable (`sasjs_stpsrv_header_loc`) which needs to be injected into
each service by the calling process, eg:
%let sasjs_stpsrv_header_loc = C:/temp/some_uuid/stpsrv_header.txt;
Note - the function works by appending headers to the file. If multiple same-
named headers are provided, they will all be appended - the calling process
needs to pick up the last one. This will mean removing the attribute if the
final record has an empty value.
The function takes the following (positional) parameters:
| PARAMETER | DESCRIPTION |
|------------|-------------|
| name $ | name of the header attribute to create|
| value $ | value of the header attribute|
It returns 0 if successful, or -1 if an error occured.
Usage:
%let sasjs_stpsrv_header_loc=%sysfunc(pathname(work))/stpsrv_header.txt;
%mcf_stpsrv_header(wrap=YES, insert_cmplib=YES)
data _null_;
rc=stpsrv_header('Content-type','application/text');
rc=stpsrv_header('Content-disposition',"attachment; filename=file.txt");
run;
data _null_;
infile "&sasjs_stpsrv_header_loc";
input;
putlog _infile_;
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
**/
%macro mcf_stpsrv_header(wrap=NO
,insert_cmplib=NO
,lib=WORK
,cat=SASJS
,pkg=UTILS
)/*/STORE SOURCE*/;
%if &wrap=YES %then %do;
proc fcmp outcat=&lib..&cat..&pkg;
%end;
function stpsrv_header(name $, value $);
length loc $128 val $512;
loc=symget('sasjs_stpsrv_header_loc');
val=trim(name)!!': '!!value;
length fref $8;
rc=filename(fref,loc);
if (rc ne 0) then return( -1 );
fid = fopen(fref,'a');
if (fid = 0) then return( -1 );
rc=fput(fid, val);
rc=fwrite(fid);
rc=fclose(fid);
rc=filename(fref);
return(0);
endsub;
%if &wrap=YES %then %do;
quit;
%end;
%if &insert_cmplib=YES %then %do;
options insert=(CMPLIB=(&lib..&cat));
%end;
%mend mcf_stpsrv_header;

79
fcmp/mcf_string2file.sas Normal file
View File

@@ -0,0 +1,79 @@
/**
@file
@brief Adds a string to a file
@details Creates an fcmp function for appending a string to an external file.
If the file does not exist, it is created.
The function itself takes the following (positional) parameters:
| PARAMETER | DESCRIPTION |
|------------|-------------|
| filepath $ | full path to the file|
| string $ | string to add to the file |
| mode $ | mode of the output - either APPEND (default) or CREATE |
It returns 0 if successful, or -1 if an error occured.
Usage:
%mcf_string2file(wrap=YES, insert_cmplib=YES)
data _null_;
rc=mcf_string2file(
"%sysfunc(pathname(work))/newfile.txt"
, "This is a test"
, "CREATE");
run;
data _null_;
infile "%sysfunc(pathname(work))/newfile.txt";
input;
putlog _infile_;
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
**/
%macro mcf_string2file(wrap=NO
,insert_cmplib=NO
,lib=WORK
,cat=SASJS
,pkg=UTILS
)/*/STORE SOURCE*/;
%if &wrap=YES %then %do;
proc fcmp outcat=&lib..&cat..&pkg;
%end;
function mcf_string2file(filepath $, string $, mode $);
if mode='APPEND' then fmode='a';
else fmode='o';
length fref $8;
rc=filename(fref,filepath);
if (rc ne 0) then return( -1 );
fid = fopen(fref,fmode);
if (fid = 0) then return( -1 );
rc=fput(fid, string);
rc=fwrite(fid);
rc=fclose(fid);
rc=filename(fref);
return(0);
endsub;
%if &wrap=YES %then %do;
quit;
%end;
%if &insert_cmplib=YES %then %do;
options insert=(CMPLIB=(&lib..&cat));
%end;
%mend mcf_string2file;

View File

@@ -1,3 +1,5 @@
-- NOTE - THE COPYRIGHT BELOW IS IN RELATION TO THE JSON.LUA FILE ONLY
-- THIS FILE STARTS ON THE NEXT LINE AND WILL FINISH WITH "JSON.LUA ENDS HERE"
--
-- json.lua
--
@@ -369,3 +371,5 @@ function json.decode(str)
end
return json
-- JSON.LUA ENDS HERE

View File

@@ -12,6 +12,8 @@
%macro ml_json();
data _null_;
file "%sysfunc(pathname(work))/ml_json.lua";
put '-- NOTE - THE COPYRIGHT BELOW IS IN RELATION TO THE JSON.LUA FILE ONLY ';
put '-- THIS FILE STARTS ON THE NEXT LINE AND WILL FINISH WITH "JSON.LUA ENDS HERE" ';
put '-- ';
put '-- json.lua ';
put '-- ';
@@ -383,8 +385,10 @@ data _null_;
put 'end ';
put ' ';
put 'return json ';
put ' ';
put '-- JSON.LUA ENDS HERE ';
run;
%inc "%sysfunc(pathname(work))/ml_json.lua";
%mend;
%mend ml_json;

View File

@@ -20,6 +20,19 @@
*/
/*! \dir fcmp
* \brief Macros for generating FCMP functions
* \details These macros have the following attributes:
* Macro name matches compiled function / subroutine name
* Prefixes: _mcf_, _mcs_
The macro part is just a wrapper for the underlying function / subroutine,
and has switches for including the proc fcmp / quit statements and whether
to insert the package into the CMPLIB option.
*/
/*! \dir meta
* \brief Metadata Aware Macros
* \details These macros have the following attributes:
@@ -42,6 +55,13 @@
*/
/*! \dir Tests
* \brief SASjs Tests
* \details These folders contain the macro tests. They are first compiled
and deployed (sasjs cbd) then executed (sasjs test).
*/
/*! \dir viya
* \brief Viya macros
* \details These macros have the following attributes:

View File

@@ -95,4 +95,4 @@ run;
filename __us2grp clear;
%mend;
%mend mm_adduser2group;

Some files were not shown because too many files have changed in this diff Show More