mirror of
https://github.com/sasjs/core.git
synced 2026-01-03 23:50:06 +00:00
Compare commits
204 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c6703e16e8 | ||
|
|
6587dce95b | ||
|
|
b60e6448b9 | ||
|
|
46d9b58b32 | ||
|
|
349cbabc94 | ||
|
|
9de056a3fc | ||
|
|
ad497b322f | ||
|
|
7a6408ee44 | ||
|
|
336743f2b4 | ||
|
|
6e32eb3bd6 | ||
|
|
b377b83442 | ||
|
|
899b94bb6e | ||
|
|
d97efdff61 | ||
|
|
1097afbcb8 | ||
|
|
165b2d3568 | ||
|
|
44a80c8985 | ||
|
|
6e32d9b743 | ||
|
|
6b167e7a4c | ||
|
|
011672b1ed | ||
|
|
a7eb926810 | ||
|
|
cad7f13a0e | ||
|
|
65fcea817a | ||
|
|
22fade13e7 | ||
|
|
7146310072 | ||
|
|
b7de1c25ec | ||
|
|
f4c7f47ffe | ||
|
|
cdf339d077 | ||
|
|
31702df19b | ||
|
|
cf0d1c0473 | ||
|
|
1f369f9848 | ||
|
|
2372ff5f4f | ||
|
|
6d0e34ba1d | ||
|
|
7a69698178 | ||
|
|
532bf84e06 | ||
|
|
e1afbc02c4 | ||
|
|
756f00d88d | ||
|
|
b72e404d52 | ||
|
|
e31cdeef42 | ||
|
|
8a4e32cc27 | ||
|
|
f285505b79 | ||
|
|
67f5c50300 | ||
|
|
ce39e4f779 | ||
|
|
9c80f5664c | ||
|
|
83466c001b | ||
|
|
ad315be503 | ||
|
|
c41ae2dcc8 | ||
| d9f8e92fac | |||
|
|
d42ede15db | ||
|
|
08ea9f7c00 | ||
|
|
c327e1fc0d | ||
|
|
02fddcf9a1 | ||
|
|
4752bfbb05 | ||
|
|
767ddd7add | ||
|
|
54a24ced83 | ||
|
|
57ae2981f1 | ||
|
|
a3043ac685 | ||
|
|
2bdb90b0be | ||
|
|
2cd846d504 | ||
|
|
f593c7bec9 | ||
|
|
c8805db0b5 | ||
|
|
1eb6d8cec9 | ||
|
|
ed19ee03af | ||
|
|
a1c931b5e6 | ||
|
|
cb553a31ab | ||
|
|
557df272ff | ||
|
|
0cb3c96c15 | ||
|
|
1cb39d4d61 | ||
|
|
934b7d4f8a | ||
|
|
24c50cde56 | ||
|
|
055e8d2f13 | ||
|
|
abfe7fe339 | ||
|
|
16ed91f6a9 | ||
|
|
67ba2a5286 | ||
|
|
3d7f9b71e1 | ||
|
|
1d972fad11 | ||
|
|
e23bc461c4 | ||
|
|
28ed458b83 | ||
|
|
827210e010 | ||
|
|
de2f32da36 | ||
|
|
6fa0fc5dc6 | ||
|
|
73e3d9d419 | ||
|
|
5f2229e3d5 | ||
|
|
d19c4a517c | ||
|
|
c47480f60c | ||
|
|
295211bb72 | ||
|
|
818bc3cc2b | ||
|
|
bb6111e2b3 | ||
|
|
512f05c0b2 | ||
|
|
500fb8124f | ||
|
|
88ddba2a4b | ||
|
|
86f6d06b85 | ||
|
|
1cefc0e7ee | ||
|
|
412182a022 | ||
|
|
43b8ee1c7e | ||
|
|
83eea02240 | ||
|
|
a14e31804a | ||
|
|
3fa639ebf7 | ||
|
|
ed11d44fe8 | ||
|
|
de4ea8888f | ||
|
|
ea0a936871 | ||
|
|
042987c91e | ||
|
|
6669e74baa | ||
|
|
906f9a139d | ||
|
|
b31f960635 | ||
|
|
1ed3cb31b5 | ||
|
|
ca7c332f20 | ||
|
|
d587b44b34 | ||
|
|
e43aac972a | ||
|
|
7dbe31b5d3 | ||
|
|
1672c96340 | ||
|
|
453aee2c1f | ||
|
|
00abbdcd65 | ||
|
|
88685dc585 | ||
|
|
cf8147d6ca | ||
|
|
f28f6b1530 | ||
|
|
cb4ea71e81 | ||
|
|
fe94d3781a | ||
|
|
7c17b39dad | ||
|
|
73dab4c651 | ||
|
|
5d72843167 | ||
|
|
f43df47cff | ||
|
|
aaca26770b | ||
|
|
4a124d5bd8 | ||
|
|
03cd52a01a | ||
|
|
da79181b00 | ||
|
|
a405104052 | ||
|
|
56fdaa65d2 | ||
|
|
9d60c49c9f | ||
|
|
380170d5ba | ||
|
|
4b450f2091 | ||
|
|
1b013fbf1c | ||
|
|
bf7459bd2d | ||
|
|
1096db0846 | ||
|
|
fc9b765246 | ||
|
|
4a8f7bb014 | ||
|
|
e0469be0d8 | ||
|
|
e9e576b5ec | ||
|
|
1a32d114f1 | ||
|
|
94e83f6b8d | ||
|
|
35a6dede6f | ||
|
|
039ec397dd | ||
|
|
dce4630eb8 | ||
|
|
1e142f042b | ||
|
|
7caca2f139 | ||
|
|
61556b2de8 | ||
|
|
9e12409389 | ||
|
|
c8050f5a79 | ||
|
|
cb4f71c7cd | ||
|
|
a39f4e4eee | ||
|
|
b525b4171d | ||
|
|
f2d80b3b63 | ||
|
|
96dda87f37 | ||
|
|
3435509eec | ||
|
|
42f2767129 | ||
|
|
099a5f7840 | ||
|
|
c83ea705a2 | ||
|
|
9ea6c875f2 | ||
|
|
0728f72c4f | ||
|
|
a90a6f00cf | ||
|
|
f71e53af8d | ||
|
|
cc1b971e19 | ||
|
|
8484c752ed | ||
|
|
8bd31e6c97 | ||
|
|
f9b0f87f44 | ||
|
|
d7e9f10291 | ||
|
|
3edc3587b3 | ||
|
|
63bf00e28f | ||
|
|
6b2574947a | ||
|
|
eb9027ecb6 | ||
|
|
2ad931a566 | ||
|
|
cebe119304 | ||
|
|
bd3082d7e3 | ||
|
|
11da53f1cb | ||
|
|
c4cb0b2395 | ||
|
|
58614e9a3d | ||
|
|
c94c334c4b | ||
|
|
3bf44405f8 | ||
|
|
db68a256cb | ||
|
|
611fac6338 | ||
|
|
ddd120bb75 | ||
|
|
0d75e0bad8 | ||
|
|
ba8c4ac844 | ||
|
|
6938a42896 | ||
|
|
efff77c94e | ||
|
|
2b10cf6192 | ||
|
|
134b91f266 | ||
|
|
969f551e10 | ||
|
|
26623ba085 | ||
|
|
8eb495890d | ||
|
|
c1a30977f1 | ||
|
|
9a6be61651 | ||
|
|
388839039e | ||
|
|
e760a89a6a | ||
|
|
d2e30267e8 | ||
|
|
190dbddfe3 | ||
|
|
05e769794e | ||
|
|
558ebaf6f2 | ||
|
|
970b56fe5a | ||
|
|
c2597bd07b | ||
|
|
c4baca477b | ||
|
|
7726b0e0b0 | ||
|
|
0a536245f3 | ||
|
|
edfa9ecc07 | ||
|
|
f4982c85ca |
@@ -126,6 +126,15 @@
|
|||||||
"contributions": [
|
"contributions": [
|
||||||
"code"
|
"code"
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"login": "eltociear",
|
||||||
|
"name": "Ikko Ashimine",
|
||||||
|
"avatar_url": "https://avatars.githubusercontent.com/u/22633385?v=4",
|
||||||
|
"profile": "https://bandism.net/",
|
||||||
|
"contributions": [
|
||||||
|
"code"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"contributorsPerLine": 7,
|
"contributorsPerLine": 7,
|
||||||
|
|||||||
8
.devcontainer/Dockerfile
Normal file
8
.devcontainer/Dockerfile
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
# See here for image contents: https://github.com/microsoft/vscode-dev-containers/tree/v0.233.0/containers/javascript-node/.devcontainer/base.Dockerfile
|
||||||
|
|
||||||
|
# [Choice] Node.js version (use -bullseye variants on local arm64/Apple Silicon): 18, 16, 14, 18-bullseye, 16-bullseye, 14-bullseye, 18-buster, 16-buster, 14-buster
|
||||||
|
ARG VARIANT="18-bullseye"
|
||||||
|
FROM mcr.microsoft.com/vscode/devcontainers/javascript-node:0-${VARIANT}
|
||||||
|
|
||||||
|
RUN apt-get update \
|
||||||
|
&& apt-get install -y doxygen
|
||||||
26
.devcontainer/devcontainer.json
Normal file
26
.devcontainer/devcontainer.json
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
// For format details, see https://aka.ms/devcontainer.json. For config options, see the README at:
|
||||||
|
// https://github.com/microsoft/vscode-dev-containers/tree/v0.233.0/containers/typescript-node
|
||||||
|
{
|
||||||
|
"name": "Node.js & TypeScript",
|
||||||
|
"build": {
|
||||||
|
"dockerfile": "Dockerfile",
|
||||||
|
// Update 'VARIANT' to pick a Node version: 18, 16, 14.
|
||||||
|
// Append -bullseye or -buster to pin to an OS version.
|
||||||
|
// Use -bullseye variants on local on arm64/Apple Silicon.
|
||||||
|
"args": {
|
||||||
|
"VARIANT": "16-bullseye"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// Set *default* container specific settings.json values on container create.
|
||||||
|
"settings": {},
|
||||||
|
// Add the IDs of extensions you want installed when the container is created.
|
||||||
|
"extensions": [
|
||||||
|
"SASjs.sasjs-for-vscode"
|
||||||
|
],
|
||||||
|
// Use 'forwardPorts' to make a list of ports inside the container available locally.
|
||||||
|
// "forwardPorts": [],
|
||||||
|
// Use 'postCreateCommand' to run commands after the container is created.
|
||||||
|
"postCreateCommand": "npm i && npm i -g @sasjs/cli",
|
||||||
|
// Comment out to connect as root instead. More info: https://aka.ms/vscode-remote/containers/non-root.
|
||||||
|
"remoteUser": "node"
|
||||||
|
}
|
||||||
17
.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
17
.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
## Issue
|
||||||
|
|
||||||
|
Link any related issue(s) in this section.
|
||||||
|
|
||||||
|
## Intent
|
||||||
|
|
||||||
|
What this PR intends to achieve.
|
||||||
|
|
||||||
|
## Implementation
|
||||||
|
|
||||||
|
What code changes have been made to achieve the intent.
|
||||||
|
|
||||||
|
## Checks
|
||||||
|
|
||||||
|
- [ ] Code is formatted correctly (`sasjs lint`).
|
||||||
|
- [ ] Any new functionality has been unit tested.
|
||||||
|
- [ ] All unit tests are passing (`sasjs test`).
|
||||||
@@ -6,7 +6,6 @@ sasjs/
|
|||||||
.github/
|
.github/
|
||||||
.git-hooks/
|
.git-hooks/
|
||||||
.vscode/
|
.vscode/
|
||||||
main.dox
|
|
||||||
make_singlefile.sh
|
make_singlefile.sh
|
||||||
*.md
|
*.md
|
||||||
.all-contributorsrc
|
.all-contributorsrc
|
||||||
|
|||||||
100
README.md
100
README.md
@@ -18,7 +18,7 @@
|
|||||||
[dependency-url]:https://github.com/sasjs/core/blob/main/package.json
|
[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.
|
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/.github/CONTRIBUTING.md) are welcome.
|
||||||
|
|
||||||
You can download and compile them all in just two lines of SAS code:
|
You can download and compile them all in just two lines of SAS code:
|
||||||
|
|
||||||
@@ -31,14 +31,14 @@ Documentation: https://core.sasjs.io
|
|||||||
|
|
||||||
## Components
|
## Components
|
||||||
|
|
||||||
### BASE library (All Platforms)
|
### BASE folder (All Platforms)
|
||||||
|
|
||||||
- OS independent
|
- OS independent
|
||||||
- Works on all SAS Platforms
|
- Works on all SAS Platforms
|
||||||
- No X command
|
- No X command
|
||||||
- Prefixes: _mf_, _mp_
|
- Prefixes: _mf_, _mp_
|
||||||
|
|
||||||
### DDL library (All Platforms)
|
### DDL folder (All Platforms)
|
||||||
|
|
||||||
- OS independent
|
- OS independent
|
||||||
- Works on all SAS Platforms
|
- Works on all SAS Platforms
|
||||||
@@ -47,46 +47,14 @@ Documentation: https://core.sasjs.io
|
|||||||
|
|
||||||
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).
|
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)
|
### FCMP folder (All Platforms)
|
||||||
|
|
||||||
- Function and macro names are identical, except for special cases
|
- Function and macro names are identical, except for special cases
|
||||||
- Prefixes: _mcf_
|
- 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)
|
### LUA folder
|
||||||
|
|
||||||
Macros used in SAS EBI, which connect to the metadata server.
|
|
||||||
|
|
||||||
- OS independent
|
|
||||||
- Metadata aware
|
|
||||||
- No X command
|
|
||||||
- Prefixes: _mm_
|
|
||||||
|
|
||||||
### SERVER library (@sasjs/server only)
|
|
||||||
These macros are used for building applications using [@sasjs/server](https://server.sasjs.io) - an open source REST API for Desktop SAS.
|
|
||||||
|
|
||||||
- OS independent
|
|
||||||
- @sasjs/server aware
|
|
||||||
- No X command
|
|
||||||
- Prefixes: _ms_
|
|
||||||
|
|
||||||
### VIYA library (Viya only)
|
|
||||||
|
|
||||||
Macros used for interfacing with SAS Viya.
|
|
||||||
|
|
||||||
- OS independent
|
|
||||||
- No X command
|
|
||||||
- Prefixes: _mv_, _mvf_
|
|
||||||
|
|
||||||
### METAX library (SAS9 only)
|
|
||||||
|
|
||||||
- OS specific
|
|
||||||
- Metadata aware
|
|
||||||
- X command enabled
|
|
||||||
- Prefixes: _mmw_,_mmu_,_mmx_
|
|
||||||
|
|
||||||
### LUA library
|
|
||||||
|
|
||||||
Wait - this is a macro library - what is LUA doing here? Well, it is a little known fact that you CAN run LUA within a SAS Macro. It has to be written to a text file with a `.lua` extension, from where you can `%include` it. So, without using the `proc lua` wrapper.
|
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.
|
||||||
|
|
||||||
@@ -106,13 +74,61 @@ run;
|
|||||||
|
|
||||||
- Prefixes: _ml_
|
- Prefixes: _ml_
|
||||||
|
|
||||||
|
### META folder (SAS9 only)
|
||||||
|
|
||||||
|
Macros used in SAS EBI, which connect to the metadata server.
|
||||||
|
|
||||||
|
- OS independent
|
||||||
|
- Metadata aware
|
||||||
|
- No X command
|
||||||
|
- Prefixes: _mm_
|
||||||
|
|
||||||
|
### METAX folder (SAS9 only)
|
||||||
|
|
||||||
|
- OS specific
|
||||||
|
- Metadata aware
|
||||||
|
- X command enabled
|
||||||
|
- Prefixes: _mmw_,_mmu_,_mmx_
|
||||||
|
|
||||||
|
### SERVER folder (@sasjs/server only)
|
||||||
|
These macros are used for building applications using [@sasjs/server](https://server.sasjs.io) - an open source REST API for Desktop SAS.
|
||||||
|
|
||||||
|
- OS independent
|
||||||
|
- @sasjs/server aware
|
||||||
|
- No X command
|
||||||
|
- Prefixes: _ms_
|
||||||
|
|
||||||
|
### VIYA folder (Viya only)
|
||||||
|
|
||||||
|
Macros used for interfacing with SAS Viya.
|
||||||
|
|
||||||
|
- OS independent
|
||||||
|
- No X command
|
||||||
|
- Prefixes: _mv_, _mvf_
|
||||||
|
|
||||||
|
### XPLATFORM folder (Viya, Meta, and Server)
|
||||||
|
|
||||||
|
Sometimes it is helpful to use a macro that can be used interchangeably regardless of the server type on which is is running (SASVIYA, SAS9, SASJS).
|
||||||
|
|
||||||
|
- OS independent
|
||||||
|
- No X command
|
||||||
|
- Prefixes: _mx_
|
||||||
|
|
||||||
## Installation
|
## 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
|
```sas
|
||||||
options insert=(sasautos="/your/path/macrocore/base");
|
%let repoloc=/your/path/core;
|
||||||
options insert=(sasautos="/your/path/macrocore/meta");
|
options insert=(sasautos="&repoloc/base");
|
||||||
|
options insert=(sasautos="&repoloc/ddl");
|
||||||
|
options insert=(sasautos="&repoloc/fcmp");
|
||||||
|
options insert=(sasautos="&repoloc/lua");
|
||||||
|
options insert=(sasautos="&repoloc/meta");
|
||||||
|
options insert=(sasautos="&repoloc/metax");
|
||||||
|
options insert=(sasautos="&repoloc/server");
|
||||||
|
options insert=(sasautos="&repoloc/viya");
|
||||||
|
options insert=(sasautos="&repoloc/xplatform");
|
||||||
```
|
```
|
||||||
|
|
||||||
The above can be done directly in your sas program, via an autoexec, or an initialisation program.
|
The above can be done directly in your sas program, via an autoexec, or an initialisation program.
|
||||||
@@ -142,7 +158,7 @@ filename mc url "https://raw.githubusercontent.com/sasjs/core/main/all.sas";
|
|||||||
- _mp_ for macro procedures (which generate sas code)
|
- _mp_ for macro procedures (which generate sas code)
|
||||||
- _ms_ for macro procedures that will only work with [@sasjs/server](https://github.com/sasjs/server)
|
- _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
|
- _mv_ for macro procedures that will only work in Viya
|
||||||
- _mx_ for macros that are XCMD enabled (working on both windows and unix)
|
- _mx_ for macros that work on Viya, SAS 9 EBI and SASjs Server
|
||||||
- follow verb-noun convention
|
- follow verb-noun convention
|
||||||
- unix style line endings (lf)
|
- unix style line endings (lf)
|
||||||
- individual lines should be no more than 80 characters long
|
- individual lines should be no more than 80 characters long
|
||||||
@@ -224,12 +240,13 @@ The following repositories are also worth checking out:
|
|||||||
* [chris-swenson/sasmacros](https://github.com/chris-swenson/sasmacros)
|
* [chris-swenson/sasmacros](https://github.com/chris-swenson/sasmacros)
|
||||||
* [greg-wotton/sas-programs](https://github.com/greg-wootton/sas-programs)
|
* [greg-wotton/sas-programs](https://github.com/greg-wootton/sas-programs)
|
||||||
* [KatjaGlassConsulting/SMILE-SmartSASMacros](https://github.com/KatjaGlassConsulting/SMILE-SmartSASMacros)
|
* [KatjaGlassConsulting/SMILE-SmartSASMacros](https://github.com/KatjaGlassConsulting/SMILE-SmartSASMacros)
|
||||||
|
* [rogerjdeangelis](https://github.com/rogerjdeangelis)
|
||||||
* [scottbass/sas](https://github.com/scottbass/SAS)
|
* [scottbass/sas](https://github.com/scottbass/SAS)
|
||||||
* [yabwon/sas_packages](https://github.com/yabwon/SAS_PACKAGES)
|
* [yabwon/sas_packages](https://github.com/yabwon/SAS_PACKAGES)
|
||||||
|
|
||||||
## Contributors ✨
|
## Contributors ✨
|
||||||
<!-- ALL-CONTRIBUTORS-BADGE:START - Do not remove or modify this section -->
|
<!-- ALL-CONTRIBUTORS-BADGE:START - Do not remove or modify this section -->
|
||||||
[](#contributors-)
|
[](#contributors-)
|
||||||
<!-- ALL-CONTRIBUTORS-BADGE:END -->
|
<!-- ALL-CONTRIBUTORS-BADGE:END -->
|
||||||
Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)):
|
Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)):
|
||||||
|
|
||||||
@@ -251,6 +268,7 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
|
|||||||
<td align="center"><a href="https://github.com/VladislavParhomchik"><img src="https://avatars.githubusercontent.com/u/83717836?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Vladislav Parhomchik</b></sub></a><br /><a href="https://github.com/sasjs/core/commits?author=VladislavParhomchik" title="Tests">⚠️</a> <a href="https://github.com/sasjs/core/pulls?q=is%3Apr+reviewed-by%3AVladislavParhomchik" title="Reviewed Pull Requests">👀</a></td>
|
<td align="center"><a href="https://github.com/VladislavParhomchik"><img src="https://avatars.githubusercontent.com/u/83717836?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Vladislav Parhomchik</b></sub></a><br /><a href="https://github.com/sasjs/core/commits?author=VladislavParhomchik" title="Tests">⚠️</a> <a href="https://github.com/sasjs/core/pulls?q=is%3Apr+reviewed-by%3AVladislavParhomchik" title="Reviewed Pull Requests">👀</a></td>
|
||||||
<td align="center"><a href="https://github.com/vznesh"><img src="https://avatars.githubusercontent.com/u/28916792?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Vignesh T.</b></sub></a><br /><a href="https://github.com/sasjs/core/issues?q=author%3Avznesh" title="Bug reports">🐛</a></td>
|
<td align="center"><a href="https://github.com/vznesh"><img src="https://avatars.githubusercontent.com/u/28916792?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Vignesh T.</b></sub></a><br /><a href="https://github.com/sasjs/core/issues?q=author%3Avznesh" title="Bug reports">🐛</a></td>
|
||||||
<td align="center"><a href="https://github.com/yabwon"><img src="https://avatars.githubusercontent.com/u/9314894?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Bart Jablonski</b></sub></a><br /><a href="https://github.com/sasjs/core/commits?author=yabwon" title="Code">💻</a></td>
|
<td align="center"><a href="https://github.com/yabwon"><img src="https://avatars.githubusercontent.com/u/9314894?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Bart Jablonski</b></sub></a><br /><a href="https://github.com/sasjs/core/commits?author=yabwon" title="Code">💻</a></td>
|
||||||
|
<td align="center"><a href="https://bandism.net/"><img src="https://avatars.githubusercontent.com/u/22633385?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Ikko Ashimine</b></sub></a><br /><a href="https://github.com/sasjs/core/commits?author=eltociear" title="Code">💻</a></td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
|
|||||||
@@ -30,7 +30,7 @@
|
|||||||
%put No feature was requested for detection;
|
%put No feature was requested for detection;
|
||||||
%end;
|
%end;
|
||||||
%else %if &feature=COLCONSTRAINTS %then %do;
|
%else %if &feature=COLCONSTRAINTS %then %do;
|
||||||
%if %substr(&sysver,1,1)=4 %then 0;
|
%if "%substr(&sysver,1,1)"="4" or "%substr(&sysver,1,1)"="5" %then 0;
|
||||||
%else 1;
|
%else 1;
|
||||||
%end;
|
%end;
|
||||||
%else %if &feature=PROCLUA %then %do;
|
%else %if &feature=PROCLUA %then %do;
|
||||||
@@ -40,6 +40,17 @@
|
|||||||
%else %if "&SYSVLONG" < "9.04.01M3" %then 0;
|
%else %if "&SYSVLONG" < "9.04.01M3" %then 0;
|
||||||
%else 1;
|
%else 1;
|
||||||
%end;
|
%end;
|
||||||
|
%else %if &feature=DBMS_MEMTYPE %then %do;
|
||||||
|
/* does dbms_memtype exist in dictionary.tables? */
|
||||||
|
%if "%substr(&sysver,1,1)"="4" or "%substr(&sysver,1,1)"="5" %then 0;
|
||||||
|
%else 1;
|
||||||
|
%end;
|
||||||
|
%else %if &feature=EXPORTXLS %then %do;
|
||||||
|
/* is it possible to PROC EXPORT an excel file? */
|
||||||
|
%if "%substr(&sysver,1,1)"="4" or "%substr(&sysver,1,1)"="5" %then 1;
|
||||||
|
%else %if %sysfunc(sysprod(SAS/ACCESS Interface to PC Files)) = 1 %then 1;
|
||||||
|
%else 0;
|
||||||
|
%end;
|
||||||
%else %do;
|
%else %do;
|
||||||
-1
|
-1
|
||||||
%put &sysmacroname: &feature not found;
|
%put &sysmacroname: &feature not found;
|
||||||
|
|||||||
@@ -6,9 +6,6 @@
|
|||||||
|
|
||||||
%put %mf_existVarList(sashelp.class, age sex name dummyvar);
|
%put %mf_existVarList(sashelp.class, age sex name dummyvar);
|
||||||
|
|
||||||
<h4> SAS Macros </h4>
|
|
||||||
@li mf_abort.sas
|
|
||||||
|
|
||||||
@param libds 2 part dataset or view reference
|
@param libds 2 part dataset or view reference
|
||||||
@param varlist space separated variable names
|
@param varlist space separated variable names
|
||||||
|
|
||||||
|
|||||||
42
base/mf_fmtdttm.sas
Normal file
42
base/mf_fmtdttm.sas
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
/**
|
||||||
|
@file
|
||||||
|
@brief Returns E8601DT26.6 if compatible else DATETIME19.3
|
||||||
|
@details From our experience in [Data Controller for SAS]
|
||||||
|
(https://datacontroller.io) deployments, the E8601DT26.6 datetime format has
|
||||||
|
the widest support when it comes to pass-through SQL queries.
|
||||||
|
|
||||||
|
However, it is not supported in WPS or early versions of SAS 9 (M3 and below)
|
||||||
|
when used as a datetime literal, eg:
|
||||||
|
|
||||||
|
data _null_;
|
||||||
|
demo="%sysfunc(datetime(),E8601DT26.6)"dt;
|
||||||
|
demo=;
|
||||||
|
run;
|
||||||
|
|
||||||
|
This macro will therefore return DATEITME19.3 as an alternative format
|
||||||
|
based on the runtime environment so that it can be used in such cases, eg:
|
||||||
|
|
||||||
|
data _null_;
|
||||||
|
demo="%sysfunc(datetime(),%mf_fmtdttm())"dt;
|
||||||
|
demo=;
|
||||||
|
run;
|
||||||
|
|
||||||
|
<h4> Related Macros </h4>
|
||||||
|
@li mf_fmtdttm.test.sas
|
||||||
|
|
||||||
|
@author Allan Bowe
|
||||||
|
**/
|
||||||
|
|
||||||
|
%macro mf_fmtdttm(
|
||||||
|
)/*/STORE SOURCE*/;
|
||||||
|
|
||||||
|
%if "&sysver"="9.2" or "&sysver"="9.3"
|
||||||
|
or ("&sysver"="9.4" and "%substr(&SYSVLONG,9,1)" le "3")
|
||||||
|
or "%substr(&sysver,1,1)"="4"
|
||||||
|
or "%substr(&sysver,1,1)"="5"
|
||||||
|
%then %do;DATETIME19.3%end;
|
||||||
|
%else %do;E8601DT26.6%end;
|
||||||
|
|
||||||
|
%mend mf_fmtdttm;
|
||||||
|
|
||||||
|
|
||||||
@@ -5,8 +5,12 @@
|
|||||||
|
|
||||||
%put %mf_getplatform();
|
%put %mf_getplatform();
|
||||||
|
|
||||||
returns:
|
returns one of:
|
||||||
SASMETA (or SASVIYA)
|
|
||||||
|
@li SASMETA
|
||||||
|
@li SASVIYA
|
||||||
|
@li SASJS
|
||||||
|
@li BASESAS
|
||||||
|
|
||||||
@param switch the param for which to return a platform specific variable
|
@param switch the param for which to return a platform specific variable
|
||||||
|
|
||||||
@@ -68,4 +72,4 @@
|
|||||||
%else %if &switch=VIYARESTAPI %then %do;
|
%else %if &switch=VIYARESTAPI %then %do;
|
||||||
%mf_trimstr(%sysfunc(getoption(servicesbaseurl)),/)
|
%mf_trimstr(%sysfunc(getoption(servicesbaseurl)),/)
|
||||||
%end;
|
%end;
|
||||||
%mend mf_getplatform;
|
%mend mf_getplatform;
|
||||||
|
|||||||
@@ -28,15 +28,17 @@
|
|||||||
be 8 characters, so a 7 letter prefix would mean `maxtries` should be 10.
|
be 8 characters, so a 7 letter prefix would mean `maxtries` should be 10.
|
||||||
if using zero (0) as the prefix, a native assignment is used.
|
if using zero (0) as the prefix, a native assignment is used.
|
||||||
@param [in] maxtries= (1000) the last part of the libref. Must be an integer.
|
@param [in] maxtries= (1000) the last part of the libref. Must be an integer.
|
||||||
|
@param [in] lrecl= (32767) Provide a default lrecl with which to initialise
|
||||||
|
the generated fileref.
|
||||||
|
|
||||||
@version 9.2
|
@version 9.2
|
||||||
@author Allan Bowe
|
@author Allan Bowe
|
||||||
**/
|
**/
|
||||||
|
|
||||||
%macro mf_getuniquefileref(prefix=_,maxtries=1000);
|
%macro mf_getuniquefileref(prefix=_,maxtries=1000,lrecl=32767);
|
||||||
%local rc fname;
|
%local rc fname;
|
||||||
%if &prefix=0 %then %do;
|
%if &prefix=0 %then %do;
|
||||||
%let rc=%sysfunc(filename(fname,,temp));
|
%let rc=%sysfunc(filename(fname,,temp,lrecl=&lrecl));
|
||||||
%if &rc %then %put %sysfunc(sysmsg());
|
%if &rc %then %put %sysfunc(sysmsg());
|
||||||
&fname
|
&fname
|
||||||
%end;
|
%end;
|
||||||
@@ -47,7 +49,7 @@
|
|||||||
%do x=0 %to &maxtries;
|
%do x=0 %to &maxtries;
|
||||||
%let fname=&prefix%substr(%sysfunc(ranuni(0)),3,&len);
|
%let fname=&prefix%substr(%sysfunc(ranuni(0)),3,&len);
|
||||||
%if %sysfunc(fileref(&fname)) > 0 %then %do;
|
%if %sysfunc(fileref(&fname)) > 0 %then %do;
|
||||||
%let rc=%sysfunc(filename(fname,,temp));
|
%let rc=%sysfunc(filename(fname,,temp,lrecl=&lrecl));
|
||||||
%if &rc %then %put %sysfunc(sysmsg());
|
%if &rc %then %put %sysfunc(sysmsg());
|
||||||
&fname
|
&fname
|
||||||
%return;
|
%return;
|
||||||
|
|||||||
@@ -3,12 +3,12 @@
|
|||||||
@brief Returns an unused libref
|
@brief Returns an unused libref
|
||||||
@details Use as follows:
|
@details Use as follows:
|
||||||
|
|
||||||
libname mclib0 (work);
|
libname mclib0 (work);
|
||||||
libname mclib1 (work);
|
libname mclib1 (work);
|
||||||
libname mclib2 (work);
|
libname mclib2 (work);
|
||||||
|
|
||||||
%let libref=%mf_getuniquelibref();
|
%let libref=%mf_getuniquelibref();
|
||||||
%put &=libref;
|
%put &=libref;
|
||||||
|
|
||||||
which returns:
|
which returns:
|
||||||
|
|
||||||
|
|||||||
@@ -23,18 +23,19 @@
|
|||||||
@author Allan Bowe
|
@author Allan Bowe
|
||||||
**/
|
**/
|
||||||
|
|
||||||
%macro mf_getuser(type=META
|
%macro mf_getuser(
|
||||||
)/*/STORE SOURCE*/;
|
)/*/STORE SOURCE*/;
|
||||||
%local user metavar;
|
%local user;
|
||||||
%if &type=OS %then %let metavar=_secureusername;
|
|
||||||
%else %let metavar=_metaperson;
|
|
||||||
|
|
||||||
%if %symexist(SYS_COMPUTE_SESSION_OWNER) %then %let user=&SYS_COMPUTE_SESSION_OWNER;
|
%if %symexist(_sasjs_username) %then %let user=&_sasjs_username;
|
||||||
%else %if %symexist(&metavar) %then %do;
|
%else %if %symexist(SYS_COMPUTE_SESSION_OWNER) %then %do;
|
||||||
%if %length(&&&metavar)=0 %then %let user=&sysuserid;
|
%let user=&SYS_COMPUTE_SESSION_OWNER;
|
||||||
|
%end;
|
||||||
|
%else %if %symexist(_metaperson) %then %do;
|
||||||
|
%if %length(&_metaperson)=0 %then %let user=&sysuserid;
|
||||||
/* sometimes SAS will add @domain extension - remove for consistency */
|
/* sometimes SAS will add @domain extension - remove for consistency */
|
||||||
/* but be sure to quote in case of usernames with commas */
|
/* but be sure to quote in case of usernames with commas */
|
||||||
%else %let user=%unquote(%scan(%quote(&&&metavar),1,@));
|
%else %let user=%unquote(%scan(%quote(&_metaperson),1,@));
|
||||||
%end;
|
%end;
|
||||||
%else %let user=&sysuserid;
|
%else %let user=&sysuserid;
|
||||||
|
|
||||||
|
|||||||
@@ -2,31 +2,48 @@
|
|||||||
@file
|
@file
|
||||||
@brief Returns number of variables in a dataset
|
@brief Returns number of variables in a dataset
|
||||||
@details Useful to identify those renagade datasets that have no columns!
|
@details Useful to identify those renagade datasets that have no columns!
|
||||||
|
Can also be used to count for numeric, or character columns
|
||||||
|
|
||||||
%put Number of Variables=%mf_getvarcount(sashelp.class);
|
%put Number of Variables=%mf_getvarcount(sashelp.class);
|
||||||
|
%put Character Variables=%mf_getvarcount(sashelp.class,typefilter=C);
|
||||||
|
%put Numeric Variables = %mf_getvarcount(sashelp.class,typefilter=N);
|
||||||
|
|
||||||
returns:
|
returns:
|
||||||
> Number of Variables=4
|
> Number of Variables=4
|
||||||
|
|
||||||
@param libds Two part dataset (or view) reference.
|
|
||||||
|
@param [in] libds Two part dataset (or view) reference.
|
||||||
|
@param [in] typefilter= (A) Filter for certain types of column. Valid values:
|
||||||
|
@li A Count All columns
|
||||||
|
@li C Count Character columns only
|
||||||
|
@li N Count Numeric columns only
|
||||||
|
|
||||||
@version 9.2
|
@version 9.2
|
||||||
@author Allan Bowe
|
@author Allan Bowe
|
||||||
|
|
||||||
**/
|
**/
|
||||||
|
|
||||||
%macro mf_getvarcount(libds
|
%macro mf_getvarcount(libds,typefilter=A
|
||||||
)/*/STORE SOURCE*/;
|
)/*/STORE SOURCE*/;
|
||||||
%local dsid nvars rc ;
|
%local dsid nvars rc outcnt x;
|
||||||
%let dsid=%sysfunc(open(&libds));
|
%let dsid=%sysfunc(open(&libds));
|
||||||
%let nvars=.;
|
%let nvars=.;
|
||||||
|
%let outcnt=0;
|
||||||
|
%let typefilter=%upcase(&typefilter);
|
||||||
%if &dsid %then %do;
|
%if &dsid %then %do;
|
||||||
%let nvars=%sysfunc(attrn(&dsid,NVARS));
|
%let nvars=%sysfunc(attrn(&dsid,NVARS));
|
||||||
|
%if &typefilter=A %then %let outcnt=&nvars;
|
||||||
|
%else %if &nvars>0 %then %do x=1 %to &nvars;
|
||||||
|
/* increment based on variable type */
|
||||||
|
%if %sysfunc(vartype(&dsid,&x))=&typefilter %then %do;
|
||||||
|
%let outcnt=%eval(&outcnt+1);
|
||||||
|
%end;
|
||||||
|
%end;
|
||||||
%let rc=%sysfunc(close(&dsid));
|
%let rc=%sysfunc(close(&dsid));
|
||||||
%end;
|
%end;
|
||||||
%else %do;
|
%else %do;
|
||||||
%put unable to open &libds (rc=&dsid);
|
%put unable to open &libds (rc=&dsid);
|
||||||
%let rc=%sysfunc(close(&dsid));
|
%let rc=%sysfunc(close(&dsid));
|
||||||
%end;
|
%end;
|
||||||
&nvars
|
&outcnt
|
||||||
%mend mf_getvarcount;
|
%mend mf_getvarcount;
|
||||||
29
base/mf_increment.sas
Normal file
29
base/mf_increment.sas
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
/**
|
||||||
|
@file
|
||||||
|
@brief Increments a macro variable
|
||||||
|
@details Useful outside of do-loops - will increment a macro variable every
|
||||||
|
time it is called.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
%let cnt=1;
|
||||||
|
%put We have run %mf_increment(cnt) lines;
|
||||||
|
%put Now we have run %mf_increment(cnt) lines;
|
||||||
|
%put There are %mf_increment(cnt) lines in total;
|
||||||
|
|
||||||
|
@param [in] MACRO_NAME the name of the macro variable to increment
|
||||||
|
@param [in] ITER= The amount to add or subtract to the macro
|
||||||
|
|
||||||
|
<h4> Related Files </h4>
|
||||||
|
@li mf_increment.test.sas
|
||||||
|
|
||||||
|
**/
|
||||||
|
|
||||||
|
%macro mf_increment(macro_name,incr=1);
|
||||||
|
|
||||||
|
/* iterate the value */
|
||||||
|
%let ¯o_name=%eval(&&¯o_name+&incr);
|
||||||
|
/* return the value */
|
||||||
|
&&¯o_name
|
||||||
|
|
||||||
|
%mend mf_increment;
|
||||||
@@ -20,8 +20,11 @@
|
|||||||
|
|
||||||
%macro mf_isint(arg
|
%macro mf_isint(arg
|
||||||
)/*/STORE SOURCE*/;
|
)/*/STORE SOURCE*/;
|
||||||
/* remove minus sign if exists */
|
|
||||||
|
|
||||||
|
/* blank val is not an integer */
|
||||||
|
%if "&arg"="" %then %do;0%return;%end;
|
||||||
|
|
||||||
|
/* remove minus sign if exists */
|
||||||
%local val;
|
%local val;
|
||||||
%if "%substr(%str(&arg),1,1)"="-" %then %let val=%substr(%str(&arg),2);
|
%if "%substr(%str(&arg),1,1)"="-" %then %let val=%substr(%str(&arg),2);
|
||||||
%else %let val=&arg;
|
%else %let val=&arg;
|
||||||
@@ -30,4 +33,4 @@
|
|||||||
%if %sysfunc(findc(%str(&val),,kd)) %then %do;0%end;
|
%if %sysfunc(findc(%str(&val),,kd)) %then %do;0%end;
|
||||||
%else %do;1%end;
|
%else %do;1%end;
|
||||||
|
|
||||||
%mend mf_isint;
|
%mend mf_isint;
|
||||||
|
|||||||
@@ -8,23 +8,29 @@
|
|||||||
|
|
||||||
The method used varies according to the context. Important points:
|
The method used varies according to the context. Important points:
|
||||||
|
|
||||||
@li should not use endsas or abort cancel in 9.4m3 environments as this can
|
@li should not use endsas or abort cancel in 9.4m3 WIN environments as this
|
||||||
cause hung multibridge sessions and result in a frozen STP server
|
can cause hung multibridge sessions and result in a frozen STP server
|
||||||
|
@li The use of endsas in 9.4m6+ windows environments for POST requests to the
|
||||||
|
STP server can result in an empty response body
|
||||||
@li should not use endsas in viya 3.5 as this destroys the session and cannot
|
@li should not use endsas in viya 3.5 as this destroys the session and cannot
|
||||||
fetch results (although both mv_getjoblog.sas and the @sasjs/adapter will
|
fetch results (although both mv_getjoblog.sas and the @sasjs/adapter will
|
||||||
recognise this and fetch the log of the parent session instead)
|
recognise this and fetch the log of the parent session instead)
|
||||||
@li STP environments must finish cleanly to avoid the log being sent to
|
@li STP environments must finish cleanly to avoid the log being sent to
|
||||||
_webout. To assist with this, we also run stpsrvset('program error', 0)
|
_webout. To assist with this, we also run stpsrvset('program error', 0)
|
||||||
and set SYSCC=0. We take a unique "soft abort" approach - we open a macro
|
and set SYSCC=0.
|
||||||
|
Where possible, we take a unique "soft abort" approach - we open a macro
|
||||||
but don't close it! This works everywhere EXCEPT inside a \%include inside
|
but don't close it! This works everywhere EXCEPT inside a \%include inside
|
||||||
a macro. For that, we recommend you use mp_include.sas to perform the
|
a macro. For that, we recommend you use mp_include.sas to perform the
|
||||||
include, and then call \%mp_abort(mode=INCLUDE) from the source program (ie,
|
include, and then call \%mp_abort(mode=INCLUDE) from the source program (ie,
|
||||||
OUTSIDE of the top-parent macro).
|
OUTSIDE of the top-parent macro).
|
||||||
|
The soft abort has become ineffective in 9.4m6 WINDOWS environments. We are
|
||||||
|
currently investigating approaches to deal with this.
|
||||||
|
|
||||||
|
|
||||||
@param mac= to contain the name of the calling macro
|
@param mac= (mp_abort.sas) To contain the name of the calling macro. Do not
|
||||||
|
use &sysmacroname as this will always resolve to MP_ABORT.
|
||||||
@param msg= message to be returned
|
@param msg= message to be returned
|
||||||
@param iftrue= supply a condition under which the macro should be executed.
|
@param iftrue= (1=1) Supply a condition for which the macro should be executed
|
||||||
@param errds= (work.mp_abort_errds) There is no clean way to end a process
|
@param errds= (work.mp_abort_errds) There is no clean way to end a process
|
||||||
within a %include called within a macro. Furthermore, there is no way to
|
within a %include called within a macro. Furthermore, there is no way to
|
||||||
test if a macro is called within a %include. To handle this particular
|
test if a macro is called within a %include. To handle this particular
|
||||||
@@ -45,11 +51,12 @@
|
|||||||
@li REGULAR (default)
|
@li REGULAR (default)
|
||||||
@li INCLUDE
|
@li INCLUDE
|
||||||
|
|
||||||
|
@version 9.4
|
||||||
|
@author Allan Bowe
|
||||||
|
|
||||||
<h4> Related Macros </h4>
|
<h4> Related Macros </h4>
|
||||||
@li mp_include.sas
|
@li mp_include.sas
|
||||||
|
|
||||||
@version 9.4
|
|
||||||
@author Allan Bowe
|
|
||||||
@cond
|
@cond
|
||||||
**/
|
**/
|
||||||
|
|
||||||
@@ -58,169 +65,197 @@
|
|||||||
, mode=REGULAR
|
, mode=REGULAR
|
||||||
)/*/STORE SOURCE*/;
|
)/*/STORE SOURCE*/;
|
||||||
|
|
||||||
%global sysprocessmode sysprocessname;
|
%global sysprocessmode sysprocessname sasjs_stpsrv_header_loc sasjsprocessmode;
|
||||||
|
%local fref fid i;
|
||||||
|
|
||||||
%if not(%eval(%unquote(&iftrue))) %then %return;
|
%if not(%eval(%unquote(&iftrue))) %then %return;
|
||||||
|
|
||||||
%put NOTE: /// mp_abort macro executing //;
|
%put NOTE: /// mp_abort macro executing //;
|
||||||
%if %length(&mac)>0 %then %put NOTE- called by &mac;
|
%if %length(&mac)>0 %then %put NOTE- called by &mac;
|
||||||
%put NOTE - &msg;
|
%put NOTE - &msg;
|
||||||
|
|
||||||
%if %symexist(_SYSINCLUDEFILEDEVICE)
|
%if %symexist(_SYSINCLUDEFILEDEVICE)
|
||||||
/* abort cancel FILE does not restart outside the INCLUDE on Viya 3.5 */
|
/* abort cancel FILE does not restart outside the INCLUDE on Viya 3.5 */
|
||||||
and "&SYSPROCESSNAME " ne "Compute Server "
|
and %superq(SYSPROCESSNAME) ne %str(Compute Server)
|
||||||
|
%then %do;
|
||||||
|
%if "*&_SYSINCLUDEFILEDEVICE*" ne "**" %then %do;
|
||||||
|
data &errds;
|
||||||
|
iftrue='1=1';
|
||||||
|
length mac $100 msg $5000;
|
||||||
|
mac=symget('mac');
|
||||||
|
msg=symget('msg');
|
||||||
|
run;
|
||||||
|
data _null_;
|
||||||
|
abort cancel FILE;
|
||||||
|
run;
|
||||||
|
%return;
|
||||||
|
%end;
|
||||||
|
%end;
|
||||||
|
|
||||||
|
/* Web App Context */
|
||||||
|
%if %symexist(_PROGRAM)
|
||||||
|
or %superq(SYSPROCESSNAME) = %str(Compute Server)
|
||||||
|
or &mode=INCLUDE
|
||||||
|
%then %do;
|
||||||
|
options obs=max replace mprint;
|
||||||
|
%if "%substr(&sysver,1,1)" ne "4" and "%substr(&sysver,1,1)" ne "5"
|
||||||
%then %do;
|
%then %do;
|
||||||
%if "*&_SYSINCLUDEFILEDEVICE*" ne "**" %then %do;
|
options nosyntaxcheck;
|
||||||
data &errds;
|
%end;
|
||||||
iftrue='1=1';
|
|
||||||
length mac $100 msg $5000;
|
%if &mode=INCLUDE %then %do;
|
||||||
mac=symget('mac');
|
%if %sysfunc(exist(&errds))=1 %then %do;
|
||||||
msg=symget('msg');
|
|
||||||
run;
|
|
||||||
data _null_;
|
data _null_;
|
||||||
abort cancel FILE;
|
set &errds;
|
||||||
|
call symputx('iftrue',iftrue,'l');
|
||||||
|
call symputx('mac',mac,'l');
|
||||||
|
call symputx('msg',msg,'l');
|
||||||
|
putlog (_all_)(=);
|
||||||
run;
|
run;
|
||||||
|
%if (&iftrue)=0 %then %return;
|
||||||
|
%end;
|
||||||
|
%else %do;
|
||||||
|
%put &sysmacroname: No include errors found;
|
||||||
%return;
|
%return;
|
||||||
%end;
|
%end;
|
||||||
%end;
|
%end;
|
||||||
|
|
||||||
/* Stored Process Server web app context */
|
/* extract log errs / warns, if exist */
|
||||||
%if %symexist(_METAFOLDER)
|
%local logloc logline;
|
||||||
or "&SYSPROCESSNAME "="Compute Server "
|
%global logmsg; /* capture global messages */
|
||||||
or &mode=INCLUDE
|
%if %symexist(SYSPRINTTOLOG) %then %let logloc=&SYSPRINTTOLOG;
|
||||||
%then %do;
|
%else %let logloc=%qsysfunc(getoption(LOG));
|
||||||
options obs=max replace nosyntaxcheck mprint;
|
proc printto log=log;run;
|
||||||
%if &mode=INCLUDE %then %do;
|
%let logline=0;
|
||||||
%if %sysfunc(exist(&errds))=1 %then %do;
|
%if %length(&logloc)>0 %then %do;
|
||||||
data _null_;
|
data _null_;
|
||||||
set &errds;
|
infile &logloc lrecl=5000;
|
||||||
call symputx('iftrue',iftrue,'l');
|
input; putlog _infile_;
|
||||||
call symputx('mac',mac,'l');
|
i=1;
|
||||||
call symputx('msg',msg,'l');
|
retain logonce 0;
|
||||||
putlog (_all_)(=);
|
if (
|
||||||
run;
|
_infile_=:"%str(WARN)ING" or _infile_=:"%str(ERR)OR"
|
||||||
%if (&iftrue)=0 %then %return;
|
) and logonce=0 then
|
||||||
%end;
|
do;
|
||||||
%else %do;
|
call symputx('logline',_n_);
|
||||||
%put &sysmacroname: No include errors found;
|
logonce+1;
|
||||||
%return;
|
end;
|
||||||
%end;
|
run;
|
||||||
%end;
|
/* capture log including lines BEFORE the err */
|
||||||
|
%if &logline>0 %then %do;
|
||||||
/* extract log errs / warns, 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;
|
|
||||||
%let logline=0;
|
|
||||||
%if %length(&logloc)>0 %then %do;
|
|
||||||
data _null_;
|
data _null_;
|
||||||
infile &logloc lrecl=5000;
|
infile &logloc lrecl=5000;
|
||||||
input; putlog _infile_;
|
input;
|
||||||
i=1;
|
i=1;
|
||||||
retain logonce 0;
|
stoploop=0;
|
||||||
if (
|
if _n_ ge &logline-15 and stoploop=0 then do until (i>22);
|
||||||
_infile_=:"%str(WARN)ING" or _infile_=:"%str(ERR)OR"
|
call symputx('logmsg',catx('\n',symget('logmsg'),_infile_));
|
||||||
) 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;
|
input;
|
||||||
i=1;
|
i+1;
|
||||||
stoploop=0;
|
stoploop=1;
|
||||||
if _n_ ge &logline-15 and stoploop=0 then do until (i>22);
|
end;
|
||||||
call symputx('logmsg',catx('\n',symget('logmsg'),_infile_));
|
if stoploop=1 then stop;
|
||||||
input;
|
|
||||||
i+1;
|
|
||||||
stoploop=1;
|
|
||||||
end;
|
|
||||||
if stoploop=1 then stop;
|
|
||||||
run;
|
|
||||||
%end;
|
|
||||||
%end;
|
|
||||||
|
|
||||||
%if %symexist(SYS_JES_JOB_URI) %then %do;
|
|
||||||
/* setup webout */
|
|
||||||
OPTIONS NOBOMFILE;
|
|
||||||
%if "X&SYS_JES_JOB_URI.X"="XX" %then %do;
|
|
||||||
filename _webout temp lrecl=999999 mod;
|
|
||||||
%end;
|
|
||||||
%else %do;
|
|
||||||
filename _webout filesrvc parenturi="&SYS_JES_JOB_URI"
|
|
||||||
name="_webout.json" lrecl=999999 mod;
|
|
||||||
%end;
|
|
||||||
%end;
|
|
||||||
|
|
||||||
/* send response in SASjs JSON format */
|
|
||||||
data _null_;
|
|
||||||
file _webout mod lrecl=32000 encoding='utf-8';
|
|
||||||
length msg syswarningtext syserrortext $32767 ;
|
|
||||||
sasdatetime=datetime();
|
|
||||||
msg=symget('msg');
|
|
||||||
%if &logline>0 %then %do;
|
|
||||||
msg=cats(msg,'\n\nLog Extract:\n',symget('logmsg'));
|
|
||||||
%end;
|
|
||||||
/* escape the escapes */
|
|
||||||
msg=tranwrd(msg,'\','\\');
|
|
||||||
/* 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=quote(trim(symget('_debug')));
|
|
||||||
else debug='""';
|
|
||||||
put '>>weboutBEGIN<<';
|
|
||||||
put '{"SYSDATE" : "' "&SYSDATE" '"';
|
|
||||||
put ',"SYSTIME" : "' "&SYSTIME" '"';
|
|
||||||
put ',"sasjsAbort" : [{';
|
|
||||||
put ' "MSG":' msg ;
|
|
||||||
put ' ,"MAC": "' "&mac" '"}]';
|
|
||||||
put ",""SYSUSERID"" : ""&sysuserid"" ";
|
|
||||||
put ',"_DEBUG":' debug ;
|
|
||||||
if symexist('_metauser') then do;
|
|
||||||
_METAUSER=quote(trim(symget('_METAUSER')));
|
|
||||||
put ",""_METAUSER"": " _METAUSER;
|
|
||||||
_METAPERSON=quote(trim(symget('_METAPERSON')));
|
|
||||||
put ',"_METAPERSON": ' _METAPERSON;
|
|
||||||
end;
|
|
||||||
if symexist('SYS_JES_JOB_URI') then do;
|
|
||||||
SYS_JES_JOB_URI=quote(trim(symget('SYS_JES_JOB_URI')));
|
|
||||||
put ',"SYS_JES_JOB_URI": ' SYS_JES_JOB_URI;
|
|
||||||
end;
|
|
||||||
_PROGRAM=quote(trim(resolve(symget('_PROGRAM'))));
|
|
||||||
put ',"_PROGRAM" : ' _PROGRAM ;
|
|
||||||
put ",""SYSCC"" : ""&syscc"" ";
|
|
||||||
syserrortext=quote(trim(symget('syserrortext')));
|
|
||||||
put ",""SYSERRORTEXT"" : " syserrortext;
|
|
||||||
put ",""SYSHOSTNAME"" : ""&syshostname"" ";
|
|
||||||
put ",""SYSJOBID"" : ""&sysjobid"" ";
|
|
||||||
put ",""SYSSCPL"" : ""&sysscpl"" ";
|
|
||||||
put ",""SYSSITE"" : ""&syssite"" ";
|
|
||||||
sysvlong=quote(trim(symget('sysvlong')));
|
|
||||||
put ',"SYSVLONG" : ' sysvlong;
|
|
||||||
syswarningtext=quote(trim(symget('syswarningtext')));
|
|
||||||
put ",""SYSWARNINGTEXT"" : " syswarningtext;
|
|
||||||
put ',"END_DTTM" : "' "%sysfunc(datetime(),E8601DT26.6)" '" ';
|
|
||||||
put "}" @;
|
|
||||||
put '>>weboutEND<<';
|
|
||||||
run;
|
|
||||||
|
|
||||||
%put _all_;
|
|
||||||
|
|
||||||
%if "&sysprocessmode " = "SAS Stored Process Server " %then %do;
|
|
||||||
data _null_;
|
|
||||||
putlog 'stpsrvset program err and syscc';
|
|
||||||
rc=stpsrvset('program error', 0);
|
|
||||||
call symputx("syscc",0,"g");
|
|
||||||
run;
|
run;
|
||||||
|
%end;
|
||||||
|
%end;
|
||||||
|
|
||||||
|
%if %symexist(SYS_JES_JOB_URI) %then %do;
|
||||||
|
/* setup webout for Viya */
|
||||||
|
options nobomfile;
|
||||||
|
%if "X&SYS_JES_JOB_URI.X"="XX" %then %do;
|
||||||
|
filename _webout temp lrecl=999999 mod;
|
||||||
|
%end;
|
||||||
|
%else %do;
|
||||||
|
filename _webout filesrvc parenturi="&SYS_JES_JOB_URI"
|
||||||
|
name="_webout.json" lrecl=999999 mod;
|
||||||
|
%end;
|
||||||
|
%end;
|
||||||
|
%else %if %sysfunc(filename(fref,&sasjs_stpsrv_header_loc))=0 %then %do;
|
||||||
|
options nobomfile;
|
||||||
|
/* set up http header for SASjs Server */
|
||||||
|
%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(Content-Type: application/json)));
|
||||||
|
%let rc=%sysfunc(fwrite(&fid));
|
||||||
|
%let rc=%sysfunc(fclose(&fid));
|
||||||
|
%let rc=%sysfunc(filename(&fref));
|
||||||
|
%end;
|
||||||
|
|
||||||
|
/* send response in SASjs JSON format */
|
||||||
|
data _null_;
|
||||||
|
file _webout mod lrecl=32000 encoding='utf-8';
|
||||||
|
length msg syswarningtext syserrortext $32767 mode $10 ;
|
||||||
|
sasdatetime=datetime();
|
||||||
|
msg=symget('msg');
|
||||||
|
%if &logline>0 %then %do;
|
||||||
|
msg=cats(msg,'\n\nLog Extract:\n',symget('logmsg'));
|
||||||
|
%end;
|
||||||
|
/* escape the escapes */
|
||||||
|
msg=tranwrd(msg,'\','\\');
|
||||||
|
/* 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=quote(trim(symget('_debug')));
|
||||||
|
else debug='""';
|
||||||
|
if symget('sasjsprocessmode')='Stored Program' then mode='SASJS';
|
||||||
|
if mode ne 'SASJS' then put '>>weboutBEGIN<<';
|
||||||
|
put '{"SYSDATE" : "' "&SYSDATE" '"';
|
||||||
|
put ',"SYSTIME" : "' "&SYSTIME" '"';
|
||||||
|
put ',"sasjsAbort" : [{';
|
||||||
|
put ' "MSG":' msg ;
|
||||||
|
put ' ,"MAC": "' "&mac" '"}]';
|
||||||
|
put ",""SYSUSERID"" : ""&sysuserid"" ";
|
||||||
|
put ',"_DEBUG":' debug ;
|
||||||
|
if symexist('_metauser') then do;
|
||||||
|
_METAUSER=quote(trim(symget('_METAUSER')));
|
||||||
|
put ",""_METAUSER"": " _METAUSER;
|
||||||
|
_METAPERSON=quote(trim(symget('_METAPERSON')));
|
||||||
|
put ',"_METAPERSON": ' _METAPERSON;
|
||||||
|
end;
|
||||||
|
if symexist('SYS_JES_JOB_URI') then do;
|
||||||
|
SYS_JES_JOB_URI=quote(trim(symget('SYS_JES_JOB_URI')));
|
||||||
|
put ',"SYS_JES_JOB_URI": ' SYS_JES_JOB_URI;
|
||||||
|
end;
|
||||||
|
_PROGRAM=quote(trim(resolve(symget('_PROGRAM'))));
|
||||||
|
put ',"_PROGRAM" : ' _PROGRAM ;
|
||||||
|
put ",""SYSCC"" : ""&syscc"" ";
|
||||||
|
syserrortext=cats('"',tranwrd(symget('syserrortext'),'"','\"'),'"');
|
||||||
|
put ",""SYSERRORTEXT"" : " syserrortext;
|
||||||
|
put ",""SYSHOSTNAME"" : ""&syshostname"" ";
|
||||||
|
put ",""SYSJOBID"" : ""&sysjobid"" ";
|
||||||
|
put ",""SYSSCPL"" : ""&sysscpl"" ";
|
||||||
|
put ",""SYSSITE"" : ""&syssite"" ";
|
||||||
|
sysvlong=quote(trim(symget('sysvlong')));
|
||||||
|
put ',"SYSVLONG" : ' sysvlong;
|
||||||
|
syswarningtext=cats('"',tranwrd(symget('syswarningtext'),'"','\"'),'"');
|
||||||
|
put ",""SYSWARNINGTEXT"" : " syswarningtext;
|
||||||
|
put ',"END_DTTM" : "' "%sysfunc(datetime(),E8601DT26.6)" '" ';
|
||||||
|
put "}" ;
|
||||||
|
if mode ne 'SASJS' then put '>>weboutEND<<';
|
||||||
|
run;
|
||||||
|
|
||||||
|
%put _all_;
|
||||||
|
|
||||||
|
%if "&sysprocessmode " = "SAS Stored Process Server " %then %do;
|
||||||
|
data _null_;
|
||||||
|
putlog 'stpsrvset program err and syscc';
|
||||||
|
rc=stpsrvset('program error', 0);
|
||||||
|
call symputx("syscc",0,"g");
|
||||||
|
run;
|
||||||
|
%if &sysscp=WIN
|
||||||
|
and 1=0 /* deprecating this logic until we figure out a consistent abort */
|
||||||
|
and "%substr(%str(&sysvlong ),1,8)"="9.04.01M"
|
||||||
|
and "%substr(%str(&sysvlong ),9,1)">"5" %then %do;
|
||||||
|
/* skip approach (below) does not work in windows m6+ envs */
|
||||||
|
endsas;
|
||||||
|
%end;
|
||||||
|
%else %do;
|
||||||
/**
|
/**
|
||||||
* endsas kills 9.4m3 deployments by orphaning multibridges.
|
* endsas kills 9.4m3 deployments by orphaning multibridges.
|
||||||
* Abort variants are ungraceful (non zero return code)
|
* Abort variants are ungraceful (non zero return code)
|
||||||
@@ -238,28 +273,29 @@
|
|||||||
run;
|
run;
|
||||||
%inc skip;
|
%inc skip;
|
||||||
%end;
|
%end;
|
||||||
%else %if "&sysprocessmode " = "SAS Compute Server " %then %do;
|
%end;
|
||||||
/* endsas kills the session making it harder to fetch results */
|
%else %if "&sysprocessmode " = "SAS Compute Server " %then %do;
|
||||||
data _null_;
|
/* endsas kills the session making it harder to fetch results */
|
||||||
syswarningtext=symget('syswarningtext');
|
data _null_;
|
||||||
syserrortext=symget('syserrortext');
|
syswarningtext=symget('syswarningtext');
|
||||||
abort_msg=symget('msg');
|
syserrortext=symget('syserrortext');
|
||||||
syscc=symget('syscc');
|
abort_msg=symget('msg');
|
||||||
sysuserid=symget('sysuserid');
|
syscc=symget('syscc');
|
||||||
iftrue=symget('iftrue');
|
sysuserid=symget('sysuserid');
|
||||||
put (_all_)(/=);
|
iftrue=symget('iftrue');
|
||||||
call symputx('syscc',0);
|
put (_all_)(/=);
|
||||||
abort cancel nolist;
|
call symputx('syscc',0);
|
||||||
run;
|
abort cancel nolist;
|
||||||
%end;
|
run;
|
||||||
%else %do;
|
|
||||||
%abort cancel;
|
|
||||||
%end;
|
|
||||||
%end;
|
%end;
|
||||||
%else %do;
|
%else %do;
|
||||||
%put _all_;
|
|
||||||
%abort cancel;
|
%abort cancel;
|
||||||
%end;
|
%end;
|
||||||
|
%end;
|
||||||
|
%else %do;
|
||||||
|
%put _all_;
|
||||||
|
%abort cancel;
|
||||||
|
%end;
|
||||||
%mend mp_abort;
|
%mend mp_abort;
|
||||||
|
|
||||||
/** @endcond */
|
/** @endcond */
|
||||||
|
|||||||
@@ -106,7 +106,7 @@
|
|||||||
|
|
||||||
proc compare
|
proc compare
|
||||||
base=&scopeds(where=(upcase(name) not in (%mf_getquotedstr(&ilist))))
|
base=&scopeds(where=(upcase(name) not in (%mf_getquotedstr(&ilist))))
|
||||||
compare=&ds;
|
compare=&ds noprint;
|
||||||
run;
|
run;
|
||||||
|
|
||||||
%if &sysinfo=0 %then %do;
|
%if &sysinfo=0 %then %do;
|
||||||
|
|||||||
@@ -4,8 +4,8 @@
|
|||||||
@details Reads in a file byte by byte and writes it back out. Is an
|
@details Reads in a file byte by byte and writes it back out. Is an
|
||||||
os-independent method to copy files. In case of naming collision, the
|
os-independent method to copy files. In case of naming collision, the
|
||||||
default filerefs can be modified.
|
default filerefs can be modified.
|
||||||
Based on:
|
Note that if you have a new enough version of SAS, and you don't need features
|
||||||
https://stackoverflow.com/questions/13046116/using-sas-to-copy-a-text-file
|
such as APPEND, you may be better of using the fcopy() function instead.
|
||||||
|
|
||||||
%mp_binarycopy(inloc="/home/me/blah.txt", outref=_webout)
|
%mp_binarycopy(inloc="/home/me/blah.txt", outref=_webout)
|
||||||
|
|
||||||
@@ -31,6 +31,7 @@
|
|||||||
@param [in] mode (CREATE) Valid values:
|
@param [in] mode (CREATE) Valid values:
|
||||||
@li CREATE - Create the file (even if it already exists)
|
@li CREATE - Create the file (even if it already exists)
|
||||||
@li APPEND - Append to the file (don't overwrite)
|
@li APPEND - Append to the file (don't overwrite)
|
||||||
|
@param iftrue= (1=1) Supply a condition for which the macro should be executed
|
||||||
|
|
||||||
@returns nothing
|
@returns nothing
|
||||||
|
|
||||||
@@ -44,15 +45,14 @@
|
|||||||
,inref=____in /* override default to use own filerefs */
|
,inref=____in /* override default to use own filerefs */
|
||||||
,outref=____out /* override default to use own filerefs */
|
,outref=____out /* override default to use own filerefs */
|
||||||
,mode=CREATE
|
,mode=CREATE
|
||||||
|
,iftrue=%str(1=1)
|
||||||
)/*/STORE SOURCE*/;
|
)/*/STORE SOURCE*/;
|
||||||
%local mod outmode;
|
%local mod;
|
||||||
%if &mode=APPEND %then %do;
|
|
||||||
%let mod=mod;
|
%if not(%eval(%unquote(&iftrue))) %then %return;
|
||||||
%let outmode='a';
|
|
||||||
%end;
|
%if &mode=APPEND %then %let mod=mod;
|
||||||
%else %do;
|
|
||||||
%let outmode='o';
|
|
||||||
%end;
|
|
||||||
/* these IN and OUT filerefs can point to anything */
|
/* these IN and OUT filerefs can point to anything */
|
||||||
%if &inref = ____in %then %do;
|
%if &inref = ____in %then %do;
|
||||||
filename &inref &inloc lrecl=1048576 ;
|
filename &inref &inloc lrecl=1048576 ;
|
||||||
@@ -63,22 +63,17 @@
|
|||||||
|
|
||||||
/* copy the file byte-for-byte */
|
/* copy the file byte-for-byte */
|
||||||
data _null_;
|
data _null_;
|
||||||
length filein 8 fileid 8;
|
infile &inref lrecl=1 recfm=n;
|
||||||
filein = fopen("&inref",'I',1,'B');
|
file &outref &mod recfm=n;
|
||||||
fileid = fopen("&outref",&outmode,1,'B');
|
input sourcechar $char1. @@;
|
||||||
rec = '20'x;
|
format sourcechar hex2.;
|
||||||
do while(fread(filein)=0);
|
put sourcechar char1. @@;
|
||||||
rc = fget(filein,rec,1);
|
|
||||||
rc = fput(fileid, rec);
|
|
||||||
rc =fwrite(fileid);
|
|
||||||
end;
|
|
||||||
rc = fclose(filein);
|
|
||||||
rc = fclose(fileid);
|
|
||||||
run;
|
run;
|
||||||
|
|
||||||
%if &inref = ____in %then %do;
|
%if &inref = ____in %then %do;
|
||||||
filename &inref clear;
|
filename &inref clear;
|
||||||
%end;
|
%end;
|
||||||
%if &outref=____out %then %do;
|
%if &outref=____out %then %do;
|
||||||
filename &outref clear;
|
filename &outref clear;
|
||||||
%end;
|
%end;
|
||||||
%mend mp_binarycopy;
|
%mend mp_binarycopy;
|
||||||
|
|||||||
194
base/mp_chop.sas
Normal file
194
base/mp_chop.sas
Normal file
@@ -0,0 +1,194 @@
|
|||||||
|
/**
|
||||||
|
@file
|
||||||
|
@brief Splits a file of ANY SIZE by reference to a search string.
|
||||||
|
@details Provide a fileref and a search string to chop off part of a file.
|
||||||
|
|
||||||
|
Works by reading in the file byte by byte, then marking the beginning and end
|
||||||
|
of each matched string, before finally doing the chop.
|
||||||
|
|
||||||
|
Choose whether to keep the FIRST or the LAST section of the file. Optionally,
|
||||||
|
use an OFFSET to fix the precise chop point.
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
|
||||||
|
%let src="%sysfunc(pathname(work))/file.txt";
|
||||||
|
%let str=Chop here!;
|
||||||
|
%let out1="%sysfunc(pathname(work))/file1.txt";
|
||||||
|
%let out2="%sysfunc(pathname(work))/file2.txt";
|
||||||
|
%let out3="%sysfunc(pathname(work))/file3.txt";
|
||||||
|
%let out4="%sysfunc(pathname(work))/file4.txt";
|
||||||
|
|
||||||
|
data _null_;
|
||||||
|
file &src;
|
||||||
|
put "startsection&str.endsection";
|
||||||
|
run;
|
||||||
|
|
||||||
|
%mp_chop(&src, matchvar=str, keep=FIRST, outfile=&out1)
|
||||||
|
%mp_chop(&src, matchvar=str, keep=LAST, outfile=&out2)
|
||||||
|
%mp_chop(&src, matchvar=str, keep=FIRST, matchpoint=END, outfile=&out3)
|
||||||
|
%mp_chop(&src, matchvar=str, keep=LAST, matchpoint=END, outfile=&out4)
|
||||||
|
|
||||||
|
filename results (&out1 &out2 &out3 &out4);
|
||||||
|
data _null_;
|
||||||
|
infile results;
|
||||||
|
input;
|
||||||
|
list;
|
||||||
|
run;
|
||||||
|
|
||||||
|
Results:
|
||||||
|
@li `startsection`
|
||||||
|
@li `Chop here!endsection`
|
||||||
|
@li `startsectionChop here!`
|
||||||
|
@li `endsection`
|
||||||
|
|
||||||
|
For more examples, see mp_chop.test.sas
|
||||||
|
|
||||||
|
@param [in] infile The QUOTED path to the file on which to perform the chop
|
||||||
|
@param [in] matchvar= Macro variable NAME containing the string to split by
|
||||||
|
@param [in] matchpoint= (START) Valid values:
|
||||||
|
@li START - chop at the beginning of the string in `matchvar`.
|
||||||
|
@li END - chop at the end of the string in `matchvar`.
|
||||||
|
@param [in] offset= (0) An adjustment to the precise chop location, by
|
||||||
|
by reference to the `matchpoint`. Should be a positive or negative integer.
|
||||||
|
@param [in] keep= (FIRST) Valid values:
|
||||||
|
@li FIRST - keep the section of the file before the chop
|
||||||
|
@li LAST - keep the section of the file after the chop
|
||||||
|
@param [in] mdebug= (0) Set to 1 to provide macro debugging
|
||||||
|
@param outfile= (0) Optional QUOTED path to the adjusted output file (avoids
|
||||||
|
overwriting the first file).
|
||||||
|
|
||||||
|
<h4> SAS Macros </h4>
|
||||||
|
@li mf_getuniquefileref.sas
|
||||||
|
@li mf_getuniquename.sas
|
||||||
|
|
||||||
|
<h4> Related Macros </h4>
|
||||||
|
@li mp_abort.sas
|
||||||
|
@li mp_gsubfile.sas
|
||||||
|
@li mp_replace.sas
|
||||||
|
@li mp_chop.test.sas
|
||||||
|
|
||||||
|
@version 9.4
|
||||||
|
@author Allan Bowe
|
||||||
|
|
||||||
|
**/
|
||||||
|
|
||||||
|
%macro mp_chop(infile,
|
||||||
|
matchvar=,
|
||||||
|
matchpoint=START,
|
||||||
|
keep=FIRST,
|
||||||
|
offset=0,
|
||||||
|
mdebug=0,
|
||||||
|
outfile=0
|
||||||
|
)/*/STORE SOURCE*/;
|
||||||
|
|
||||||
|
%local fref0 dttm ds1 outref;
|
||||||
|
%let fref0=%mf_getuniquefileref();
|
||||||
|
%let ds1=%mf_getuniquename(prefix=allchars);
|
||||||
|
%let ds2=%mf_getuniquename(prefix=startmark);
|
||||||
|
|
||||||
|
%if &outfile=0 %then %let outfile=&infile;
|
||||||
|
|
||||||
|
%mp_abort(iftrue= (%length(%superq(&matchvar))=0)
|
||||||
|
,mac=mp_chop.sas
|
||||||
|
,msg=%str(&matchvar is an empty variable)
|
||||||
|
)
|
||||||
|
|
||||||
|
/* START */
|
||||||
|
%let dttm=%sysfunc(datetime());
|
||||||
|
|
||||||
|
filename &fref0 &infile lrecl=1 recfm=n;
|
||||||
|
|
||||||
|
/* create dataset with one char per row */
|
||||||
|
data &ds1;
|
||||||
|
infile &fref0;
|
||||||
|
input sourcechar $char1. @@;
|
||||||
|
format sourcechar hex2.;
|
||||||
|
run;
|
||||||
|
|
||||||
|
/* get start & stop position of first matchvar string (one row, two vars) */
|
||||||
|
data &ds2;
|
||||||
|
/* set find string to length in bytes to cover trailing spaces */
|
||||||
|
length string $ %length(%superq(&matchvar));
|
||||||
|
string =symget("&matchvar");
|
||||||
|
drop string;
|
||||||
|
|
||||||
|
firstchar=char(string,1);
|
||||||
|
findlen=lengthm(string); /* <- for trailing bytes */
|
||||||
|
|
||||||
|
do _N_=1 to nobs;
|
||||||
|
set &ds1 nobs=nobs point=_N_;
|
||||||
|
if sourcechar=firstchar then do;
|
||||||
|
pos=1;
|
||||||
|
s=0;
|
||||||
|
do point=_N_ to min(_N_ + findlen -1,nobs);
|
||||||
|
set &ds1 point=point;
|
||||||
|
if sourcechar=char(string, pos) then s + 1;
|
||||||
|
else goto _leave_;
|
||||||
|
pos+1;
|
||||||
|
end;
|
||||||
|
_leave_:
|
||||||
|
if s=findlen then do;
|
||||||
|
START =_N_;
|
||||||
|
_N_ =_N_+ s - 1;
|
||||||
|
STOP =_N_;
|
||||||
|
output;
|
||||||
|
/* matched! */
|
||||||
|
stop;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
stop;
|
||||||
|
keep START STOP;
|
||||||
|
run;
|
||||||
|
|
||||||
|
%local split;
|
||||||
|
%let split=0;
|
||||||
|
data _null_;
|
||||||
|
set &ds2;
|
||||||
|
if "&matchpoint"='START' then do;
|
||||||
|
if "&keep"='FIRST' then mp=start;
|
||||||
|
else if "&keep"='LAST' then mp=start-1;
|
||||||
|
end;
|
||||||
|
else if "&matchpoint"='END' then do;
|
||||||
|
if "&keep"='FIRST' then mp=stop+1;
|
||||||
|
else if "&keep"='LAST' then mp=stop;
|
||||||
|
end;
|
||||||
|
split=mp+&offset;
|
||||||
|
call symputx('split',split,'l');
|
||||||
|
%if &mdebug=1 %then %do;
|
||||||
|
put (_all_)(=);
|
||||||
|
%put &=offset;
|
||||||
|
%end;
|
||||||
|
run;
|
||||||
|
%if &split=0 %then %do;
|
||||||
|
%put &sysmacroname: No match found in &infile for string %superq(&matchvar);
|
||||||
|
%return;
|
||||||
|
%end;
|
||||||
|
|
||||||
|
data _null_;
|
||||||
|
file &outfile recfm=n;
|
||||||
|
set &ds1;
|
||||||
|
%if &keep=FIRST %then %do;
|
||||||
|
if _n_ ge &split then stop;
|
||||||
|
%end;
|
||||||
|
%else %do;
|
||||||
|
if _n_ gt &split;
|
||||||
|
%end;
|
||||||
|
put sourcechar char1.;
|
||||||
|
run;
|
||||||
|
|
||||||
|
%if &mdebug=0 %then %do;
|
||||||
|
filename &fref0 clear;
|
||||||
|
%end;
|
||||||
|
%else %do;
|
||||||
|
data _null_;
|
||||||
|
infile &outfile lrecl=32767;
|
||||||
|
input;
|
||||||
|
list;
|
||||||
|
if _n_>50 then stop;
|
||||||
|
run;
|
||||||
|
%end;
|
||||||
|
/* END */
|
||||||
|
%put &sysmacroname took %sysevalf(%sysfunc(datetime())-&dttm) seconds to run;
|
||||||
|
|
||||||
|
%mend mp_chop;
|
||||||
@@ -16,8 +16,11 @@
|
|||||||
|
|
||||||
%mp_copyfolder(&rootdir,©dir)
|
%mp_copyfolder(&rootdir,©dir)
|
||||||
|
|
||||||
@param source Unquoted path to the folder to copy from.
|
@param [in] source Unquoted path to the folder to copy from.
|
||||||
@param target Unquoted path to the folder to copy to.
|
@param [out] target Unquoted path to the folder to copy to.
|
||||||
|
@param [in] copymax=(MAX) Set to a positive integer to indicate the level of
|
||||||
|
subdirectory copy recursion - eg 3, to go `./3/levels/deep`. For unlimited
|
||||||
|
recursion, set to MAX.
|
||||||
|
|
||||||
<h4> SAS Macros </h4>
|
<h4> SAS Macros </h4>
|
||||||
@li mf_getuniquename.sas
|
@li mf_getuniquename.sas
|
||||||
@@ -31,7 +34,7 @@
|
|||||||
|
|
||||||
**/
|
**/
|
||||||
|
|
||||||
%macro mp_copyfolder(source,target);
|
%macro mp_copyfolder(source,target,copymax=MAX);
|
||||||
|
|
||||||
%mp_abort(iftrue=(%mf_isdir(&source)=0)
|
%mp_abort(iftrue=(%mf_isdir(&source)=0)
|
||||||
,mac=&sysmacroname
|
,mac=&sysmacroname
|
||||||
@@ -50,7 +53,7 @@
|
|||||||
%let tempds=%mf_getuniquename();
|
%let tempds=%mf_getuniquename();
|
||||||
|
|
||||||
/* recursive directory listing */
|
/* recursive directory listing */
|
||||||
%mp_dirlist(path=&source,outds=work.&tempds, maxdepth=MAX)
|
%mp_dirlist(path=&source,outds=work.&tempds,maxdepth=©max)
|
||||||
|
|
||||||
/* create folders and copy content */
|
/* create folders and copy content */
|
||||||
data _null_;
|
data _null_;
|
||||||
@@ -78,4 +81,4 @@
|
|||||||
proc sql;
|
proc sql;
|
||||||
drop table work.&tempds;
|
drop table work.&tempds;
|
||||||
|
|
||||||
%mend mp_copyfolder;
|
%mend mp_copyfolder;
|
||||||
|
|||||||
@@ -30,6 +30,7 @@
|
|||||||
@li mddl_dc_filtersummary.sas
|
@li mddl_dc_filtersummary.sas
|
||||||
@li mddl_dc_locktable.sas
|
@li mddl_dc_locktable.sas
|
||||||
@li mddl_dc_maxkeytable.sas
|
@li mddl_dc_maxkeytable.sas
|
||||||
|
@li mf_getuniquename.sas
|
||||||
|
|
||||||
<h4> Related Macros </h4>
|
<h4> Related Macros </h4>
|
||||||
@li mp_filterstore.sas
|
@li mp_filterstore.sas
|
||||||
@@ -46,7 +47,7 @@
|
|||||||
%macro mp_coretable(table_ref,libds=0
|
%macro mp_coretable(table_ref,libds=0
|
||||||
)/*/STORE SOURCE*/;
|
)/*/STORE SOURCE*/;
|
||||||
%local outds ;
|
%local outds ;
|
||||||
%let outds=%sysfunc(ifc(&libds=0,_data_,&libds));
|
%let outds=%sysfunc(ifc(&libds=0,%mf_getuniquename(),&libds));
|
||||||
proc sql;
|
proc sql;
|
||||||
%if &table_ref=DIFFTABLE %then %do;
|
%if &table_ref=DIFFTABLE %then %do;
|
||||||
%mddl_dc_difftable(libds=&outds)
|
%mddl_dc_difftable(libds=&outds)
|
||||||
@@ -65,7 +66,8 @@ proc sql;
|
|||||||
%end;
|
%end;
|
||||||
|
|
||||||
%if &libds=0 %then %do;
|
%if &libds=0 %then %do;
|
||||||
|
proc sql;
|
||||||
describe table &syslast;
|
describe table &syslast;
|
||||||
drop table &syslast;
|
drop table &syslast;
|
||||||
%end;
|
%end;
|
||||||
%mend mp_coretable;
|
%mend mp_coretable;
|
||||||
|
|||||||
@@ -1,49 +1,13 @@
|
|||||||
/**
|
/**
|
||||||
@file mp_createwebservice.sas
|
@file mp_createwebservice.sas
|
||||||
@brief Create a web service in SAS 9 or Viya
|
@brief Create a web service in SAS 9, Viya or SASjs Server
|
||||||
@details Creates a SASJS ready Stored Process in SAS 9 or Job Execution
|
@details This is actually a wrapper for mx_createwebservice.sas, remaining
|
||||||
Service in SAS Viya
|
for legacy purposes. For new apps, use mx_createwebservice.sas.
|
||||||
|
|
||||||
Usage:
|
|
||||||
|
|
||||||
%* compile macros ;
|
|
||||||
filename mc url "https://raw.githubusercontent.com/sasjs/core/main/all.sas";
|
|
||||||
%inc mc;
|
|
||||||
|
|
||||||
%* write some code;
|
|
||||||
filename ft15f001 temp;
|
|
||||||
parmcards4;
|
|
||||||
%* fetch any data from frontend ;
|
|
||||||
%webout(FETCH)
|
|
||||||
data example1 example2;
|
|
||||||
set sashelp.class;
|
|
||||||
run;
|
|
||||||
%* send data back;
|
|
||||||
%webout(OPEN)
|
|
||||||
%webout(ARR,example1) * Array format, fast, suitable for large tables ;
|
|
||||||
%webout(OBJ,example2) * Object format, easier to work with ;
|
|
||||||
%webout(CLOSE)
|
|
||||||
;;;;
|
|
||||||
%mp_createwebservice(path=/Public/app/common,name=appInit,replace=YES)
|
|
||||||
|
|
||||||
<h4> SAS Macros </h4>
|
<h4> SAS Macros </h4>
|
||||||
@li mf_getplatform.sas
|
@li mx_createwebservice.sas
|
||||||
@li mm_createwebservice.sas
|
|
||||||
@li mv_createwebservice.sas
|
|
||||||
|
|
||||||
@param [in,out] path= The full folder path where the service will be created
|
|
||||||
@param [in,out] name= Service name. Avoid spaces.
|
|
||||||
@param [in] desc= The description of the service (optional)
|
|
||||||
@param [in] precode= Space separated list of filerefs, pointing to the code
|
|
||||||
that needs to be attached to the beginning of the service (optional)
|
|
||||||
@param [in] code= (ft15f001) Space seperated fileref(s) of the actual code to
|
|
||||||
be added
|
|
||||||
@param [in] replace= (YES) Select YES to replace any existing service in that
|
|
||||||
location
|
|
||||||
|
|
||||||
|
|
||||||
@version 9.2
|
|
||||||
@author Allan Bowe
|
|
||||||
|
|
||||||
**/
|
**/
|
||||||
|
|
||||||
@@ -53,33 +17,16 @@ Usage:
|
|||||||
,code=ft15f001
|
,code=ft15f001
|
||||||
,desc=This service was created by the mp_createwebservice macro
|
,desc=This service was created by the mp_createwebservice macro
|
||||||
,replace=YES
|
,replace=YES
|
||||||
|
,mdebug=0
|
||||||
)/*/STORE SOURCE*/;
|
)/*/STORE SOURCE*/;
|
||||||
|
|
||||||
%if &syscc ge 4 %then %do;
|
%mx_createwebservice(path=&path
|
||||||
%put syscc=&syscc - &sysmacroname will not execute in this state;
|
|
||||||
%return;
|
|
||||||
%end;
|
|
||||||
|
|
||||||
%local platform; %let platform=%mf_getplatform();
|
|
||||||
%if &platform=SASVIYA %then %do;
|
|
||||||
%if "&path"="HOME" %then %let path=/Users/&sysuserid/My Folder;
|
|
||||||
%mv_createwebservice(path=&path
|
|
||||||
,name=&name
|
,name=&name
|
||||||
,code=&code
|
|
||||||
,precode=&precode
|
,precode=&precode
|
||||||
|
,code=&code
|
||||||
,desc=&desc
|
,desc=&desc
|
||||||
,replace=&replace
|
,replace=&replace
|
||||||
|
,mdebug=&mdebug
|
||||||
)
|
)
|
||||||
%end;
|
|
||||||
%else %do;
|
|
||||||
%if "&path"="HOME" %then %let path=/User Folders/&sysuserid/My Folder;
|
|
||||||
%mm_createwebservice(path=&path
|
|
||||||
,name=&name
|
|
||||||
,code=&code
|
|
||||||
,precode=&precode
|
|
||||||
,desc=&desc
|
|
||||||
,replace=&replace
|
|
||||||
)
|
|
||||||
%end;
|
|
||||||
|
|
||||||
%mend mp_createwebservice;
|
%mend mp_createwebservice;
|
||||||
|
|||||||
@@ -152,6 +152,7 @@ run;
|
|||||||
data &out_ds;
|
data &out_ds;
|
||||||
set &out_ds;
|
set &out_ds;
|
||||||
length infoname infoval $60 fref $8;
|
length infoname infoval $60 fref $8;
|
||||||
|
if _n_=1 then call missing(fref);
|
||||||
rc=filename(fref,filepath);
|
rc=filename(fref,filepath);
|
||||||
drop rc infoname fid i close fref;
|
drop rc infoname fid i close fref;
|
||||||
if file_or_folder='file' then do;
|
if file_or_folder='file' then do;
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
|
|
||||||
Usage:
|
Usage:
|
||||||
|
|
||||||
%mp_ds2cards(base_ds=sashelp.class
|
%mp_ds2cards(sashelp.class
|
||||||
, tgt_ds=work.class
|
, tgt_ds=work.class
|
||||||
, cards_file= "C:\temp\class.sas"
|
, cards_file= "C:\temp\class.sas"
|
||||||
, showlog=NO
|
, showlog=NO
|
||||||
@@ -23,7 +23,7 @@
|
|||||||
- explicity setting a unix LF
|
- explicity setting a unix LF
|
||||||
- constraints / indexes etc
|
- constraints / indexes etc
|
||||||
|
|
||||||
@param [in] base_ds= Should be two level - eg work.blah. This is the table
|
@param [in] base_ds Should be two level - eg work.blah. This is the table
|
||||||
that is converted to a cards file.
|
that is converted to a cards file.
|
||||||
@param [in] tgt_ds= Table that the generated cards file would create.
|
@param [in] tgt_ds= Table that the generated cards file would create.
|
||||||
Optional - if omitted, will be same as BASE_DS.
|
Optional - if omitted, will be same as BASE_DS.
|
||||||
@@ -48,9 +48,10 @@
|
|||||||
|
|
||||||
@version 9.2
|
@version 9.2
|
||||||
@author Allan Bowe
|
@author Allan Bowe
|
||||||
|
@cond
|
||||||
**/
|
**/
|
||||||
|
|
||||||
%macro mp_ds2cards(base_ds=, tgt_ds=
|
%macro mp_ds2cards(base_ds, tgt_ds=
|
||||||
,cards_file="%sysfunc(pathname(work))/cardgen.sas"
|
,cards_file="%sysfunc(pathname(work))/cardgen.sas"
|
||||||
,maxobs=max
|
,maxobs=max
|
||||||
,random_sample=NO
|
,random_sample=NO
|
||||||
@@ -219,7 +220,8 @@ data _null_;
|
|||||||
put ' @file';
|
put ' @file';
|
||||||
put " @brief Datalines for %upcase(%scan(&base_ds,2)) dataset";
|
put " @brief Datalines for %upcase(%scan(&base_ds,2)) dataset";
|
||||||
put " @details Generated by %nrstr(%%)mp_ds2cards()";
|
put " @details Generated by %nrstr(%%)mp_ds2cards()";
|
||||||
put " Available on github.com/sasjs/core";
|
put " Source: https://github.com/sasjs/core";
|
||||||
|
put ' @cond ';
|
||||||
put '**/';
|
put '**/';
|
||||||
put "data &tgt_ds &indexes;";
|
put "data &tgt_ds &indexes;";
|
||||||
put "attrib ";
|
put "attrib ";
|
||||||
@@ -252,6 +254,7 @@ data _null_;
|
|||||||
;
|
;
|
||||||
%end;
|
%end;
|
||||||
put ";";
|
put ";";
|
||||||
|
put 'missing a b c d e f g h i j k l m n o p q r s t u v w x y z _;';
|
||||||
put "datalines4;";
|
put "datalines4;";
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
@@ -264,6 +267,7 @@ data _null_;
|
|||||||
if __lastobs then do;
|
if __lastobs then do;
|
||||||
put ';;;;';
|
put ';;;;';
|
||||||
put 'run;';
|
put 'run;';
|
||||||
|
put '/** @endcond **/';
|
||||||
stop;
|
stop;
|
||||||
end;
|
end;
|
||||||
run;
|
run;
|
||||||
@@ -283,4 +287,5 @@ quit;
|
|||||||
%put NOTE-;%put NOTE-;
|
%put NOTE-;%put NOTE-;
|
||||||
%put NOTE- %sysfunc(dequote(&cards_file.));
|
%put NOTE- %sysfunc(dequote(&cards_file.));
|
||||||
%put NOTE-;%put NOTE-;
|
%put NOTE-;%put NOTE-;
|
||||||
%mend mp_ds2cards;
|
%mend mp_ds2cards;
|
||||||
|
/** @endcond **/
|
||||||
|
|||||||
@@ -57,6 +57,11 @@
|
|||||||
%local vars;
|
%local vars;
|
||||||
%let vars=%upcase(%mf_getvarlist(&libds));
|
%let vars=%upcase(%mf_getvarlist(&libds));
|
||||||
|
|
||||||
|
%if %trim(X&vars)=X %then %do;
|
||||||
|
%put &sysmacroname: Table &libds has no columns!!;
|
||||||
|
%return;
|
||||||
|
%end;
|
||||||
|
|
||||||
/* create the header row */
|
/* create the header row */
|
||||||
data _null_;
|
data _null_;
|
||||||
file &outref;
|
file &outref;
|
||||||
|
|||||||
@@ -92,7 +92,37 @@ data &outds;
|
|||||||
/*length GROUP_LOGIC SUBGROUP_LOGIC $3 SUBGROUP_ID 8 VARIABLE_NM $32
|
/*length GROUP_LOGIC SUBGROUP_LOGIC $3 SUBGROUP_ID 8 VARIABLE_NM $32
|
||||||
OPERATOR_NM $10 RAW_VALUE $4000;*/
|
OPERATOR_NM $10 RAW_VALUE $4000;*/
|
||||||
set &inds;
|
set &inds;
|
||||||
length reason_cd $4032;
|
length reason_cd $4032 vtype $1 vnum dsid 8 tmp $4000;
|
||||||
|
drop tmp;
|
||||||
|
|
||||||
|
/* quick check to ensure column exists */
|
||||||
|
if upcase(VARIABLE_NM) not in
|
||||||
|
(%upcase(%mf_getvarlist(&targetds,dlm=%str(,),quote=SINGLE)))
|
||||||
|
then do;
|
||||||
|
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;
|
||||||
|
return;
|
||||||
|
end;
|
||||||
|
|
||||||
|
/* need to open the dataset to get the column type */
|
||||||
|
dsid=open("&targetds","i");
|
||||||
|
if dsid>0 then do;
|
||||||
|
vnum=varnum(dsid,VARIABLE_NM);
|
||||||
|
if vnum<1 then do;
|
||||||
|
/* should not happen as was also tested for above */
|
||||||
|
REASON_CD=cats("Variable (",VARIABLE_NM,") not found in &targetds");
|
||||||
|
putlog REASON_CD= dsid=;
|
||||||
|
call symputx('reason_cd',reason_cd,'l');
|
||||||
|
call symputx('nobs',_n_,'l');
|
||||||
|
output;
|
||||||
|
return;
|
||||||
|
end;
|
||||||
|
/* now we can get the type */
|
||||||
|
else vtype=vartype(dsid,vnum);
|
||||||
|
end;
|
||||||
|
|
||||||
/* closed list checks */
|
/* closed list checks */
|
||||||
if GROUP_LOGIC not in ('AND','OR') then do;
|
if GROUP_LOGIC not in ('AND','OR') then do;
|
||||||
@@ -116,17 +146,8 @@ data &outds;
|
|||||||
call symputx('nobs',_n_,'l');
|
call symputx('nobs',_n_,'l');
|
||||||
output;
|
output;
|
||||||
end;
|
end;
|
||||||
if upcase(VARIABLE_NM) not in
|
|
||||||
(%upcase(%mf_getvarlist(&targetds,dlm=%str(,),quote=SINGLE)))
|
|
||||||
then do;
|
|
||||||
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
|
if OPERATOR_NM not in
|
||||||
('=','>','<','<=','>=','BETWEEN','IN','NOT IN','NE','CONTAINS','GE','LE')
|
('=','>','<','<=','>=','NE','GE','LE','BETWEEN','IN','NOT IN','CONTAINS')
|
||||||
then do;
|
then do;
|
||||||
REASON_CD='Invalid OPERATOR_NM: '!!cats(OPERATOR_NM);
|
REASON_CD='Invalid OPERATOR_NM: '!!cats(OPERATOR_NM);
|
||||||
putlog REASON_CD= OPERATOR_NM=;
|
putlog REASON_CD= OPERATOR_NM=;
|
||||||
@@ -135,19 +156,45 @@ data &outds;
|
|||||||
output;
|
output;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
/* special missing logic */
|
||||||
|
if vtype='N'
|
||||||
|
and OPERATOR_NM in ('=','>','<','<=','>=','NE','GE','LE')
|
||||||
|
and cats(upcase(raw_value)) in (
|
||||||
|
'.','.A','.B','.C','.D','.E','.F','.G','.H','.I','.J','.K','.L','.M','.N'
|
||||||
|
'.N','.O','.P','.Q','.R','.S','.T','.U','.V','.W','.X','.Y','.Z','._'
|
||||||
|
)
|
||||||
|
then do;
|
||||||
|
/* valid numeric - exit data step loop */
|
||||||
|
return;
|
||||||
|
end;
|
||||||
|
|
||||||
/* special logic */
|
/* special logic */
|
||||||
if OPERATOR_NM='BETWEEN' then raw_value1=tranwrd(raw_value,' AND ','');
|
if OPERATOR_NM in ('IN','NOT IN','BETWEEN') then do;
|
||||||
else if OPERATOR_NM in ('IN','NOT IN') then do;
|
if OPERATOR_NM='BETWEEN' then raw_value1=tranwrd(raw_value,' AND ',',');
|
||||||
if substr(raw_value,1,1) ne '('
|
else do;
|
||||||
or substr(cats(reverse(raw_value)),1,1) ne ')'
|
if substr(raw_value,1,1) ne '('
|
||||||
then do;
|
or substr(cats(reverse(raw_value)),1,1) ne ')'
|
||||||
REASON_CD='Missing start/end bracket in RAW_VALUE';
|
then do;
|
||||||
putlog REASON_CD= OPERATOR_NM= raw_value= raw_value1= ;
|
REASON_CD='Missing start/end bracket in RAW_VALUE';
|
||||||
call symputx('reason_cd',reason_cd,'l');
|
putlog REASON_CD= OPERATOR_NM= raw_value= raw_value1= ;
|
||||||
call symputx('nobs',_n_,'l');
|
call symputx('reason_cd',reason_cd,'l');
|
||||||
output;
|
call symputx('nobs',_n_,'l');
|
||||||
|
output;
|
||||||
|
end;
|
||||||
|
else raw_value1=substr(raw_value,2,max(length(raw_value)-2,0));
|
||||||
|
end;
|
||||||
|
/* we now have a comma seperated list of values */
|
||||||
|
if vtype='N' then do i=1 to countc(raw_value1, ',')+1;
|
||||||
|
tmp=scan(raw_value1,i,',');
|
||||||
|
if cats(tmp) ne '.' and input(tmp, ?? 8.) eq . then do;
|
||||||
|
REASON_CD='Non Numeric value provided';
|
||||||
|
putlog REASON_CD= OPERATOR_NM= raw_value= raw_value1= ;
|
||||||
|
call symputx('reason_cd',reason_cd,'l');
|
||||||
|
call symputx('nobs',_n_,'l');
|
||||||
|
output;
|
||||||
|
end;
|
||||||
|
return;
|
||||||
end;
|
end;
|
||||||
else raw_value1=substr(raw_value,2,max(length(raw_value)-2,0));
|
|
||||||
end;
|
end;
|
||||||
else raw_value1=raw_value;
|
else raw_value1=raw_value;
|
||||||
|
|
||||||
|
|||||||
@@ -84,6 +84,9 @@ filename &outref temp;
|
|||||||
run;
|
run;
|
||||||
%end;
|
%end;
|
||||||
%else %do;
|
%else %do;
|
||||||
|
proc sort data=&inds;
|
||||||
|
by SUBGROUP_ID;
|
||||||
|
run;
|
||||||
data _null_;
|
data _null_;
|
||||||
file &outref lrecl=32800;
|
file &outref lrecl=32800;
|
||||||
set &inds end=last;
|
set &inds end=last;
|
||||||
|
|||||||
@@ -40,6 +40,22 @@
|
|||||||
%let lib=%upcase(&lib);
|
%let lib=%upcase(&lib);
|
||||||
%let ds=%upcase(&ds);
|
%let ds=%upcase(&ds);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cater for environments where sashelp.vcncolu is not available
|
||||||
|
*/
|
||||||
|
%if %sysfunc(exist(sashelp.vcncolu,view))=0 %then %do;
|
||||||
|
proc sql;
|
||||||
|
create table &outds(
|
||||||
|
libref char(8)
|
||||||
|
,TABLE_NAME char(32)
|
||||||
|
,constraint_type char(8) label='Constraint Type'
|
||||||
|
,constraint_name char(32) label='Constraint Name'
|
||||||
|
,column_name char(32) label='Column'
|
||||||
|
,constraint_order num
|
||||||
|
);
|
||||||
|
%return;
|
||||||
|
%end;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Neither dictionary tables nor sashelp provides a constraint order column,
|
* Neither dictionary tables nor sashelp provides a constraint order column,
|
||||||
* however they DO arrive in the correct order. So, create the col.
|
* however they DO arrive in the correct order. So, create the col.
|
||||||
@@ -78,8 +94,11 @@ create table &outds as
|
|||||||
/**
|
/**
|
||||||
* We cannot apply this clause to the underlying dictionary table. See:
|
* We cannot apply this clause to the underlying dictionary table. See:
|
||||||
* https://communities.sas.com/t5/SAS-Programming/Unexpected-Where-Clause-behaviour-in-dictionary-TABLE/m-p/771554#M244867
|
* https://communities.sas.com/t5/SAS-Programming/Unexpected-Where-Clause-behaviour-in-dictionary-TABLE/m-p/771554#M244867
|
||||||
|
* cannot use`where calculated libref="&lib"` either as it will STILL execute
|
||||||
|
* all the underlying constraint queries, causing exception errors in some
|
||||||
|
* cases: https://github.com/sasjs/core/issues/283
|
||||||
*/
|
*/
|
||||||
where calculated libref="&lib"
|
where a.TABLE_CATALOG="&lib"
|
||||||
%if "&ds" ne "" %then %do;
|
%if "&ds" ne "" %then %do;
|
||||||
and upcase(a.TABLE_NAME)="&ds"
|
and upcase(a.TABLE_NAME)="&ds"
|
||||||
and upcase(b.TABLE_NAME)="&ds"
|
and upcase(b.TABLE_NAME)="&ds"
|
||||||
|
|||||||
@@ -41,6 +41,7 @@
|
|||||||
<h4> SAS Macros </h4>
|
<h4> SAS Macros </h4>
|
||||||
@li mcf_length.sas
|
@li mcf_length.sas
|
||||||
@li mf_getuniquename.sas
|
@li mf_getuniquename.sas
|
||||||
|
@li mf_getvarcount.sas
|
||||||
@li mf_getvarlist.sas
|
@li mf_getvarlist.sas
|
||||||
@li mf_getvartype.sas
|
@li mf_getvartype.sas
|
||||||
@li mf_getvarformat.sas
|
@li mf_getvarformat.sas
|
||||||
@@ -60,7 +61,7 @@
|
|||||||
,outds=work.mp_getmaxvarlengths
|
,outds=work.mp_getmaxvarlengths
|
||||||
)/*/STORE SOURCE*/;
|
)/*/STORE SOURCE*/;
|
||||||
|
|
||||||
%local vars prefix x var fmt;
|
%local vars prefix x var fmt srcds;
|
||||||
%let vars=%mf_getvarlist(libds=&libds);
|
%let vars=%mf_getvarlist(libds=&libds);
|
||||||
%let prefix=%substr(%mf_getuniquename(),1,25);
|
%let prefix=%substr(%mf_getuniquename(),1,25);
|
||||||
%let num2char=%upcase(&num2char);
|
%let num2char=%upcase(&num2char);
|
||||||
@@ -70,6 +71,24 @@
|
|||||||
%mcf_length(wrap=YES, insert_cmplib=YES)
|
%mcf_length(wrap=YES, insert_cmplib=YES)
|
||||||
%end;
|
%end;
|
||||||
|
|
||||||
|
%if &num2char=NO
|
||||||
|
and ("%substr(&sysver,1,1)"="4" or "%substr(&sysver,1,1)"="5")
|
||||||
|
and %mf_getvarcount(&libds,typefilter=N) gt 0
|
||||||
|
%then %do;
|
||||||
|
/* custom functions not supported in summary operations */
|
||||||
|
%let srcds=%mf_getuniquename();
|
||||||
|
data &srcds/view=&srcds;
|
||||||
|
set &libds;
|
||||||
|
%do x=1 %to %sysfunc(countw(&vars,%str( )));
|
||||||
|
%let var=%scan(&vars,&x);
|
||||||
|
%if %mf_getvartype(&libds,&var)=N %then %do;
|
||||||
|
&prefix.&x=mcf_length(&var);
|
||||||
|
%end;
|
||||||
|
%end;
|
||||||
|
run;
|
||||||
|
%end;
|
||||||
|
%else %let srcds=&libds;
|
||||||
|
|
||||||
proc sql;
|
proc sql;
|
||||||
create table &outds (rename=(
|
create table &outds (rename=(
|
||||||
%do x=1 %to %sysfunc(countw(&vars,%str( )));
|
%do x=1 %to %sysfunc(countw(&vars,%str( )));
|
||||||
@@ -94,10 +113,15 @@ create table &outds (rename=(
|
|||||||
%end;
|
%end;
|
||||||
%end;
|
%end;
|
||||||
%else %do;
|
%else %do;
|
||||||
max(mcf_length(&var)) as &prefix.&x
|
%if "%substr(&sysver,1,1)"="4" or "%substr(&sysver,1,1)"="5" %then %do;
|
||||||
|
max(&prefix.&x) as &prefix.&x
|
||||||
|
%end;
|
||||||
|
%else %do;
|
||||||
|
max(mcf_length(&var)) as &prefix.&x
|
||||||
|
%end;
|
||||||
%end;
|
%end;
|
||||||
%end;
|
%end;
|
||||||
from &libds;
|
from &srcds;
|
||||||
|
|
||||||
proc transpose data=&outds
|
proc transpose data=&outds
|
||||||
out=&outds(rename=(_name_=NAME COL1=MAXLEN));
|
out=&outds(rename=(_name_=NAME COL1=MAXLEN));
|
||||||
|
|||||||
@@ -34,6 +34,7 @@
|
|||||||
@param [out] outds= (work.mp_getpk) The name of the output table to create.
|
@param [out] outds= (work.mp_getpk) The name of the output table to create.
|
||||||
|
|
||||||
<h4> SAS Macros </h4>
|
<h4> SAS Macros </h4>
|
||||||
|
@li mf_existfeature.sas
|
||||||
@li mf_getengine.sas
|
@li mf_getengine.sas
|
||||||
@li mf_getschema.sas
|
@li mf_getschema.sas
|
||||||
@li mp_dropmembers.sas
|
@li mp_dropmembers.sas
|
||||||
@@ -55,7 +56,8 @@
|
|||||||
)/*/STORE SOURCE*/;
|
)/*/STORE SOURCE*/;
|
||||||
|
|
||||||
|
|
||||||
%local engine schema ds1 ds2 ds3 dsn tabs1 tabs2 sum pk4sure pkdefault finalpks;
|
%local engine schema ds1 ds2 ds3 dsn tabs1 tabs2 sum pk4sure pkdefault finalpks
|
||||||
|
pkfromindex;
|
||||||
|
|
||||||
%let lib=%upcase(&lib);
|
%let lib=%upcase(&lib);
|
||||||
%let ds=%upcase(&ds);
|
%let ds=%upcase(&ds);
|
||||||
@@ -70,6 +72,7 @@
|
|||||||
%let sum=%mf_getuniquename(prefix=getpk_sum);
|
%let sum=%mf_getuniquename(prefix=getpk_sum);
|
||||||
%let pk4sure=%mf_getuniquename(prefix=getpk_pk4sure);
|
%let pk4sure=%mf_getuniquename(prefix=getpk_pk4sure);
|
||||||
%let pkdefault=%mf_getuniquename(prefix=getpk_pkdefault);
|
%let pkdefault=%mf_getuniquename(prefix=getpk_pkdefault);
|
||||||
|
%let pkfromindex=%mf_getuniquename(prefix=getpk_pkfromindex);
|
||||||
%let finalpks=%mf_getuniquename(prefix=getpk_finalpks);
|
%let finalpks=%mf_getuniquename(prefix=getpk_finalpks);
|
||||||
|
|
||||||
%local dbg;
|
%local dbg;
|
||||||
@@ -180,9 +183,23 @@ create table &ds1 as
|
|||||||
and a.constraint_name=b.constraint_name
|
and a.constraint_name=b.constraint_name
|
||||||
order by 1,2,3,4;
|
order by 1,2,3,4;
|
||||||
|
|
||||||
|
/* extract cols from the relevant unique INDEXES */
|
||||||
|
create table &pkfromindex as
|
||||||
|
select libname as libref
|
||||||
|
,memname as table_name
|
||||||
|
,indxname as constraint_name
|
||||||
|
,indxpos as constraint_order
|
||||||
|
,name
|
||||||
|
from dictionary.indexes
|
||||||
|
where nomiss='yes' and unique='yes' and upcase(libname)="&lib"
|
||||||
|
%if &ds ne 0 %then %do;
|
||||||
|
and upcase(memname)="&ds"
|
||||||
|
%end;
|
||||||
|
order by 1,2,3,4;
|
||||||
|
|
||||||
/* create one table */
|
/* create one table */
|
||||||
data &finalpks;
|
data &finalpks;
|
||||||
set &pkdefault &pk4sure ;
|
set &pkdefault &pk4sure &pkfromindex;
|
||||||
pk_ind=1;
|
pk_ind=1;
|
||||||
/* if there are multiple unique constraints, take the first */
|
/* if there are multiple unique constraints, take the first */
|
||||||
by libref table_name constraint_name;
|
by libref table_name constraint_name;
|
||||||
@@ -213,7 +230,12 @@ create table work.&tabs1 as select
|
|||||||
libname as libref
|
libname as libref
|
||||||
,upcase(memname) as dsn
|
,upcase(memname) as dsn
|
||||||
,memtype
|
,memtype
|
||||||
|
%if %mf_existfeature(DBMS_MEMTYPE)=1 %then %do;
|
||||||
,dbms_memtype
|
,dbms_memtype
|
||||||
|
%end;
|
||||||
|
%else %do;
|
||||||
|
,'n/a' as dbms_memtype format=$32.
|
||||||
|
%end;
|
||||||
,typemem
|
,typemem
|
||||||
,memlabel
|
,memlabel
|
||||||
,nvar
|
,nvar
|
||||||
@@ -256,4 +278,4 @@ create table &outds as
|
|||||||
iftrue=(&mdebug=0)
|
iftrue=(&mdebug=0)
|
||||||
)
|
)
|
||||||
|
|
||||||
%mend mp_getpk;
|
%mend mp_getpk;
|
||||||
|
|||||||
@@ -48,7 +48,7 @@
|
|||||||
outfile=0
|
outfile=0
|
||||||
)/*/STORE SOURCE*/;
|
)/*/STORE SOURCE*/;
|
||||||
|
|
||||||
%if "%substr(&sysver,1,4)"="V.04" %then %do;
|
%if "%substr(&sysver.XX,1,4)"="V.04" %then %do;
|
||||||
%put %str(ERR)OR: Viya 4 does not support the IO library in lua;
|
%put %str(ERR)OR: Viya 4 does not support the IO library in lua;
|
||||||
%return;
|
%return;
|
||||||
%end;
|
%end;
|
||||||
|
|||||||
@@ -58,7 +58,7 @@
|
|||||||
%if %mf_getattrn(&libds,NLOBS)=0 %then %do;
|
%if %mf_getattrn(&libds,NLOBS)=0 %then %do;
|
||||||
data &outds;
|
data &outds;
|
||||||
length hashkey $32;
|
length hashkey $32;
|
||||||
retain hashkey "%sysfunc(md5(%str(&salt)),$hex32.)";
|
hashkey=put(md5("&salt"),$hex32.);
|
||||||
output;
|
output;
|
||||||
stop;
|
stop;
|
||||||
run;
|
run;
|
||||||
@@ -69,9 +69,14 @@
|
|||||||
%put %str(ERR)OR: Dataset &libds is not a dataset;
|
%put %str(ERR)OR: Dataset &libds is not a dataset;
|
||||||
%end;
|
%end;
|
||||||
%else %do;
|
%else %do;
|
||||||
data &outds(rename=(&keyvar=hashkey) keep=&keyvar);
|
data &outds(rename=(&keyvar=hashkey) keep=&keyvar)
|
||||||
|
%if "%substr(&sysver,1,1)" ne "4" and "%substr(&sysver,1,1)" ne "5" %then %do;
|
||||||
|
/nonote2err
|
||||||
|
%end;
|
||||||
|
;
|
||||||
length &prevkeyvar &keyvar $32;
|
length &prevkeyvar &keyvar $32;
|
||||||
retain &prevkeyvar "%sysfunc(md5(%str(&salt)),$hex32.)";
|
retain &prevkeyvar;
|
||||||
|
if _n_=1 then &prevkeyvar=put(md5("&salt"),$hex32.);
|
||||||
set &libds end=&lastvar;
|
set &libds end=&lastvar;
|
||||||
/* hash should include previous row */
|
/* hash should include previous row */
|
||||||
&keyvar=%mp_md5(
|
&keyvar=%mp_md5(
|
||||||
|
|||||||
@@ -67,10 +67,10 @@ options
|
|||||||
validvarname=V7 /* avoid special characters etc in variable names */
|
validvarname=V7 /* avoid special characters etc in variable names */
|
||||||
varinitchk=%str(ERR)OR /* avoid data mistakes from variable name typos */
|
varinitchk=%str(ERR)OR /* avoid data mistakes from variable name typos */
|
||||||
varlenchk=%str(ERR)OR /* fail hard if truncation (data loss) can result */
|
varlenchk=%str(ERR)OR /* fail hard if truncation (data loss) can result */
|
||||||
%if %substr(&sysver,1,1) ne 4 %then %do;
|
%if "%substr(&sysver,1,1)" ne "4" and "%substr(&sysver,1,1)" ne "5" %then %do;
|
||||||
noautocorrect /* disallow misspelled procedure names */
|
noautocorrect /* disallow misspelled procedure names */
|
||||||
dsoptions=note2err /* undocumented - convert bad NOTEs to ERRs */
|
dsoptions=note2err /* undocumented - convert bad NOTEs to ERRs */
|
||||||
%end;
|
%end;
|
||||||
;
|
;
|
||||||
|
|
||||||
%mend mp_init;
|
%mend mp_init;
|
||||||
|
|||||||
@@ -1,7 +1,12 @@
|
|||||||
/**
|
/**
|
||||||
@file mp_jsonout.sas
|
@file mp_jsonout.sas
|
||||||
@brief Writes JSON in SASjs format to a fileref
|
@brief Writes JSON in SASjs format to a fileref
|
||||||
@details PROC JSON is faster but will produce errs like the ones below if
|
@details This macro can be used to OPEN a JSON stream and send one or more
|
||||||
|
tables as arrays of rows, where each row can be an object or a nested array.
|
||||||
|
|
||||||
|
There are two engines available - DATASTEP or PROCJSON.
|
||||||
|
|
||||||
|
PROC JSON is fast but will produce errs like the ones below if
|
||||||
special chars are encountered.
|
special chars are encountered.
|
||||||
|
|
||||||
> (ERR)OR: Some code points did not transcode.
|
> (ERR)OR: Some code points did not transcode.
|
||||||
@@ -12,6 +17,10 @@
|
|||||||
|
|
||||||
If this happens, try running with ENGINE=DATASTEP.
|
If this happens, try running with ENGINE=DATASTEP.
|
||||||
|
|
||||||
|
The DATASTEP engine is used to handle special SAS missing numerics, and
|
||||||
|
can also convert entire datasets to formatted values. Output JSON is always
|
||||||
|
in UTF-8.
|
||||||
|
|
||||||
Usage:
|
Usage:
|
||||||
|
|
||||||
filename tmp temp;
|
filename tmp temp;
|
||||||
@@ -19,7 +28,7 @@
|
|||||||
|
|
||||||
%mp_jsonout(OPEN,jref=tmp)
|
%mp_jsonout(OPEN,jref=tmp)
|
||||||
%mp_jsonout(OBJ,class,jref=tmp)
|
%mp_jsonout(OBJ,class,jref=tmp)
|
||||||
%mp_jsonout(OBJ,class,dslabel=class2,jref=tmp,showmeta=YES)
|
%mp_jsonout(OBJ,class,dslabel=class2,jref=tmp,showmeta=Y)
|
||||||
%mp_jsonout(CLOSE,jref=tmp)
|
%mp_jsonout(CLOSE,jref=tmp)
|
||||||
|
|
||||||
data _null_;
|
data _null_;
|
||||||
@@ -46,10 +55,12 @@
|
|||||||
@li DATASTEP (more reliable when data has non standard characters)
|
@li DATASTEP (more reliable when data has non standard characters)
|
||||||
@param [in] missing= (NULL) Special numeric missing values can be sent as NULL
|
@param [in] missing= (NULL) Special numeric missing values can be sent as NULL
|
||||||
(eg `null`) or as STRING values (eg `".a"` or `".b"`)
|
(eg `null`) or as STRING values (eg `".a"` or `".b"`)
|
||||||
@param [in] showmeta= (NO) Set to YES to output metadata alongside each table,
|
@param [in] showmeta= (N) Set to Y to output metadata alongside each table,
|
||||||
such as the column formats and types. The metadata is contained inside an
|
such as the column formats and types. The metadata is contained inside an
|
||||||
object with the same name as the table but prefixed with a dollar sign - ie,
|
object with the same name as the table but prefixed with a dollar sign - ie,
|
||||||
`,"$tablename":{"formats":{"col1":"$CHAR1"},"types":{"COL1":"C"}}`
|
`,"$tablename":{"formats":{"col1":"$CHAR1"},"types":{"COL1":"C"}}`
|
||||||
|
@param [in] maxobs= (MAX) Provide an integer to limit the number of input rows
|
||||||
|
that should be converted to JSON
|
||||||
|
|
||||||
<h4> Related Macros <h4>
|
<h4> Related Macros <h4>
|
||||||
@li mp_ds2fmtds.sas
|
@li mp_ds2fmtds.sas
|
||||||
@@ -63,21 +74,37 @@
|
|||||||
%macro mp_jsonout(action,ds,jref=_webout,dslabel=,fmt=Y
|
%macro mp_jsonout(action,ds,jref=_webout,dslabel=,fmt=Y
|
||||||
,engine=DATASTEP
|
,engine=DATASTEP
|
||||||
,missing=NULL
|
,missing=NULL
|
||||||
,showmeta=NO
|
,showmeta=N
|
||||||
|
,maxobs=MAX
|
||||||
)/*/STORE SOURCE*/;
|
)/*/STORE SOURCE*/;
|
||||||
%local tempds colinfo fmtds i numcols;
|
%local tempds colinfo fmtds i numcols stmt_obs;
|
||||||
%let numcols=0;
|
%let numcols=0;
|
||||||
|
%if &maxobs ne MAX %then %let stmt_obs=%str(if _n_>&maxobs then stop;);
|
||||||
|
|
||||||
%if &action=OPEN %then %do;
|
%if &action=OPEN %then %do;
|
||||||
options nobomfile;
|
options nobomfile;
|
||||||
data _null_;file &jref encoding='utf-8' ;
|
data _null_;file &jref encoding='utf-8' lrecl=200;
|
||||||
put '{"PROCESSED_DTTM" : "' "%sysfunc(datetime(),E8601DT26.6)" '"';
|
put '{"PROCESSED_DTTM" : "' "%sysfunc(datetime(),E8601DT26.6)" '"';
|
||||||
run;
|
run;
|
||||||
%end;
|
%end;
|
||||||
%else %if (&action=ARR or &action=OBJ) %then %do;
|
%else %if (&action=ARR or &action=OBJ) %then %do;
|
||||||
|
/* force variable names to always be uppercase in the JSON */
|
||||||
options validvarname=upcase;
|
options validvarname=upcase;
|
||||||
data _null_; file &jref encoding='utf-8' mod;
|
/* To avoid issues with _webout on EBI - such as encoding diffs and truncation
|
||||||
|
(https://support.sas.com/kb/49/325.html) we use temporary files */
|
||||||
|
filename _sjs1 temp lrecl=200 ;
|
||||||
|
data _null_; file _sjs1 encoding='utf-8';
|
||||||
put ", ""%lowcase(%sysfunc(coalescec(&dslabel,&ds)))"":";
|
put ", ""%lowcase(%sysfunc(coalescec(&dslabel,&ds)))"":";
|
||||||
|
run;
|
||||||
|
/* now write to _webout 1 char at a time */
|
||||||
|
data _null_;
|
||||||
|
infile _sjs1 lrecl=1 recfm=n;
|
||||||
|
file &jref mod lrecl=1 recfm=n;
|
||||||
|
input sourcechar $char1. @@;
|
||||||
|
format sourcechar hex2.;
|
||||||
|
put sourcechar char1. @@;
|
||||||
|
run;
|
||||||
|
filename _sjs1 clear;
|
||||||
|
|
||||||
/* grab col defs */
|
/* grab col defs */
|
||||||
proc contents noprint data=&ds
|
proc contents noprint data=&ds
|
||||||
@@ -131,13 +158,25 @@
|
|||||||
%put &sysmacroname: Switching to DATASTEP engine;
|
%put &sysmacroname: Switching to DATASTEP engine;
|
||||||
%goto datastep;
|
%goto datastep;
|
||||||
%end;
|
%end;
|
||||||
data &tempds;set &ds;
|
data &tempds;
|
||||||
|
set &ds;
|
||||||
|
&stmt_obs;
|
||||||
%if &fmt=N %then format _numeric_ best32.;;
|
%if &fmt=N %then format _numeric_ best32.;;
|
||||||
/* PRETTY is necessary to avoid line truncation in large files */
|
/* PRETTY is necessary to avoid line truncation in large files */
|
||||||
proc json out=&jref pretty
|
filename _sjs2 temp lrecl=131068 encoding='utf-8';
|
||||||
|
proc json out=_sjs2 pretty
|
||||||
%if &action=ARR %then nokeys ;
|
%if &action=ARR %then nokeys ;
|
||||||
;export &tempds / nosastags fmtnumeric;
|
;export &tempds / nosastags fmtnumeric;
|
||||||
run;
|
run;
|
||||||
|
/* send back to webout */
|
||||||
|
data _null_;
|
||||||
|
infile _sjs2 lrecl=1 recfm=n;
|
||||||
|
file &jref mod lrecl=1 recfm=n;
|
||||||
|
input sourcechar $char1. @@;
|
||||||
|
format sourcechar hex2.;
|
||||||
|
put sourcechar char1. @@;
|
||||||
|
run;
|
||||||
|
filename _sjs2 clear;
|
||||||
%end;
|
%end;
|
||||||
%else %if &engine=DATASTEP %then %do;
|
%else %if &engine=DATASTEP %then %do;
|
||||||
%datastep:
|
%datastep:
|
||||||
@@ -196,30 +235,34 @@
|
|||||||
%else %do;
|
%else %do;
|
||||||
set &ds;
|
set &ds;
|
||||||
%end;
|
%end;
|
||||||
|
&stmt_obs;
|
||||||
format _numeric_ bart.;
|
format _numeric_ bart.;
|
||||||
%do i=1 %to &numcols;
|
%do i=1 %to &numcols;
|
||||||
%if &&typelong&i=char or &fmt=Y %then %do;
|
%if &&typelong&i=char or &fmt=Y %then %do;
|
||||||
&&name&i='"'!!trim(prxchange('s/"/\"/',-1,
|
if findc(&&name&i,'"\'!!'0A0D09000E0F01021011'x) then do;
|
||||||
prxchange('s/'!!'0A'x!!'/\n/',-1,
|
&&name&i='"'!!trim(
|
||||||
prxchange('s/'!!'0D'x!!'/\r/',-1,
|
prxchange('s/"/\\"/',-1, /* double quote */
|
||||||
prxchange('s/'!!'09'x!!'/\t/',-1,
|
prxchange('s/\x0A/\n/',-1, /* new line */
|
||||||
prxchange('s/'!!'00'x!!'/\\u0000/',-1, /* NUL */
|
prxchange('s/\x0D/\r/',-1, /* carriage return */
|
||||||
prxchange('s/'!!'0E'x!!'/\\u000E/',-1, /* SS */
|
prxchange('s/\x09/\\t/',-1, /* tab */
|
||||||
prxchange('s/'!!'0F'x!!'/\\u000F/',-1, /* SF */
|
prxchange('s/\x00/\\u0000/',-1, /* NUL */
|
||||||
prxchange('s/'!!'01'x!!'/\\u0001/',-1, /* SOH */
|
prxchange('s/\x0E/\\u000E/',-1, /* SS */
|
||||||
prxchange('s/'!!'02'x!!'/\\u0002/',-1, /* STX */
|
prxchange('s/\x0F/\\u000F/',-1, /* SF */
|
||||||
prxchange('s/'!!'02'x!!'/\\u0010/',-1, /* DLE */
|
prxchange('s/\x01/\\u0001/',-1, /* SOH */
|
||||||
prxchange('s/'!!'11'x!!'/\\u0011/',-1, /* DC1 */
|
prxchange('s/\x02/\\u0002/',-1, /* STX */
|
||||||
prxchange('s/\\/\\\\/',-1,&&name&i)
|
prxchange('s/\x10/\\u0010/',-1, /* DLE */
|
||||||
))))))))))))!!'"';
|
prxchange('s/\x11/\\u0011/',-1, /* DC1 */
|
||||||
|
prxchange('s/\\/\\\\/',-1,&&name&i)
|
||||||
|
))))))))))))!!'"';
|
||||||
|
end;
|
||||||
|
else &&name&i=quote(cats(&&name&i));
|
||||||
%end;
|
%end;
|
||||||
%end;
|
%end;
|
||||||
run;
|
run;
|
||||||
|
|
||||||
/* write to temp loc to avoid _webout truncation
|
filename _sjs3 temp lrecl=131068 ;
|
||||||
- https://support.sas.com/kb/49/325.html */
|
data _null_;
|
||||||
filename _sjs temp lrecl=131068 encoding='utf-8';
|
file _sjs3 encoding='utf-8';
|
||||||
data _null_; file _sjs lrecl=131068 encoding='utf-8' mod ;
|
|
||||||
if _n_=1 then put "[";
|
if _n_=1 then put "[";
|
||||||
set &tempds;
|
set &tempds;
|
||||||
if _n_>1 then put "," @; put
|
if _n_>1 then put "," @; put
|
||||||
@@ -227,39 +270,38 @@
|
|||||||
%do i=1 %to &numcols;
|
%do i=1 %to &numcols;
|
||||||
%if &i>1 %then "," ;
|
%if &i>1 %then "," ;
|
||||||
%if &action=OBJ %then """&&name&i"":" ;
|
%if &action=OBJ %then """&&name&i"":" ;
|
||||||
&&name&i
|
"&&name&i"n /* name literal for reserved variable names */
|
||||||
%end;
|
%end;
|
||||||
%if &action=ARR %then "]" ; %else "}" ; ;
|
%if &action=ARR %then "]" ; %else "}" ; ;
|
||||||
/* now write the long strings to _webout 1 byte at a time */
|
|
||||||
|
/* close out the table */
|
||||||
data _null_;
|
data _null_;
|
||||||
length filein 8 fileid 8;
|
file _sjs3 mod encoding='utf-8';
|
||||||
filein=fopen("_sjs",'I',1,'B');
|
put ']';
|
||||||
fileid=fopen("&jref",'A',1,'B');
|
|
||||||
rec='20'x;
|
|
||||||
do while(fread(filein)=0);
|
|
||||||
rc=fget(filein,rec,1);
|
|
||||||
rc=fput(fileid, rec);
|
|
||||||
rc=fwrite(fileid);
|
|
||||||
end;
|
|
||||||
/* close out the table */
|
|
||||||
rc=fput(fileid, "]");
|
|
||||||
rc=fwrite(fileid);
|
|
||||||
rc=fclose(filein);
|
|
||||||
rc=fclose(fileid);
|
|
||||||
run;
|
run;
|
||||||
filename _sjs clear;
|
data _null_;
|
||||||
|
infile _sjs3 lrecl=1 recfm=n;
|
||||||
|
file &jref mod lrecl=1 recfm=n;
|
||||||
|
input sourcechar $char1. @@;
|
||||||
|
format sourcechar hex2.;
|
||||||
|
put sourcechar char1. @@;
|
||||||
|
run;
|
||||||
|
filename _sjs3 clear;
|
||||||
%end;
|
%end;
|
||||||
|
|
||||||
proc sql;
|
proc sql;
|
||||||
drop table &colinfo, &tempds;
|
drop table &colinfo, &tempds;
|
||||||
|
|
||||||
%if &showmeta=YES %then %do;
|
%if %substr(&showmeta,1,1)=Y %then %do;
|
||||||
data _null_; file &jref encoding='utf-8' mod;
|
filename _sjs4 temp lrecl=131068 encoding='utf-8';
|
||||||
|
data _null_;
|
||||||
|
file _sjs4;
|
||||||
|
length label $350;
|
||||||
put ", ""$%lowcase(%sysfunc(coalescec(&dslabel,&ds)))"":{""vars"":{";
|
put ", ""$%lowcase(%sysfunc(coalescec(&dslabel,&ds)))"":{""vars"":{";
|
||||||
do i=1 to &numcols;
|
do i=1 to &numcols;
|
||||||
name=quote(trim(symget(cats('name',i))));
|
name=quote(trim(symget(cats('name',i))));
|
||||||
format=quote(trim(symget(cats('fmt',i))));
|
format=quote(trim(symget(cats('fmt',i))));
|
||||||
label=quote(trim(symget(cats('label',i))));
|
label=quote(prxchange('s/\\/\\\\/',-1,trim(symget(cats('label',i)))));
|
||||||
length=quote(trim(symget(cats('length',i))));
|
length=quote(trim(symget(cats('length',i))));
|
||||||
type=quote(trim(symget(cats('typelong',i))));
|
type=quote(trim(symget(cats('typelong',i))));
|
||||||
if i>1 then put "," @@;
|
if i>1 then put "," @@;
|
||||||
@@ -268,6 +310,15 @@
|
|||||||
end;
|
end;
|
||||||
put '}}';
|
put '}}';
|
||||||
run;
|
run;
|
||||||
|
/* send back to webout */
|
||||||
|
data _null_;
|
||||||
|
infile _sjs4 lrecl=1 recfm=n;
|
||||||
|
file &jref mod lrecl=1 recfm=n;
|
||||||
|
input sourcechar $char1. @@;
|
||||||
|
format sourcechar hex2.;
|
||||||
|
put sourcechar char1. @@;
|
||||||
|
run;
|
||||||
|
filename _sjs4 clear;
|
||||||
%end;
|
%end;
|
||||||
%end;
|
%end;
|
||||||
|
|
||||||
|
|||||||
@@ -22,6 +22,7 @@
|
|||||||
@param [in] loop_secs= (1) Seconds to wait between each lock attempt
|
@param [in] loop_secs= (1) Seconds to wait between each lock attempt
|
||||||
|
|
||||||
<h4> SAS Macros </h4>
|
<h4> SAS Macros </h4>
|
||||||
|
@li mf_fmtdttm.sas
|
||||||
@li mp_abort.sas
|
@li mp_abort.sas
|
||||||
@li mp_lockfilecheck.sas
|
@li mp_lockfilecheck.sas
|
||||||
@li mf_getuser.sas
|
@li mf_getuser.sas
|
||||||
@@ -111,7 +112,7 @@ run;
|
|||||||
LOCK_LIB ="&lib";
|
LOCK_LIB ="&lib";
|
||||||
LOCK_DS="&ds";
|
LOCK_DS="&ds";
|
||||||
LOCK_STATUS_CD='LOCKED';
|
LOCK_STATUS_CD='LOCKED';
|
||||||
LOCK_START_DTTM="%sysfunc(datetime(),E8601DT26.6)"dt;
|
LOCK_START_DTTM="%sysfunc(datetime(),%mf_fmtdttm())"dt;
|
||||||
LOCK_USER_NM="&user";
|
LOCK_USER_NM="&user";
|
||||||
LOCK_PID="&sysjobid";
|
LOCK_PID="&sysjobid";
|
||||||
LOCK_REF="&ref";
|
LOCK_REF="&ref";
|
||||||
@@ -131,7 +132,7 @@ run;
|
|||||||
proc sql;
|
proc sql;
|
||||||
update &ctl_ds
|
update &ctl_ds
|
||||||
set LOCK_STATUS_CD='LOCKED'
|
set LOCK_STATUS_CD='LOCKED'
|
||||||
, LOCK_START_DTTM="%sysfunc(datetime(),E8601DT26.6)"dt
|
, LOCK_START_DTTM="%sysfunc(datetime(),%mf_fmtdttm())"dt
|
||||||
, LOCK_USER_NM="&user"
|
, LOCK_USER_NM="&user"
|
||||||
, LOCK_PID="&sysjobid"
|
, LOCK_PID="&sysjobid"
|
||||||
, LOCK_REF="&ref"
|
, LOCK_REF="&ref"
|
||||||
@@ -206,7 +207,7 @@ run;
|
|||||||
proc sql;
|
proc sql;
|
||||||
update &ctl_ds
|
update &ctl_ds
|
||||||
set LOCK_STATUS_CD='UNLOCKED'
|
set LOCK_STATUS_CD='UNLOCKED'
|
||||||
, LOCK_END_DTTM="%sysfunc(datetime(),E8601DT26.6)"dt
|
, LOCK_END_DTTM="%sysfunc(datetime(),%mf_fmtdttm())"dt
|
||||||
, LOCK_USER_NM="&user"
|
, LOCK_USER_NM="&user"
|
||||||
, LOCK_PID="&sysjobid"
|
, LOCK_PID="&sysjobid"
|
||||||
, LOCK_REF="&ref"
|
, LOCK_REF="&ref"
|
||||||
|
|||||||
@@ -35,7 +35,7 @@
|
|||||||
@param infile The QUOTED path to the file on which to perform the substitution
|
@param infile The QUOTED path to the file on which to perform the substitution
|
||||||
@param findvar= Macro variable NAME containing the string to search for
|
@param findvar= Macro variable NAME containing the string to search for
|
||||||
@param replacevar= Macro variable NAME containing the replacement string
|
@param replacevar= Macro variable NAME containing the replacement string
|
||||||
@param outfile= (0) Optional QUOTED path to an the adjusted output file (to
|
@param outfile= (0) Optional QUOTED path to the adjusted output file (to
|
||||||
avoid overwriting the first file).
|
avoid overwriting the first file).
|
||||||
|
|
||||||
<h4> SAS Macros </h4>
|
<h4> SAS Macros </h4>
|
||||||
@@ -43,7 +43,9 @@
|
|||||||
@li mf_getuniquename.sas
|
@li mf_getuniquename.sas
|
||||||
|
|
||||||
<h4> Related Macros </h4>
|
<h4> Related Macros </h4>
|
||||||
@li mp_gsubfile.test.sas
|
@li mp_chop.sas
|
||||||
|
@li mp_gsubfile.sas
|
||||||
|
@li mp_replace.test.sas
|
||||||
|
|
||||||
@version 9.4
|
@version 9.4
|
||||||
@author Bartosz Jabłoński
|
@author Bartosz Jabłoński
|
||||||
|
|||||||
@@ -21,15 +21,19 @@ https://blogs.sas.com/content/sastraining/2012/08/14/jedi-sas-tricks-reset-sas-s
|
|||||||
%macro mp_resetoption(option /* the option to reset */
|
%macro mp_resetoption(option /* the option to reset */
|
||||||
)/*/STORE SOURCE*/;
|
)/*/STORE SOURCE*/;
|
||||||
|
|
||||||
data _null_;
|
%if "%substr(&sysver,1,1)" ne "4" and "%substr(&sysver,1,1)" ne "5" %then %do;
|
||||||
length code $1500;
|
data _null_;
|
||||||
startup=getoption("&option",'startupvalue');
|
length code $1500;
|
||||||
current=getoption("&option");
|
startup=getoption("&option",'startupvalue');
|
||||||
if startup ne current then do;
|
current=getoption("&option");
|
||||||
code =cat('OPTIONS ',getoption("&option",'keyword','startupvalue'),';');
|
if startup ne current then do;
|
||||||
putlog "NOTE: Resetting system option: " code ;
|
code =cat('OPTIONS ',getoption("&option",'keyword','startupvalue'),';');
|
||||||
call execute(code );
|
putlog "NOTE: Resetting system option: " code ;
|
||||||
end;
|
call execute(code );
|
||||||
run;
|
end;
|
||||||
|
run;
|
||||||
|
%end;
|
||||||
|
%else %do;
|
||||||
|
%put &sysmacroname: reset option feature unavailable on &sysvlong;
|
||||||
|
%end;
|
||||||
%mend mp_resetoption;
|
%mend mp_resetoption;
|
||||||
@@ -58,6 +58,7 @@
|
|||||||
|
|
||||||
<h4> SAS Macros </h4>
|
<h4> SAS Macros </h4>
|
||||||
@li mf_existvar.sas
|
@li mf_existvar.sas
|
||||||
|
@li mf_fmtdttm.sas
|
||||||
@li mf_getquotedstr.sas
|
@li mf_getquotedstr.sas
|
||||||
@li mf_getuniquename.sas
|
@li mf_getuniquename.sas
|
||||||
@li mf_nobs.sas
|
@li mf_nobs.sas
|
||||||
@@ -217,12 +218,12 @@ quit;
|
|||||||
set keytable="&base_libds"
|
set keytable="&base_libds"
|
||||||
,keycolumn="&retained_key"
|
,keycolumn="&retained_key"
|
||||||
,max_key=%eval(&maxkey+&newkey_cnt)
|
,max_key=%eval(&maxkey+&newkey_cnt)
|
||||||
,processed_dttm="%sysfunc(datetime(),E8601DT26.6)"dt;
|
,processed_dttm="%sysfunc(datetime(),%mf_fmtdttm())"dt;
|
||||||
%end;
|
%end;
|
||||||
%else %do;
|
%else %do;
|
||||||
update &maxkeytable
|
update &maxkeytable
|
||||||
set max_key=%eval(&maxkey+&newkey_cnt)
|
set max_key=%eval(&maxkey+&newkey_cnt)
|
||||||
,processed_dttm="%sysfunc(datetime(),E8601DT26.6)"dt
|
,processed_dttm="%sysfunc(datetime(),%mf_fmtdttm())"dt
|
||||||
where keytable="&base_libds";
|
where keytable="&base_libds";
|
||||||
%end;
|
%end;
|
||||||
%mp_lockanytable(UNLOCK
|
%mp_lockanytable(UNLOCK
|
||||||
|
|||||||
@@ -125,7 +125,7 @@
|
|||||||
data &ds1;
|
data &ds1;
|
||||||
set &dslist indsname=&inds_auto;
|
set &dslist indsname=&inds_auto;
|
||||||
&hashkey=put(md5(catx('|',%mf_getquotedstr(&key,quote=N))),$hex32.);
|
&hashkey=put(md5(catx('|',%mf_getquotedstr(&key,quote=N))),$hex32.);
|
||||||
&inds_keep=&inds_auto;
|
&inds_keep=upcase(&inds_auto);
|
||||||
proc sort;
|
proc sort;
|
||||||
by &inds_keep &hashkey;
|
by &inds_keep &hashkey;
|
||||||
run;
|
run;
|
||||||
@@ -154,14 +154,16 @@ run;
|
|||||||
%else %let vlist=%mf_getvarlist(&libds,dlm=%str(,),quote=DOUBLE);
|
%else %let vlist=%mf_getvarlist(&libds,dlm=%str(,),quote=DOUBLE);
|
||||||
|
|
||||||
data &ds4;
|
data &ds4;
|
||||||
length &inds_keep $41 tgtvar_nm $32;
|
length &inds_keep $41 tgtvar_nm $32 _label_ $256;
|
||||||
|
if _n_=1 then call missing(_label_);
|
||||||
|
drop _label_;
|
||||||
set &ds2 &ds3 indsname=&inds_auto;
|
set &ds2 &ds3 indsname=&inds_auto;
|
||||||
|
|
||||||
tgtvar_nm=upcase(tgtvar_nm);
|
tgtvar_nm=upcase(tgtvar_nm);
|
||||||
if tgtvar_nm in (%upcase(&vlist));
|
if tgtvar_nm in (%upcase(&vlist));
|
||||||
|
|
||||||
if &inds_auto="&ds2" then tgtvar_type='N';
|
if upcase(&inds_auto)="&ds2" then tgtvar_type='N';
|
||||||
else if &inds_auto="&ds3" then tgtvar_type='C';
|
else if upcase(&inds_auto)="&ds3" then tgtvar_type='C';
|
||||||
else do;
|
else do;
|
||||||
putlog "%str(ERR)OR: unidentified vartype input!" &inds_auto;
|
putlog "%str(ERR)OR: unidentified vartype input!" &inds_auto;
|
||||||
call symputx('syscc',98);
|
call symputx('syscc',98);
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
/**
|
/**
|
||||||
@file
|
@file
|
||||||
@brief Streams a file to _webout according to content type
|
@brief Streams a file to _webout according to content type
|
||||||
@details Will set headers using appropriate functions (SAS 9 vs Viya) and send
|
@details Will set headers using appropriate functions per the server type
|
||||||
content as a binary stream.
|
(Viya, EBI, [SASjs Server](https://github.com/sasjs/server)) and stream
|
||||||
|
content using mp_binarycopy().
|
||||||
|
|
||||||
Usage:
|
Usage:
|
||||||
|
|
||||||
@@ -12,7 +13,14 @@
|
|||||||
|
|
||||||
%mp_streamfile(contenttype=csv,inloc=/some/where.txt,outname=myfile.txt)
|
%mp_streamfile(contenttype=csv,inloc=/some/where.txt,outname=myfile.txt)
|
||||||
|
|
||||||
@param [in] contenttype= (TEXT) Either TEXT, ZIP, CSV, EXCEL
|
@param [in] contenttype= (TEXT) Supported:
|
||||||
|
@li CSV
|
||||||
|
@li EXCEL
|
||||||
|
@li MARKDOWN
|
||||||
|
@li TEXT
|
||||||
|
@li ZIP
|
||||||
|
Feel free to submit PRs to support more mime types! The official list is
|
||||||
|
here: https://www.iana.org/assignments/media-types/media-types.xhtml
|
||||||
@param [in] inloc= /path/to/file.ext to be sent
|
@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] inref= fileref of file to be sent (if provided, overrides `inloc`)
|
||||||
@param [in] iftrue= (1=1) Provide a condition under which to execute.
|
@param [in] iftrue= (1=1) Provide a condition under which to execute.
|
||||||
@@ -58,7 +66,7 @@ run;
|
|||||||
%if &contentype=CSV %then %do;
|
%if &contentype=CSV %then %do;
|
||||||
%if (&platform=SASMETA and &streamweb=1) %then %do;
|
%if (&platform=SASMETA and &streamweb=1) %then %do;
|
||||||
data _null_;
|
data _null_;
|
||||||
rc=stpsrv_header('Content-type','application/csv');
|
rc=stpsrv_header('Content-Type','application/csv');
|
||||||
rc=stpsrv_header('Content-disposition',"attachment; filename=&outname");
|
rc=stpsrv_header('Content-disposition',"attachment; filename=&outname");
|
||||||
run;
|
run;
|
||||||
%end;
|
%end;
|
||||||
@@ -68,7 +76,7 @@ run;
|
|||||||
contentdisp="attachment; filename=&outname";
|
contentdisp="attachment; filename=&outname";
|
||||||
%end;
|
%end;
|
||||||
%else %if &platform=SASJS %then %do;
|
%else %if &platform=SASJS %then %do;
|
||||||
%mfs_httpheader(Content-type,application/csv)
|
%mfs_httpheader(Content-Type,application/csv)
|
||||||
%mfs_httpheader(Content-disposition,%str(attachment; filename=&outname))
|
%mfs_httpheader(Content-disposition,%str(attachment; filename=&outname))
|
||||||
%end;
|
%end;
|
||||||
%end;
|
%end;
|
||||||
@@ -76,7 +84,7 @@ run;
|
|||||||
/* suitable for XLS format */
|
/* suitable for XLS format */
|
||||||
%if (&platform=SASMETA and &streamweb=1) %then %do;
|
%if (&platform=SASMETA and &streamweb=1) %then %do;
|
||||||
data _null_;
|
data _null_;
|
||||||
rc=stpsrv_header('Content-type','application/vnd.ms-excel');
|
rc=stpsrv_header('Content-Type','application/vnd.ms-excel');
|
||||||
rc=stpsrv_header('Content-disposition',"attachment; filename=&outname");
|
rc=stpsrv_header('Content-disposition',"attachment; filename=&outname");
|
||||||
run;
|
run;
|
||||||
%end;
|
%end;
|
||||||
@@ -86,14 +94,14 @@ run;
|
|||||||
contentdisp="attachment; filename=&outname";
|
contentdisp="attachment; filename=&outname";
|
||||||
%end;
|
%end;
|
||||||
%else %if &platform=SASJS %then %do;
|
%else %if &platform=SASJS %then %do;
|
||||||
%mfs_httpheader(Content-type,application/vnd.ms-excel)
|
%mfs_httpheader(Content-Type,application/vnd.ms-excel)
|
||||||
%mfs_httpheader(Content-disposition,%str(attachment; filename=&outname))
|
%mfs_httpheader(Content-disposition,%str(attachment; filename=&outname))
|
||||||
%end;
|
%end;
|
||||||
%end;
|
%end;
|
||||||
%else %if &contentype=GIF or &contentype=JPEG or &contentype=PNG %then %do;
|
%else %if &contentype=GIF or &contentype=JPEG or &contentype=PNG %then %do;
|
||||||
%if (&platform=SASMETA and &streamweb=1) %then %do;
|
%if (&platform=SASMETA and &streamweb=1) %then %do;
|
||||||
data _null_;
|
data _null_;
|
||||||
rc=stpsrv_header('Content-type',"image/%lowcase(&contenttype)");
|
rc=stpsrv_header('Content-Type',"image/%lowcase(&contenttype)");
|
||||||
run;
|
run;
|
||||||
%end;
|
%end;
|
||||||
%else %if &platform=SASVIYA %then %do;
|
%else %if &platform=SASVIYA %then %do;
|
||||||
@@ -101,22 +109,30 @@ run;
|
|||||||
contenttype="image/%lowcase(&contenttype)";
|
contenttype="image/%lowcase(&contenttype)";
|
||||||
%end;
|
%end;
|
||||||
%else %if &platform=SASJS %then %do;
|
%else %if &platform=SASJS %then %do;
|
||||||
%mfs_httpheader(Content-type,image/%lowcase(&contenttype))
|
%mfs_httpheader(Content-Type,image/%lowcase(&contenttype))
|
||||||
%end;
|
%end;
|
||||||
%end;
|
%end;
|
||||||
%else %if &contentype=HTML %then %do;
|
%else %if &contentype=HTML or &contenttype=MARKDOWN %then %do;
|
||||||
%if &platform=SASVIYA %then %do;
|
%if (&platform=SASMETA and &streamweb=1) %then %do;
|
||||||
|
data _null_;
|
||||||
|
rc=stpsrv_header('Content-Type',"text/%lowcase(&contenttype)");
|
||||||
|
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.json"
|
filename &outref filesrvc parenturi="&SYS_JES_JOB_URI" name="_webout.json"
|
||||||
contenttype="text/html";
|
contenttype="text/%lowcase(&contenttype)"
|
||||||
|
contentdisp="attachment; filename=&outname";
|
||||||
%end;
|
%end;
|
||||||
%else %if &platform=SASJS %then %do;
|
%else %if &platform=SASJS %then %do;
|
||||||
%mfs_httpheader(Content-type,text/html)
|
%mfs_httpheader(Content-Type,text/%lowcase(&contenttype))
|
||||||
|
%mfs_httpheader(Content-disposition,%str(attachment; filename=&outname))
|
||||||
%end;
|
%end;
|
||||||
%end;
|
%end;
|
||||||
%else %if &contentype=TEXT %then %do;
|
%else %if &contentype=TEXT %then %do;
|
||||||
%if (&platform=SASMETA and &streamweb=1) %then %do;
|
%if (&platform=SASMETA and &streamweb=1) %then %do;
|
||||||
data _null_;
|
data _null_;
|
||||||
rc=stpsrv_header('Content-type','application/text');
|
rc=stpsrv_header('Content-Type','application/text');
|
||||||
rc=stpsrv_header('Content-disposition',"attachment; filename=&outname");
|
rc=stpsrv_header('Content-disposition',"attachment; filename=&outname");
|
||||||
run;
|
run;
|
||||||
%end;
|
%end;
|
||||||
@@ -126,14 +142,14 @@ run;
|
|||||||
contentdisp="attachment; filename=&outname";
|
contentdisp="attachment; filename=&outname";
|
||||||
%end;
|
%end;
|
||||||
%else %if &platform=SASJS %then %do;
|
%else %if &platform=SASJS %then %do;
|
||||||
%mfs_httpheader(Content-type,application/text)
|
%mfs_httpheader(Content-Type,application/text)
|
||||||
%mfs_httpheader(Content-disposition,%str(attachment; filename=&outname))
|
%mfs_httpheader(Content-disposition,%str(attachment; filename=&outname))
|
||||||
%end;
|
%end;
|
||||||
%end;
|
%end;
|
||||||
%else %if &contentype=WOFF or &contentype=WOFF2 or &contentype=TTF %then %do;
|
%else %if &contentype=WOFF or &contentype=WOFF2 or &contentype=TTF %then %do;
|
||||||
%if (&platform=SASMETA and &streamweb=1) %then %do;
|
%if (&platform=SASMETA and &streamweb=1) %then %do;
|
||||||
data _null_;
|
data _null_;
|
||||||
rc=stpsrv_header('Content-type',"font/%lowcase(&contenttype)");
|
rc=stpsrv_header('Content-Type',"font/%lowcase(&contenttype)");
|
||||||
run;
|
run;
|
||||||
%end;
|
%end;
|
||||||
%else %if &platform=SASVIYA %then %do;
|
%else %if &platform=SASVIYA %then %do;
|
||||||
@@ -141,13 +157,13 @@ run;
|
|||||||
contenttype="font/%lowcase(&contenttype)";
|
contenttype="font/%lowcase(&contenttype)";
|
||||||
%end;
|
%end;
|
||||||
%else %if &platform=SASJS %then %do;
|
%else %if &platform=SASJS %then %do;
|
||||||
%mfs_httpheader(Content-type,font/%lowcase(&contenttype))
|
%mfs_httpheader(Content-Type,font/%lowcase(&contenttype))
|
||||||
%end;
|
%end;
|
||||||
%end;
|
%end;
|
||||||
%else %if &contentype=XLSX %then %do;
|
%else %if &contentype=XLSX %then %do;
|
||||||
%if (&platform=SASMETA and &streamweb=1) %then %do;
|
%if (&platform=SASMETA and &streamweb=1) %then %do;
|
||||||
data _null_;
|
data _null_;
|
||||||
rc=stpsrv_header('Content-type',
|
rc=stpsrv_header('Content-Type',
|
||||||
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
|
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
|
||||||
rc=stpsrv_header('Content-disposition',"attachment; filename=&outname");
|
rc=stpsrv_header('Content-disposition',"attachment; filename=&outname");
|
||||||
run;
|
run;
|
||||||
@@ -159,7 +175,7 @@ run;
|
|||||||
contentdisp="attachment; filename=&outname";
|
contentdisp="attachment; filename=&outname";
|
||||||
%end;
|
%end;
|
||||||
%else %if &platform=SASJS %then %do;
|
%else %if &platform=SASJS %then %do;
|
||||||
%mfs_httpheader(Content-type
|
%mfs_httpheader(Content-Type
|
||||||
,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
|
,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
|
||||||
)
|
)
|
||||||
%mfs_httpheader(Content-disposition,%str(attachment; filename=&outname))
|
%mfs_httpheader(Content-disposition,%str(attachment; filename=&outname))
|
||||||
@@ -168,7 +184,7 @@ run;
|
|||||||
%else %if &contentype=ZIP %then %do;
|
%else %if &contentype=ZIP %then %do;
|
||||||
%if (&platform=SASMETA and &streamweb=1) %then %do;
|
%if (&platform=SASMETA and &streamweb=1) %then %do;
|
||||||
data _null_;
|
data _null_;
|
||||||
rc=stpsrv_header('Content-type','application/zip');
|
rc=stpsrv_header('Content-Type','application/zip');
|
||||||
rc=stpsrv_header('Content-disposition',"attachment; filename=&outname");
|
rc=stpsrv_header('Content-disposition',"attachment; filename=&outname");
|
||||||
run;
|
run;
|
||||||
%end;
|
%end;
|
||||||
@@ -178,7 +194,7 @@ run;
|
|||||||
contentdisp="attachment; filename=&outname";
|
contentdisp="attachment; filename=&outname";
|
||||||
%end;
|
%end;
|
||||||
%else %if &platform=SASJS %then %do;
|
%else %if &platform=SASJS %then %do;
|
||||||
%mfs_httpheader(Content-type,application/zip)
|
%mfs_httpheader(Content-Type,application/zip)
|
||||||
%mfs_httpheader(Content-disposition,%str(attachment; filename=&outname))
|
%mfs_httpheader(Content-disposition,%str(attachment; filename=&outname))
|
||||||
%end;
|
%end;
|
||||||
%end;
|
%end;
|
||||||
|
|||||||
@@ -1,51 +1,11 @@
|
|||||||
/**
|
/**
|
||||||
@file
|
@file
|
||||||
@brief Will execute a SASjs web service on SAS 9 or Viya
|
@brief To be deprecated. Will execute a SASjs web service on SAS 9 or Viya
|
||||||
@details Prepares the input files and retrieves the resulting datasets from
|
@details Use the mx_testservice.sas macro instead (documentation can be
|
||||||
the response JSON.
|
found there)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Note - the _webout fileref should NOT be assigned prior to running this macro.
|
|
||||||
|
|
||||||
@param [in] program The _PROGRAM endpoint to test
|
|
||||||
@param [in] inputfiles=(0) A list of space seperated fileref:filename pairs as
|
|
||||||
follows:
|
|
||||||
inputfiles=inref:filename inref2:filename2
|
|
||||||
@param [in] inputdatasets= (0) All datasets in this space seperated list are
|
|
||||||
converted into SASJS-formatted CSVs (see mp_ds2csv.sas) files and added to
|
|
||||||
the list of `inputfiles` for ingestion. The dataset will be sent with the
|
|
||||||
same name (no need for a colon modifier).
|
|
||||||
@param [in] inputparams=(0) A dataset containing name/value pairs in the
|
|
||||||
following format:
|
|
||||||
|name:$32|value:$1000|
|
|
||||||
|---|---|
|
|
||||||
|stpmacname|some value|
|
|
||||||
|mustbevalidname|can be anything, oops, %abort!!|
|
|
||||||
|
|
||||||
@param [in] debug= (log) Provide the _debug value
|
|
||||||
@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
|
|
||||||
response.
|
|
||||||
|
|
||||||
<h4> SAS Macros </h4>
|
<h4> SAS Macros </h4>
|
||||||
@li mf_getplatform.sas
|
@li mx_testservice.sas
|
||||||
@li mf_getuniquefileref.sas
|
|
||||||
@li mf_getuniquename.sas
|
|
||||||
@li mp_abort.sas
|
|
||||||
@li mp_binarycopy.sas
|
|
||||||
@li mp_ds2csv.sas
|
|
||||||
@li mv_getjobresult.sas
|
|
||||||
@li mv_jobflow.sas
|
|
||||||
|
|
||||||
<h4> Related Programs </h4>
|
|
||||||
@li mp_testservice.test.sas
|
|
||||||
|
|
||||||
@version 9.4
|
@version 9.4
|
||||||
@author Allan Bowe
|
@author Allan Bowe
|
||||||
@@ -63,216 +23,17 @@
|
|||||||
viyaresult=WEBOUT_JSON,
|
viyaresult=WEBOUT_JSON,
|
||||||
viyacontext=SAS Job Execution compute context
|
viyacontext=SAS Job Execution compute context
|
||||||
)/*/STORE SOURCE*/;
|
)/*/STORE SOURCE*/;
|
||||||
%local dbg pcnt fref1 webref i webcount var platform;
|
|
||||||
%if &mdebug=1 %then %do;
|
|
||||||
%put &sysmacroname entry vars:;
|
|
||||||
%put _local_;
|
|
||||||
%end;
|
|
||||||
%else %let dbg=*;
|
|
||||||
|
|
||||||
/* sanitise inputparams */
|
%mx_testservice(&program,
|
||||||
%let pcnt=0;
|
inputfiles=&inputfiles,
|
||||||
%if &inputparams ne 0 %then %do;
|
inputdatasets=&inputdatasets,
|
||||||
data _null_;
|
inputparams=&inputparams,
|
||||||
set &inputparams;
|
debug=&debug,
|
||||||
if not nvalid(name,'v7') then putlog (_all_)(=);
|
mdebug=&mdebug,
|
||||||
else if name in (
|
outlib=&outlib,
|
||||||
'program','inputfiles','inputparams','debug','outlib','outref'
|
outref=&outref,
|
||||||
) then putlog (_all_)(=);
|
viyaresult=&viyaresult,
|
||||||
else do;
|
viyacontext=&viyacontext
|
||||||
x+1;
|
)
|
||||||
call symputx(name,quote(cats(value)),'l');
|
|
||||||
call symputx(cats('pval',x),name,'l');
|
|
||||||
call symputx('pcnt',x,'l');
|
|
||||||
end;
|
|
||||||
run;
|
|
||||||
%mp_abort(iftrue= (%mf_nobs(&inputparams) ne &pcnt)
|
|
||||||
,mac=&sysmacroname
|
|
||||||
,msg=%str(Invalid values in &inputparams)
|
|
||||||
)
|
|
||||||
%end;
|
|
||||||
|
|
||||||
/* convert inputdatasets to filerefs */
|
%mend mp_testservice;
|
||||||
%if "&inputdatasets" ne "0" %then %do;
|
|
||||||
%if %quote(&inputfiles)=0 %then %let inputfiles=;
|
|
||||||
%do i=1 %to %sysfunc(countw(&inputdatasets,%str( )));
|
|
||||||
%let var=%scan(&inputdatasets,&i,%str( ));
|
|
||||||
%local dsref&i;
|
|
||||||
%let dsref&i=%mf_getuniquefileref();
|
|
||||||
%mp_ds2csv(&var,outref=&&dsref&i,headerformat=SASJS)
|
|
||||||
%let inputfiles=&inputfiles &&dsref&i:%scan(&var,-1,.);
|
|
||||||
%end;
|
|
||||||
%end;
|
|
||||||
|
|
||||||
|
|
||||||
%let fref1=%mf_getuniquefileref();
|
|
||||||
%let webref=%mf_getuniquefileref();
|
|
||||||
%let platform=%mf_getplatform();
|
|
||||||
%if &platform=SASMETA %then %do;
|
|
||||||
|
|
||||||
/* parse the input files */
|
|
||||||
%if %quote(&inputfiles) ne 0 %then %do;
|
|
||||||
%let webcount=%sysfunc(countw(&inputfiles));
|
|
||||||
%put &=webcount;
|
|
||||||
%do i=1 %to &webcount;
|
|
||||||
%let var=%scan(&inputfiles,&i,%str( ));
|
|
||||||
%local webfref&i webname&i;
|
|
||||||
%let webref&i=%scan(&var,1,%str(:));
|
|
||||||
%let webname&i=%scan(&var,2,%str(:));
|
|
||||||
%put webref&i=&&webref&i;
|
|
||||||
%put webname&i=&&webname&i;
|
|
||||||
%end;
|
|
||||||
%end;
|
|
||||||
%else %let webcount=0;
|
|
||||||
|
|
||||||
proc stp program="&program";
|
|
||||||
inputparam _program="&program"
|
|
||||||
%do i=1 %to &webcount;
|
|
||||||
%if &webcount=1 %then %do;
|
|
||||||
_webin_fileref="&&webref&i"
|
|
||||||
_webin_name="&&webname&i"
|
|
||||||
%end;
|
|
||||||
%else %do;
|
|
||||||
_webin_fileref&i="&&webref&i"
|
|
||||||
_webin_name&i="&&webname&i"
|
|
||||||
%end;
|
|
||||||
%end;
|
|
||||||
_webin_file_count="&webcount"
|
|
||||||
_debug="&debug"
|
|
||||||
%do i=1 %to &pcnt;
|
|
||||||
/* resolve name only, proc stp fetches value */
|
|
||||||
&&pval&i=&&&&&&pval&i
|
|
||||||
%end;
|
|
||||||
;
|
|
||||||
%do i=1 %to &webcount;
|
|
||||||
inputfile &&webref&i;
|
|
||||||
%end;
|
|
||||||
outputfile _webout=&webref;
|
|
||||||
run;
|
|
||||||
|
|
||||||
data _null_;
|
|
||||||
infile &webref;
|
|
||||||
file &fref1;
|
|
||||||
input;
|
|
||||||
length line $10000;
|
|
||||||
if index(_infile_,'>>weboutBEGIN<<') then do;
|
|
||||||
line=tranwrd(_infile_,'>>weboutBEGIN<<','');
|
|
||||||
put line;
|
|
||||||
end;
|
|
||||||
else if index(_infile_,'>>weboutEND<<') then do;
|
|
||||||
line=tranwrd(_infile_,'>>weboutEND<<','');
|
|
||||||
put line;
|
|
||||||
stop;
|
|
||||||
end;
|
|
||||||
else put _infile_;
|
|
||||||
run;
|
|
||||||
data _null_;
|
|
||||||
infile &fref1;
|
|
||||||
input;
|
|
||||||
put _infile_;
|
|
||||||
run;
|
|
||||||
%if &outlib ne 0 %then %do;
|
|
||||||
libname &outlib json (&fref1);
|
|
||||||
%end;
|
|
||||||
%if &outref ne 0 %then %do;
|
|
||||||
filename &outref temp;
|
|
||||||
%mp_binarycopy(inref=&webref,outref=&outref)
|
|
||||||
%end;
|
|
||||||
|
|
||||||
%end;
|
|
||||||
%else %if &platform=SASVIYA %then %do;
|
|
||||||
|
|
||||||
/* prepare inputparams */
|
|
||||||
%local ds1;
|
|
||||||
%let ds1=%mf_getuniquename();
|
|
||||||
%if "&inputparams" ne "0" %then %do;
|
|
||||||
proc transpose data=&inputparams out=&ds1;
|
|
||||||
id name;
|
|
||||||
var value;
|
|
||||||
run;
|
|
||||||
%end;
|
|
||||||
%else %do;
|
|
||||||
data &ds1;run;
|
|
||||||
%end;
|
|
||||||
|
|
||||||
/* parse the input files - convert to sasjs params */
|
|
||||||
%local webcount i var sasjs_tables;
|
|
||||||
%if %quote(&inputfiles) ne 0 %then %do;
|
|
||||||
%let webcount=%sysfunc(countw(&inputfiles));
|
|
||||||
%put &=webcount;
|
|
||||||
%do i=1 %to &webcount;
|
|
||||||
%let var=%scan(&inputfiles,&i,%str( ));
|
|
||||||
%local webfref&i webname&i sasjs&i.data;
|
|
||||||
%let webref&i=%scan(&var,1,%str(:));
|
|
||||||
%let webname&i=%scan(&var,2,%str(:));
|
|
||||||
%put webref&i=&&webref&i;
|
|
||||||
%put webname&i=&&webname&i;
|
|
||||||
|
|
||||||
%let sasjs_tables=&sasjs_tables &&webname&i;
|
|
||||||
data _null_;
|
|
||||||
infile &&webref&i lrecl=32767;
|
|
||||||
input;
|
|
||||||
if _n_=1 then call symputx("sasjs&i.data",_infile_);
|
|
||||||
else call symputx(
|
|
||||||
"sasjs&i.data",cats(symget("sasjs&i.data"),'0D0A'x,_infile_)
|
|
||||||
);
|
|
||||||
putlog "&sysmacroname infile: " _infile_;
|
|
||||||
run;
|
|
||||||
data &ds1;
|
|
||||||
set &ds1;
|
|
||||||
length sasjs&i.data $32767 sasjs_tables $1000;
|
|
||||||
sasjs&i.data=symget("sasjs&i.data");
|
|
||||||
sasjs_tables=symget("sasjs_tables");
|
|
||||||
run;
|
|
||||||
%end;
|
|
||||||
%end;
|
|
||||||
%else %let webcount=0;
|
|
||||||
|
|
||||||
data &ds1;
|
|
||||||
retain _program "&program";
|
|
||||||
retain _contextname "&viyacontext";
|
|
||||||
set &ds1;
|
|
||||||
putlog "&sysmacroname inputparams:";
|
|
||||||
putlog (_all_)(=);
|
|
||||||
run;
|
|
||||||
|
|
||||||
%mv_jobflow(inds=&ds1
|
|
||||||
,maxconcurrency=1
|
|
||||||
,outds=work.results
|
|
||||||
,outref=&fref1
|
|
||||||
,mdebug=&mdebug
|
|
||||||
)
|
|
||||||
/* show the log */
|
|
||||||
data _null_;
|
|
||||||
infile &fref1;
|
|
||||||
input;
|
|
||||||
putlog _infile_;
|
|
||||||
run;
|
|
||||||
/* get the uri to fetch results */
|
|
||||||
data _null_;
|
|
||||||
set work.results;
|
|
||||||
call symputx('uri',uri);
|
|
||||||
putlog "&sysmacroname: fetching results for " uri;
|
|
||||||
run;
|
|
||||||
/* fetch results from webout.json */
|
|
||||||
%mv_getjobresult(uri=&uri,
|
|
||||||
result=&viyaresult,
|
|
||||||
outref=&outref,
|
|
||||||
outlib=&outlib,
|
|
||||||
mdebug=&mdebug
|
|
||||||
)
|
|
||||||
|
|
||||||
%end;
|
|
||||||
%else %do;
|
|
||||||
%put %str(ERR)OR: Unrecognised platform: &platform;
|
|
||||||
%end;
|
|
||||||
|
|
||||||
%if &mdebug=0 %then %do;
|
|
||||||
filename &webref clear;
|
|
||||||
%end;
|
|
||||||
%else %do;
|
|
||||||
%put &sysmacroname exit vars:;
|
|
||||||
%put _local_;
|
|
||||||
%end;
|
|
||||||
|
|
||||||
%mend mp_testservice;
|
|
||||||
|
|||||||
77
build.py
77
build.py
@@ -4,8 +4,8 @@ from pathlib import Path
|
|||||||
# Prepare Lua Macros
|
# Prepare Lua Macros
|
||||||
files = [f for f in Path('lua').iterdir() if f.match("*.lua")]
|
files = [f for f in Path('lua').iterdir() if f.match("*.lua")]
|
||||||
for file in files:
|
for file in files:
|
||||||
basename=os.path.basename(file)
|
basename = os.path.basename(file)
|
||||||
name='ml_' + os.path.splitext(basename)[0]
|
name = 'ml_' + os.path.splitext(basename)[0]
|
||||||
ml = open('lua/' + name + '.sas', "w")
|
ml = open('lua/' + name + '.sas', "w")
|
||||||
ml.write("/**\n")
|
ml.write("/**\n")
|
||||||
ml.write(" @file " + name + '.sas\n')
|
ml.write(" @file " + name + '.sas\n')
|
||||||
@@ -20,50 +20,68 @@ for file in files:
|
|||||||
ml.write(" file \"%sysfunc(pathname(work))/" + name + ".lua\";\n")
|
ml.write(" file \"%sysfunc(pathname(work))/" + name + ".lua\";\n")
|
||||||
with open(file) as infile:
|
with open(file) as infile:
|
||||||
for line in infile:
|
for line in infile:
|
||||||
ml.write(" put '" + line.rstrip().replace("'","''") + " ';\n")
|
ml.write(" put '" + line.rstrip().replace("'", "''") + " ';\n")
|
||||||
ml.write("run;\n\n")
|
ml.write("run;\n\n")
|
||||||
ml.write("%inc \"%sysfunc(pathname(work))/" + name + ".lua\" /source2;\n\n")
|
|
||||||
|
ml.write("/* ensure big enough lrecl to avoid lua compilation issues */\n")
|
||||||
|
ml.write("%local optval;\n")
|
||||||
|
ml.write("%let optval=%sysfunc(getoption(lrecl));\n")
|
||||||
|
ml.write("options lrecl=1024;\n\n")
|
||||||
|
ml.write("/* execute the lua code by using a .lua extension */\n")
|
||||||
|
ml.write("%inc \"%sysfunc(pathname(work))/" +
|
||||||
|
name + ".lua\" /source2;\n\n")
|
||||||
|
ml.write("options lrecl=&optval;\n\n")
|
||||||
ml.write("%mend " + name + ";\n")
|
ml.write("%mend " + name + ";\n")
|
||||||
|
|
||||||
ml.close()
|
ml.close()
|
||||||
|
|
||||||
# prepare web files
|
# prepare web files
|
||||||
files=['viya/mv_createwebservice.sas','meta/mm_createwebservice.sas']
|
files = ['viya/mv_createwebservice.sas',
|
||||||
|
'meta/mm_createwebservice.sas', 'server/ms_createwebservice.sas']
|
||||||
for file in files:
|
for file in files:
|
||||||
webout0=open('base/mp_jsonout.sas','r')
|
webout0 = open('base/mp_jsonout.sas', 'r')
|
||||||
if file=='viya/mv_createwebservice.sas':
|
webout1 = open('base/mf_getuser.sas', 'r')
|
||||||
webout1=open('viya/mv_webout.sas',"r")
|
|
||||||
|
if file == 'viya/mv_createwebservice.sas':
|
||||||
|
webout2 = open('viya/mv_webout.sas', "r")
|
||||||
|
weboutfiles = [webout0, webout1, webout2]
|
||||||
|
elif file == 'server/ms_createwebservice.sas':
|
||||||
|
webout2 = open('server/ms_webout.sas', "r")
|
||||||
|
webout3 = open('server/mfs_httpheader.sas', 'r')
|
||||||
|
weboutfiles = [webout0, webout1, webout2, webout3]
|
||||||
else:
|
else:
|
||||||
webout1=open('meta/mm_webout.sas','r')
|
webout2 = open('meta/mm_webout.sas', 'r')
|
||||||
webout2=open('base/mf_getuser.sas','r')
|
weboutfiles = [webout0, webout1, webout2]
|
||||||
outfile=open(file + 'TEMP','w')
|
outfile = open(file + 'TEMP', 'w')
|
||||||
infile=open(file,'r')
|
infile = open(file, 'r')
|
||||||
delrow=0
|
delrow = 0
|
||||||
for line in infile:
|
for line in infile:
|
||||||
if line=='/* WEBOUT BEGIN */\n':
|
if line == '/* WEBOUT BEGIN */\n':
|
||||||
delrow=1
|
delrow = 1
|
||||||
outfile.write('/* WEBOUT BEGIN */\n')
|
outfile.write('/* WEBOUT BEGIN */\n')
|
||||||
weboutfiles=[webout0,webout1,webout2]
|
|
||||||
for weboutfile in weboutfiles:
|
for weboutfile in weboutfiles:
|
||||||
stripcomment=1
|
stripcomment = 1
|
||||||
for w in weboutfile:
|
for w in weboutfile:
|
||||||
if w=='**/\n': stripcomment=0
|
if w == '**/\n':
|
||||||
elif stripcomment==0:
|
stripcomment = 0
|
||||||
outfile.write(" put '" + w.rstrip().replace("'","''") + " ';\n")
|
elif stripcomment == 0:
|
||||||
elif delrow==1 and line=='/* WEBOUT END */\n':
|
outfile.write(
|
||||||
delrow=0
|
" put '" + w.rstrip().replace("'", "''") + " ';\n")
|
||||||
outfile.write('/* WEBOUT END */\n')
|
elif delrow == 1 and line == '/* WEBOUT END */\n':
|
||||||
elif delrow==0:
|
delrow = 0
|
||||||
|
outfile.write('/* WEBOUT END */\n')
|
||||||
|
elif delrow == 0:
|
||||||
outfile.write(line.rstrip() + "\n")
|
outfile.write(line.rstrip() + "\n")
|
||||||
webout0.close()
|
webout0.close()
|
||||||
webout1.close()
|
webout1.close()
|
||||||
|
webout2.close()
|
||||||
outfile.close()
|
outfile.close()
|
||||||
infile.close()
|
infile.close()
|
||||||
os.remove(file)
|
os.remove(file)
|
||||||
os.rename(file + 'TEMP',file)
|
os.rename(file + 'TEMP', file)
|
||||||
|
|
||||||
# Concatenate all macros into a single file
|
# Concatenate all macros into a single file
|
||||||
header="""
|
header = """
|
||||||
/**
|
/**
|
||||||
@file
|
@file
|
||||||
@brief Auto-generated file
|
@brief Auto-generated file
|
||||||
@@ -84,14 +102,15 @@ options noquotelenmax;
|
|||||||
"""
|
"""
|
||||||
f = open('all.sas', "w") # r / r+ / rb / rb+ / w / wb
|
f = open('all.sas', "w") # r / r+ / rb / rb+ / w / wb
|
||||||
f.write(header)
|
f.write(header)
|
||||||
folders=['base','ddl','meta','metax','server','viya','lua','fcmp']
|
folders = ['base', 'ddl', 'meta', 'metax', 'server', 'viya', 'lua', 'fcmp', 'xplatform']
|
||||||
for folder in folders:
|
for folder in folders:
|
||||||
filenames = [fn for fn in Path('./' + folder).iterdir() if fn.match("*.sas")]
|
filenames = [fn for fn in Path(
|
||||||
|
'./' + folder).iterdir() if fn.match("*.sas")]
|
||||||
filenames.sort()
|
filenames.sort()
|
||||||
with open('mc_' + folder + '.sas', 'w') as outfile:
|
with open('mc_' + folder + '.sas', 'w') as outfile:
|
||||||
for fname in filenames:
|
for fname in filenames:
|
||||||
with open(fname) as infile:
|
with open(fname) as infile:
|
||||||
outfile.write(infile.read())
|
outfile.write(infile.read())
|
||||||
with open('mc_' + folder + '.sas','r') as c:
|
with open('mc_' + folder + '.sas', 'r') as c:
|
||||||
f.write(c.read())
|
f.write(c.read())
|
||||||
f.close()
|
f.close()
|
||||||
|
|||||||
@@ -26,9 +26,19 @@
|
|||||||
oldval_num num format=best32. label='Old (numeric) value',
|
oldval_num num format=best32. label='Old (numeric) value',
|
||||||
newval_num num format=best32. label='New (numeric) value',
|
newval_num num format=best32. label='New (numeric) value',
|
||||||
oldval_char char(32765) label='Old (character) value',
|
oldval_char char(32765) label='Old (character) value',
|
||||||
newval_char char(32765) label='New (character) value',
|
newval_char char(32765) label='New (character) value'
|
||||||
constraint pk_mpe_audit
|
|
||||||
primary key(load_ref,libref,dsn,key_hash,tgtvar_nm)
|
|
||||||
);
|
);
|
||||||
|
|
||||||
|
%local lib;
|
||||||
|
%let libds=%upcase(&libds);
|
||||||
|
%if %index(&libds,.)=0 %then %let lib=WORK;
|
||||||
|
%else %let lib=%scan(&libds,1,.);
|
||||||
|
|
||||||
|
proc datasets lib=&lib noprint;
|
||||||
|
modify %scan(&libds,-1,.);
|
||||||
|
index create
|
||||||
|
pk_mpe_audit=(load_ref libref dsn key_hash tgtvar_nm)
|
||||||
|
/nomiss unique;
|
||||||
|
quit;
|
||||||
|
|
||||||
%mend mddl_dc_difftable;
|
%mend mddl_dc_difftable;
|
||||||
@@ -9,19 +9,32 @@
|
|||||||
|
|
||||||
%macro mddl_dc_filterdetail(libds=WORK.FILTER_DETAIL);
|
%macro mddl_dc_filterdetail(libds=WORK.FILTER_DETAIL);
|
||||||
|
|
||||||
|
%local nn lib;
|
||||||
|
%if "%substr(&sysver,1,1)" ne "4" and "%substr(&sysver,1,1)" ne "5" %then %do;
|
||||||
|
%let nn=not null;
|
||||||
|
%end;
|
||||||
|
%else %let nn=;
|
||||||
|
|
||||||
proc sql;
|
proc sql;
|
||||||
create table &libds(
|
create table &libds(
|
||||||
filter_hash char(32) not null,
|
filter_hash char(32) &nn,
|
||||||
filter_line num not null,
|
filter_line num &nn,
|
||||||
group_logic char(3) not null,
|
group_logic char(3) &nn,
|
||||||
subgroup_logic char(3) not null,
|
subgroup_logic char(3) &nn,
|
||||||
subgroup_id num not null,
|
subgroup_id num &nn,
|
||||||
variable_nm varchar(32) not null,
|
variable_nm varchar(32) &nn,
|
||||||
operator_nm varchar(12) not null,
|
operator_nm varchar(12) &nn,
|
||||||
raw_value varchar(4000) not null,
|
raw_value varchar(4000) &nn,
|
||||||
processed_dttm num not null format=E8601DT26.6,
|
processed_dttm num &nn format=E8601DT26.6
|
||||||
constraint pk_mpe_filteranytable
|
|
||||||
primary key(filter_hash,filter_line)
|
|
||||||
);
|
);
|
||||||
|
|
||||||
|
%let libds=%upcase(&libds);
|
||||||
|
%if %index(&libds,.)=0 %then %let lib=WORK;
|
||||||
|
%else %let lib=%scan(&libds,1,.);
|
||||||
|
|
||||||
|
proc datasets lib=&lib noprint;
|
||||||
|
modify %scan(&libds,-1,.);
|
||||||
|
index create pk_mpe_filterdetail=(filter_hash filter_line)/nomiss unique;
|
||||||
|
quit;
|
||||||
|
|
||||||
%mend mddl_dc_filterdetail;
|
%mend mddl_dc_filterdetail;
|
||||||
@@ -9,14 +9,27 @@
|
|||||||
|
|
||||||
%macro mddl_dc_filtersummary(libds=WORK.FILTER_SUMMARY);
|
%macro mddl_dc_filtersummary(libds=WORK.FILTER_SUMMARY);
|
||||||
|
|
||||||
|
%local nn lib;
|
||||||
|
%if "%substr(&sysver,1,1)" ne "4" and "%substr(&sysver,1,1)" ne "5" %then %do;
|
||||||
|
%let nn=not null;
|
||||||
|
%end;
|
||||||
|
%else %let nn=;
|
||||||
|
|
||||||
proc sql;
|
proc sql;
|
||||||
create table &libds(
|
create table &libds(
|
||||||
filter_rk num not null,
|
filter_rk num &nn,
|
||||||
filter_hash char(32) not null,
|
filter_hash char(32) &nn,
|
||||||
filter_table char(41) not null,
|
filter_table char(41) &nn,
|
||||||
processed_dttm num not null format=E8601DT26.6,
|
processed_dttm num &nn format=E8601DT26.6
|
||||||
constraint pk_mpe_filteranytable
|
|
||||||
primary key(filter_rk)
|
|
||||||
);
|
);
|
||||||
|
|
||||||
|
%let libds=%upcase(&libds);
|
||||||
|
%if %index(&libds,.)=0 %then %let lib=WORK;
|
||||||
|
%else %let lib=%scan(&libds,1,.);
|
||||||
|
|
||||||
|
proc datasets lib=&lib noprint;
|
||||||
|
modify %scan(&libds,-1,.);
|
||||||
|
index create filter_rk /nomiss unique;
|
||||||
|
quit;
|
||||||
|
|
||||||
%mend mddl_dc_filtersummary;
|
%mend mddl_dc_filtersummary;
|
||||||
@@ -9,17 +9,33 @@
|
|||||||
|
|
||||||
%macro mddl_dc_locktable(libds=WORK.LOCKTABLE);
|
%macro mddl_dc_locktable(libds=WORK.LOCKTABLE);
|
||||||
|
|
||||||
|
%local nn lib;
|
||||||
|
%if "%substr(&sysver,1,1)" ne "4" and "%substr(&sysver,1,1)" ne "5" %then %do;
|
||||||
|
%let nn=not null;
|
||||||
|
%end;
|
||||||
|
%else %let nn=;
|
||||||
|
|
||||||
proc sql;
|
proc sql;
|
||||||
create table &libds(
|
create table &libds(
|
||||||
lock_lib char(8),
|
lock_lib char(8),
|
||||||
lock_ds char(32),
|
lock_ds char(32),
|
||||||
lock_status_cd char(10) not null,
|
lock_status_cd char(10) &nn,
|
||||||
lock_user_nm char(100) not null ,
|
lock_user_nm char(100) &nn ,
|
||||||
lock_ref char(200),
|
lock_ref char(200),
|
||||||
lock_pid char(10),
|
lock_pid char(10),
|
||||||
lock_start_dttm num format=E8601DT26.6,
|
lock_start_dttm num format=E8601DT26.6,
|
||||||
lock_end_dttm num format=E8601DT26.6,
|
lock_end_dttm num format=E8601DT26.6
|
||||||
constraint pk_mp_lockanytable primary key(lock_lib,lock_ds)
|
|
||||||
);
|
);
|
||||||
|
|
||||||
|
%let libds=%upcase(&libds);
|
||||||
|
%if %index(&libds,.)=0 %then %let lib=WORK;
|
||||||
|
%else %let lib=%scan(&libds,1,.);
|
||||||
|
|
||||||
|
proc datasets lib=&lib noprint;
|
||||||
|
modify %scan(&libds,-1,.);
|
||||||
|
index create
|
||||||
|
pk_mp_lockanytable=(lock_lib lock_ds)
|
||||||
|
/nomiss unique;
|
||||||
|
quit;
|
||||||
|
|
||||||
%mend mddl_dc_locktable;
|
%mend mddl_dc_locktable;
|
||||||
@@ -17,8 +17,17 @@
|
|||||||
max_key num label=
|
max_key num label=
|
||||||
'Integer representing current max RK or SK value in the KEYTABLE',
|
'Integer representing current max RK or SK value in the KEYTABLE',
|
||||||
processed_dttm num format=E8601DT26.6
|
processed_dttm num format=E8601DT26.6
|
||||||
label='Datetime this value was last updated',
|
label='Datetime this value was last updated'
|
||||||
constraint pk_mpe_maxkeyvalues
|
);
|
||||||
primary key(keytable));
|
|
||||||
|
%local lib;
|
||||||
|
%let libds=%upcase(&libds);
|
||||||
|
%if %index(&libds,.)=0 %then %let lib=WORK;
|
||||||
|
%else %let lib=%scan(&libds,1,.);
|
||||||
|
|
||||||
|
proc datasets lib=&lib noprint;
|
||||||
|
modify %scan(&libds,-1,.);
|
||||||
|
index create keytable /nomiss unique;
|
||||||
|
quit;
|
||||||
|
|
||||||
%mend mddl_dc_maxkeytable;
|
%mend mddl_dc_maxkeytable;
|
||||||
@@ -14,8 +14,8 @@ proc sql;
|
|||||||
create table &libds(
|
create table &libds(
|
||||||
FMTNAME char(32) label='Format name'
|
FMTNAME char(32) label='Format name'
|
||||||
/*
|
/*
|
||||||
to accomodate larger START values, mp_loadformat.sas will need the
|
to accommodate larger START values, mp_loadformat.sas will need the
|
||||||
SQL dependency removed (proc sql needs to accomodate 3 index values in
|
SQL dependency removed (proc sql needs to accommodate 3 index values in
|
||||||
a 32767 ibufsize limit)
|
a 32767 ibufsize limit)
|
||||||
*/
|
*/
|
||||||
,START char(10000) label='Starting value for format'
|
,START char(10000) label='Starting value for format'
|
||||||
@@ -40,4 +40,4 @@ create table &libds(
|
|||||||
,LANGUAGE char(8) label='Language for date strings'
|
,LANGUAGE char(8) label='Language for date strings'
|
||||||
);
|
);
|
||||||
|
|
||||||
%mend mddl_sas_cntlout;
|
%mend mddl_sas_cntlout;
|
||||||
|
|||||||
@@ -39,6 +39,14 @@ data _null_;
|
|||||||
put 'io.close(file) ';
|
put 'io.close(file) ';
|
||||||
run;
|
run;
|
||||||
|
|
||||||
|
/* ensure big enough lrecl to avoid lua compilation issues */
|
||||||
|
%local optval;
|
||||||
|
%let optval=%sysfunc(getoption(lrecl));
|
||||||
|
options lrecl=1024;
|
||||||
|
|
||||||
|
/* execute the lua code by using a .lua extension */
|
||||||
%inc "%sysfunc(pathname(work))/ml_gsubfile.lua" /source2;
|
%inc "%sysfunc(pathname(work))/ml_gsubfile.lua" /source2;
|
||||||
|
|
||||||
|
options lrecl=&optval;
|
||||||
|
|
||||||
%mend ml_gsubfile;
|
%mend ml_gsubfile;
|
||||||
|
|||||||
@@ -389,6 +389,14 @@ data _null_;
|
|||||||
put '-- JSON.LUA ENDS HERE ';
|
put '-- JSON.LUA ENDS HERE ';
|
||||||
run;
|
run;
|
||||||
|
|
||||||
|
/* ensure big enough lrecl to avoid lua compilation issues */
|
||||||
|
%local optval;
|
||||||
|
%let optval=%sysfunc(getoption(lrecl));
|
||||||
|
options lrecl=1024;
|
||||||
|
|
||||||
|
/* execute the lua code by using a .lua extension */
|
||||||
%inc "%sysfunc(pathname(work))/ml_json.lua" /source2;
|
%inc "%sysfunc(pathname(work))/ml_json.lua" /source2;
|
||||||
|
|
||||||
|
options lrecl=&optval;
|
||||||
|
|
||||||
%mend ml_json;
|
%mend ml_json;
|
||||||
|
|||||||
49
main.dox
49
main.dox
@@ -18,7 +18,17 @@
|
|||||||
statements. Those starting `mp_` are macro _procedures_, which generate
|
statements. Those starting `mp_` are macro _procedures_, which generate
|
||||||
SAS statements, and must therefore be applied accordingly.
|
SAS statements, and must therefore be applied accordingly.
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/*! \dir ddl
|
||||||
|
* \brief Data Definition Language files
|
||||||
|
* \details Provides templates for commonly used tables in sasjs/core.
|
||||||
|
Attributes:
|
||||||
|
|
||||||
|
* OS independent
|
||||||
|
* No X command
|
||||||
|
* Prefixes: _mddl_
|
||||||
|
*/
|
||||||
|
|
||||||
/*! \dir fcmp
|
/*! \dir fcmp
|
||||||
* \brief Macros for generating FCMP functions
|
* \brief Macros for generating FCMP functions
|
||||||
@@ -92,4 +102,39 @@
|
|||||||
* Auto-generated from the plain source `.lua` files in the same directory
|
* Auto-generated from the plain source `.lua` files in the same directory
|
||||||
* Prefixes: _ml_
|
* Prefixes: _ml_
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/*! \dir tests/base
|
||||||
|
* \brief Tests for Base macros
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*! \dir tests/ddlonly
|
||||||
|
* \brief Tests for DDL macros
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*! \dir tests/sas9only
|
||||||
|
* \brief Tests for SAS Metadata macros
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*! \dir tests/serveronly
|
||||||
|
* \brief Tests for SASjs Server macros
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*! \dir tests/viyaonly
|
||||||
|
* \brief Tests for Viya macros
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*! \dir tests/x-platform
|
||||||
|
* \brief Tests for cross-platform macros
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*! \dir xplatform
|
||||||
|
* \brief Cross Platform, works on all SAS servers (Viya, EBI, SASjs)
|
||||||
|
* \details Useful when you need to run a single piece of code against Viya,
|
||||||
|
SAS 9 with metadata, or SASjs on Base SAS.
|
||||||
|
|
||||||
|
* OS independent
|
||||||
|
* No X command
|
||||||
|
* Prefixes: _mx_
|
||||||
|
|
||||||
|
*/
|
||||||
|
|||||||
@@ -2,7 +2,10 @@
|
|||||||
@file mm_adduser2group.sas
|
@file mm_adduser2group.sas
|
||||||
@brief Adds a user to a group
|
@brief Adds a user to a group
|
||||||
@details Adds a user to a metadata group. The macro first checks whether the
|
@details Adds a user to a metadata group. The macro first checks whether the
|
||||||
user is in that group, and if not, the user is added.
|
user is in that group, and if not, the user is added.
|
||||||
|
|
||||||
|
Note that the macro does not check inherited group memberships - it looks at
|
||||||
|
direct members only.
|
||||||
|
|
||||||
Usage:
|
Usage:
|
||||||
|
|
||||||
@@ -12,10 +15,10 @@
|
|||||||
|
|
||||||
@param user= the user name (not displayname)
|
@param user= the user name (not displayname)
|
||||||
@param group= the group to which to add the user
|
@param group= the group to which to add the user
|
||||||
@param mdebug= set to 1 to show debug info in log
|
@param mdebug= (0) set to 1 to show debug info in log
|
||||||
|
|
||||||
@warning the macro does not check inherited group memberships - it looks at
|
<h4> Related Files </h4>
|
||||||
direct members only
|
@li ms_adduser2group.sas
|
||||||
|
|
||||||
@version 9.3
|
@version 9.3
|
||||||
@author Allan Bowe
|
@author Allan Bowe
|
||||||
|
|||||||
@@ -1,22 +1,21 @@
|
|||||||
/**
|
/**
|
||||||
@file mm_createdataset.sas
|
@file
|
||||||
@brief Create a dataset from a metadata definition
|
@brief Create an empty dataset from a metadata definition
|
||||||
@details This macro was built to support viewing empty tables in
|
@details This macro was built to support viewing empty tables in
|
||||||
https://datacontroller.io - a free evaluation copy is available by
|
https://datacontroller.io
|
||||||
contacting the author (Allan Bowe).
|
|
||||||
|
|
||||||
The table can be retrieved using LIBRARY.DATASET reference, or directly
|
The table can be retrieved using LIBRARY.DATASET reference, or directly
|
||||||
using the metadata URI.
|
using the metadata URI.
|
||||||
|
|
||||||
The dataset is written to the WORK library.
|
The dataset is written to the WORK library.
|
||||||
|
|
||||||
usage:
|
Usage:
|
||||||
|
|
||||||
%mm_createdataset(libds=metlib.some_dataset)
|
%mm_createdataset(libds=metlib.some_dataset)
|
||||||
|
|
||||||
or
|
or
|
||||||
|
|
||||||
%mm_createdataset(tableuri=G5X8AFW1.BE00015Y)
|
%mm_createdataset(tableuri=G5X8AFW1.BE00015Y)
|
||||||
|
|
||||||
<h4> SAS Macros </h4>
|
<h4> SAS Macros </h4>
|
||||||
@li mm_getlibs.sas
|
@li mm_getlibs.sas
|
||||||
@@ -26,9 +25,9 @@
|
|||||||
@param libds= library.dataset metadata source. Note - table names in metadata
|
@param libds= library.dataset metadata source. Note - table names in metadata
|
||||||
can be longer than 32 chars (just fyi, not an issue here)
|
can be longer than 32 chars (just fyi, not an issue here)
|
||||||
@param tableuri= Metadata URI of the table to be created
|
@param tableuri= Metadata URI of the table to be created
|
||||||
@param outds= The dataset to create, default is `work.mm_createdataset`.
|
@param outds= (work.mm_createdataset) The dataset to create. The table name
|
||||||
The table name needs to be 32 chars or less as per SAS naming rules.
|
needs to be 32 chars or less as per SAS naming rules.
|
||||||
@param mdebug= set DBG to 1 to disable DEBUG messages
|
@param mdebug= (0) Set to 1 to enable DEBUG messages
|
||||||
|
|
||||||
@version 9.4
|
@version 9.4
|
||||||
@author Allan Bowe
|
@author Allan Bowe
|
||||||
@@ -54,14 +53,23 @@
|
|||||||
%mm_gettables(uri=&liburi,outds=&tempds2)
|
%mm_gettables(uri=&liburi,outds=&tempds2)
|
||||||
data _null_;
|
data _null_;
|
||||||
set &tempds2;
|
set &tempds2;
|
||||||
if upcase(tablename)="%upcase(%scan(&libds,2,.))";
|
where upcase(tablename)="%upcase(%scan(&libds,2,.))";
|
||||||
|
&dbg putlog tableuri=;
|
||||||
call symputx('tableuri',tableuri);
|
call symputx('tableuri',tableuri);
|
||||||
run;
|
run;
|
||||||
%end;
|
%end;
|
||||||
|
|
||||||
data;run;%let tempds3=&syslast;
|
data;run;
|
||||||
|
%let tempds3=&syslast;
|
||||||
%mm_getcols(tableuri=&tableuri,outds=&tempds3)
|
%mm_getcols(tableuri=&tableuri,outds=&tempds3)
|
||||||
|
|
||||||
|
%if %mf_nobs(&tempds3)=0 %then %do;
|
||||||
|
%put &libds (&tableuri) has no columns defined!!;
|
||||||
|
data &outds;
|
||||||
|
run;
|
||||||
|
%return;
|
||||||
|
%end;
|
||||||
|
|
||||||
data _null_;
|
data _null_;
|
||||||
set &tempds3 end=last;
|
set &tempds3 end=last;
|
||||||
if _n_=1 then call execute('data &outds;');
|
if _n_=1 then call execute('data &outds;');
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
%mm_createfolder(path=/some/meta/folder)
|
%mm_createfolder(path=/some/meta/folder)
|
||||||
|
|
||||||
@param [in] path= Name of the folder to create.
|
@param [in] path= Name of the folder to create.
|
||||||
@param [in] mdebug= set DBG to 1 to disable DEBUG messages
|
@param [in] mdebug= (0) Set to 1 to enable DEBUG messages
|
||||||
|
|
||||||
|
|
||||||
@version 9.4
|
@version 9.4
|
||||||
|
|||||||
@@ -2,7 +2,8 @@
|
|||||||
@file mm_createwebservice.sas
|
@file mm_createwebservice.sas
|
||||||
@brief Create a Web Ready Stored Process
|
@brief Create a Web Ready Stored Process
|
||||||
@details This macro creates a Type 2 Stored Process with the mm_webout macro
|
@details This macro creates a Type 2 Stored Process with the mm_webout macro
|
||||||
included as pre-code.
|
(and dependencies) included as pre-code.
|
||||||
|
|
||||||
Usage:
|
Usage:
|
||||||
|
|
||||||
%* compile macros ;
|
%* compile macros ;
|
||||||
@@ -96,21 +97,37 @@ data _null_;
|
|||||||
put '%macro mp_jsonout(action,ds,jref=_webout,dslabel=,fmt=Y ';
|
put '%macro mp_jsonout(action,ds,jref=_webout,dslabel=,fmt=Y ';
|
||||||
put ' ,engine=DATASTEP ';
|
put ' ,engine=DATASTEP ';
|
||||||
put ' ,missing=NULL ';
|
put ' ,missing=NULL ';
|
||||||
put ' ,showmeta=NO ';
|
put ' ,showmeta=N ';
|
||||||
|
put ' ,maxobs=MAX ';
|
||||||
put ')/*/STORE SOURCE*/; ';
|
put ')/*/STORE SOURCE*/; ';
|
||||||
put '%local tempds colinfo fmtds i numcols; ';
|
put '%local tempds colinfo fmtds i numcols stmt_obs; ';
|
||||||
put '%let numcols=0; ';
|
put '%let numcols=0; ';
|
||||||
|
put '%if &maxobs ne MAX %then %let stmt_obs=%str(if _n_>&maxobs then stop;); ';
|
||||||
put ' ';
|
put ' ';
|
||||||
put '%if &action=OPEN %then %do; ';
|
put '%if &action=OPEN %then %do; ';
|
||||||
put ' options nobomfile; ';
|
put ' options nobomfile; ';
|
||||||
put ' data _null_;file &jref encoding=''utf-8'' ; ';
|
put ' data _null_;file &jref encoding=''utf-8'' lrecl=200; ';
|
||||||
put ' put ''{"PROCESSED_DTTM" : "'' "%sysfunc(datetime(),E8601DT26.6)" ''"''; ';
|
put ' put ''{"PROCESSED_DTTM" : "'' "%sysfunc(datetime(),E8601DT26.6)" ''"''; ';
|
||||||
put ' run; ';
|
put ' run; ';
|
||||||
put '%end; ';
|
put '%end; ';
|
||||||
put '%else %if (&action=ARR or &action=OBJ) %then %do; ';
|
put '%else %if (&action=ARR or &action=OBJ) %then %do; ';
|
||||||
|
put ' /* force variable names to always be uppercase in the JSON */ ';
|
||||||
put ' options validvarname=upcase; ';
|
put ' options validvarname=upcase; ';
|
||||||
put ' data _null_; file &jref encoding=''utf-8'' mod; ';
|
put ' /* To avoid issues with _webout on EBI - such as encoding diffs and truncation ';
|
||||||
|
put ' (https://support.sas.com/kb/49/325.html) we use temporary files */ ';
|
||||||
|
put ' filename _sjs1 temp lrecl=200 ; ';
|
||||||
|
put ' data _null_; file _sjs1 encoding=''utf-8''; ';
|
||||||
put ' put ", ""%lowcase(%sysfunc(coalescec(&dslabel,&ds)))"":"; ';
|
put ' put ", ""%lowcase(%sysfunc(coalescec(&dslabel,&ds)))"":"; ';
|
||||||
|
put ' run; ';
|
||||||
|
put ' /* now write to _webout 1 char at a time */ ';
|
||||||
|
put ' data _null_; ';
|
||||||
|
put ' infile _sjs1 lrecl=1 recfm=n; ';
|
||||||
|
put ' file &jref mod lrecl=1 recfm=n; ';
|
||||||
|
put ' input sourcechar $char1. @@; ';
|
||||||
|
put ' format sourcechar hex2.; ';
|
||||||
|
put ' put sourcechar char1. @@; ';
|
||||||
|
put ' run; ';
|
||||||
|
put ' filename _sjs1 clear; ';
|
||||||
put ' ';
|
put ' ';
|
||||||
put ' /* grab col defs */ ';
|
put ' /* grab col defs */ ';
|
||||||
put ' proc contents noprint data=&ds ';
|
put ' proc contents noprint data=&ds ';
|
||||||
@@ -164,13 +181,25 @@ data _null_;
|
|||||||
put ' %put &sysmacroname: Switching to DATASTEP engine; ';
|
put ' %put &sysmacroname: Switching to DATASTEP engine; ';
|
||||||
put ' %goto datastep; ';
|
put ' %goto datastep; ';
|
||||||
put ' %end; ';
|
put ' %end; ';
|
||||||
put ' data &tempds;set &ds; ';
|
put ' data &tempds; ';
|
||||||
|
put ' set &ds; ';
|
||||||
|
put ' &stmt_obs; ';
|
||||||
put ' %if &fmt=N %then format _numeric_ best32.;; ';
|
put ' %if &fmt=N %then format _numeric_ best32.;; ';
|
||||||
put ' /* PRETTY is necessary to avoid line truncation in large files */ ';
|
put ' /* PRETTY is necessary to avoid line truncation in large files */ ';
|
||||||
put ' proc json out=&jref pretty ';
|
put ' filename _sjs2 temp lrecl=131068 encoding=''utf-8''; ';
|
||||||
|
put ' proc json out=_sjs2 pretty ';
|
||||||
put ' %if &action=ARR %then nokeys ; ';
|
put ' %if &action=ARR %then nokeys ; ';
|
||||||
put ' ;export &tempds / nosastags fmtnumeric; ';
|
put ' ;export &tempds / nosastags fmtnumeric; ';
|
||||||
put ' run; ';
|
put ' run; ';
|
||||||
|
put ' /* send back to webout */ ';
|
||||||
|
put ' data _null_; ';
|
||||||
|
put ' infile _sjs2 lrecl=1 recfm=n; ';
|
||||||
|
put ' file &jref mod lrecl=1 recfm=n; ';
|
||||||
|
put ' input sourcechar $char1. @@; ';
|
||||||
|
put ' format sourcechar hex2.; ';
|
||||||
|
put ' put sourcechar char1. @@; ';
|
||||||
|
put ' run; ';
|
||||||
|
put ' filename _sjs2 clear; ';
|
||||||
put ' %end; ';
|
put ' %end; ';
|
||||||
put ' %else %if &engine=DATASTEP %then %do; ';
|
put ' %else %if &engine=DATASTEP %then %do; ';
|
||||||
put ' %datastep: ';
|
put ' %datastep: ';
|
||||||
@@ -229,30 +258,34 @@ data _null_;
|
|||||||
put ' %else %do; ';
|
put ' %else %do; ';
|
||||||
put ' set &ds; ';
|
put ' set &ds; ';
|
||||||
put ' %end; ';
|
put ' %end; ';
|
||||||
|
put ' &stmt_obs; ';
|
||||||
put ' format _numeric_ bart.; ';
|
put ' format _numeric_ bart.; ';
|
||||||
put ' %do i=1 %to &numcols; ';
|
put ' %do i=1 %to &numcols; ';
|
||||||
put ' %if &&typelong&i=char or &fmt=Y %then %do; ';
|
put ' %if &&typelong&i=char or &fmt=Y %then %do; ';
|
||||||
put ' &&name&i=''"''!!trim(prxchange(''s/"/\"/'',-1, ';
|
put ' if findc(&&name&i,''"\''!!''0A0D09000E0F01021011''x) then do; ';
|
||||||
put ' prxchange(''s/''!!''0A''x!!''/\n/'',-1, ';
|
put ' &&name&i=''"''!!trim( ';
|
||||||
put ' prxchange(''s/''!!''0D''x!!''/\r/'',-1, ';
|
put ' prxchange(''s/"/\\"/'',-1, /* double quote */ ';
|
||||||
put ' prxchange(''s/''!!''09''x!!''/\t/'',-1, ';
|
put ' prxchange(''s/\x0A/\n/'',-1, /* new line */ ';
|
||||||
put ' prxchange(''s/''!!''00''x!!''/\\u0000/'',-1, /* NUL */ ';
|
put ' prxchange(''s/\x0D/\r/'',-1, /* carriage return */ ';
|
||||||
put ' prxchange(''s/''!!''0E''x!!''/\\u000E/'',-1, /* SS */ ';
|
put ' prxchange(''s/\x09/\\t/'',-1, /* tab */ ';
|
||||||
put ' prxchange(''s/''!!''0F''x!!''/\\u000F/'',-1, /* SF */ ';
|
put ' prxchange(''s/\x00/\\u0000/'',-1, /* NUL */ ';
|
||||||
put ' prxchange(''s/''!!''01''x!!''/\\u0001/'',-1, /* SOH */ ';
|
put ' prxchange(''s/\x0E/\\u000E/'',-1, /* SS */ ';
|
||||||
put ' prxchange(''s/''!!''02''x!!''/\\u0002/'',-1, /* STX */ ';
|
put ' prxchange(''s/\x0F/\\u000F/'',-1, /* SF */ ';
|
||||||
put ' prxchange(''s/''!!''02''x!!''/\\u0010/'',-1, /* DLE */ ';
|
put ' prxchange(''s/\x01/\\u0001/'',-1, /* SOH */ ';
|
||||||
put ' prxchange(''s/''!!''11''x!!''/\\u0011/'',-1, /* DC1 */ ';
|
put ' prxchange(''s/\x02/\\u0002/'',-1, /* STX */ ';
|
||||||
put ' prxchange(''s/\\/\\\\/'',-1,&&name&i) ';
|
put ' prxchange(''s/\x10/\\u0010/'',-1, /* DLE */ ';
|
||||||
put ' ))))))))))))!!''"''; ';
|
put ' prxchange(''s/\x11/\\u0011/'',-1, /* DC1 */ ';
|
||||||
|
put ' prxchange(''s/\\/\\\\/'',-1,&&name&i) ';
|
||||||
|
put ' ))))))))))))!!''"''; ';
|
||||||
|
put ' end; ';
|
||||||
|
put ' else &&name&i=quote(cats(&&name&i)); ';
|
||||||
put ' %end; ';
|
put ' %end; ';
|
||||||
put ' %end; ';
|
put ' %end; ';
|
||||||
put ' run; ';
|
put ' run; ';
|
||||||
put ' ';
|
put ' ';
|
||||||
put ' /* write to temp loc to avoid _webout truncation ';
|
put ' filename _sjs3 temp lrecl=131068 ; ';
|
||||||
put ' - https://support.sas.com/kb/49/325.html */ ';
|
put ' data _null_; ';
|
||||||
put ' filename _sjs temp lrecl=131068 encoding=''utf-8''; ';
|
put ' file _sjs3 encoding=''utf-8''; ';
|
||||||
put ' data _null_; file _sjs lrecl=131068 encoding=''utf-8'' mod ; ';
|
|
||||||
put ' if _n_=1 then put "["; ';
|
put ' if _n_=1 then put "["; ';
|
||||||
put ' set &tempds; ';
|
put ' set &tempds; ';
|
||||||
put ' if _n_>1 then put "," @; put ';
|
put ' if _n_>1 then put "," @; put ';
|
||||||
@@ -260,39 +293,38 @@ data _null_;
|
|||||||
put ' %do i=1 %to &numcols; ';
|
put ' %do i=1 %to &numcols; ';
|
||||||
put ' %if &i>1 %then "," ; ';
|
put ' %if &i>1 %then "," ; ';
|
||||||
put ' %if &action=OBJ %then """&&name&i"":" ; ';
|
put ' %if &action=OBJ %then """&&name&i"":" ; ';
|
||||||
put ' &&name&i ';
|
put ' "&&name&i"n /* name literal for reserved variable names */ ';
|
||||||
put ' %end; ';
|
put ' %end; ';
|
||||||
put ' %if &action=ARR %then "]" ; %else "}" ; ; ';
|
put ' %if &action=ARR %then "]" ; %else "}" ; ; ';
|
||||||
put ' /* now write the long strings to _webout 1 byte at a time */ ';
|
put ' ';
|
||||||
|
put ' /* close out the table */ ';
|
||||||
put ' data _null_; ';
|
put ' data _null_; ';
|
||||||
put ' length filein 8 fileid 8; ';
|
put ' file _sjs3 mod encoding=''utf-8''; ';
|
||||||
put ' filein=fopen("_sjs",''I'',1,''B''); ';
|
put ' put '']''; ';
|
||||||
put ' fileid=fopen("&jref",''A'',1,''B''); ';
|
|
||||||
put ' rec=''20''x; ';
|
|
||||||
put ' do while(fread(filein)=0); ';
|
|
||||||
put ' rc=fget(filein,rec,1); ';
|
|
||||||
put ' rc=fput(fileid, rec); ';
|
|
||||||
put ' rc=fwrite(fileid); ';
|
|
||||||
put ' end; ';
|
|
||||||
put ' /* close out the table */ ';
|
|
||||||
put ' rc=fput(fileid, "]"); ';
|
|
||||||
put ' rc=fwrite(fileid); ';
|
|
||||||
put ' rc=fclose(filein); ';
|
|
||||||
put ' rc=fclose(fileid); ';
|
|
||||||
put ' run; ';
|
put ' run; ';
|
||||||
put ' filename _sjs clear; ';
|
put ' data _null_; ';
|
||||||
|
put ' infile _sjs3 lrecl=1 recfm=n; ';
|
||||||
|
put ' file &jref mod lrecl=1 recfm=n; ';
|
||||||
|
put ' input sourcechar $char1. @@; ';
|
||||||
|
put ' format sourcechar hex2.; ';
|
||||||
|
put ' put sourcechar char1. @@; ';
|
||||||
|
put ' run; ';
|
||||||
|
put ' filename _sjs3 clear; ';
|
||||||
put ' %end; ';
|
put ' %end; ';
|
||||||
put ' ';
|
put ' ';
|
||||||
put ' proc sql; ';
|
put ' proc sql; ';
|
||||||
put ' drop table &colinfo, &tempds; ';
|
put ' drop table &colinfo, &tempds; ';
|
||||||
put ' ';
|
put ' ';
|
||||||
put ' %if &showmeta=YES %then %do; ';
|
put ' %if %substr(&showmeta,1,1)=Y %then %do; ';
|
||||||
put ' data _null_; file &jref encoding=''utf-8'' mod; ';
|
put ' filename _sjs4 temp lrecl=131068 encoding=''utf-8''; ';
|
||||||
|
put ' data _null_; ';
|
||||||
|
put ' file _sjs4; ';
|
||||||
|
put ' length label $350; ';
|
||||||
put ' put ", ""$%lowcase(%sysfunc(coalescec(&dslabel,&ds)))"":{""vars"":{"; ';
|
put ' put ", ""$%lowcase(%sysfunc(coalescec(&dslabel,&ds)))"":{""vars"":{"; ';
|
||||||
put ' do i=1 to &numcols; ';
|
put ' do i=1 to &numcols; ';
|
||||||
put ' name=quote(trim(symget(cats(''name'',i)))); ';
|
put ' name=quote(trim(symget(cats(''name'',i)))); ';
|
||||||
put ' format=quote(trim(symget(cats(''fmt'',i)))); ';
|
put ' format=quote(trim(symget(cats(''fmt'',i)))); ';
|
||||||
put ' label=quote(trim(symget(cats(''label'',i)))); ';
|
put ' label=quote(prxchange(''s/\\/\\\\/'',-1,trim(symget(cats(''label'',i))))); ';
|
||||||
put ' length=quote(trim(symget(cats(''length'',i)))); ';
|
put ' length=quote(trim(symget(cats(''length'',i)))); ';
|
||||||
put ' type=quote(trim(symget(cats(''typelong'',i)))); ';
|
put ' type=quote(trim(symget(cats(''typelong'',i)))); ';
|
||||||
put ' if i>1 then put "," @@; ';
|
put ' if i>1 then put "," @@; ';
|
||||||
@@ -301,6 +333,15 @@ data _null_;
|
|||||||
put ' end; ';
|
put ' end; ';
|
||||||
put ' put ''}}''; ';
|
put ' put ''}}''; ';
|
||||||
put ' run; ';
|
put ' run; ';
|
||||||
|
put ' /* send back to webout */ ';
|
||||||
|
put ' data _null_; ';
|
||||||
|
put ' infile _sjs4 lrecl=1 recfm=n; ';
|
||||||
|
put ' file &jref mod lrecl=1 recfm=n; ';
|
||||||
|
put ' input sourcechar $char1. @@; ';
|
||||||
|
put ' format sourcechar hex2.; ';
|
||||||
|
put ' put sourcechar char1. @@; ';
|
||||||
|
put ' run; ';
|
||||||
|
put ' filename _sjs4 clear; ';
|
||||||
put ' %end; ';
|
put ' %end; ';
|
||||||
put '%end; ';
|
put '%end; ';
|
||||||
put ' ';
|
put ' ';
|
||||||
@@ -310,8 +351,28 @@ data _null_;
|
|||||||
put ' run; ';
|
put ' run; ';
|
||||||
put '%end; ';
|
put '%end; ';
|
||||||
put '%mend mp_jsonout; ';
|
put '%mend mp_jsonout; ';
|
||||||
|
put ' ';
|
||||||
|
put '%macro mf_getuser( ';
|
||||||
|
put ')/*/STORE SOURCE*/; ';
|
||||||
|
put ' %local user; ';
|
||||||
|
put ' ';
|
||||||
|
put ' %if %symexist(_sasjs_username) %then %let user=&_sasjs_username; ';
|
||||||
|
put ' %else %if %symexist(SYS_COMPUTE_SESSION_OWNER) %then %do; ';
|
||||||
|
put ' %let user=&SYS_COMPUTE_SESSION_OWNER; ';
|
||||||
|
put ' %end; ';
|
||||||
|
put ' %else %if %symexist(_metaperson) %then %do; ';
|
||||||
|
put ' %if %length(&_metaperson)=0 %then %let user=&sysuserid; ';
|
||||||
|
put ' /* sometimes SAS will add @domain extension - remove for consistency */ ';
|
||||||
|
put ' /* but be sure to quote in case of usernames with commas */ ';
|
||||||
|
put ' %else %let user=%unquote(%scan(%quote(&_metaperson),1,@)); ';
|
||||||
|
put ' %end; ';
|
||||||
|
put ' %else %let user=&sysuserid; ';
|
||||||
|
put ' ';
|
||||||
|
put ' %quote(&user) ';
|
||||||
|
put ' ';
|
||||||
|
put '%mend mf_getuser; ';
|
||||||
put '%macro mm_webout(action,ds,dslabel=,fref=_webout,fmt=Y,missing=NULL ';
|
put '%macro mm_webout(action,ds,dslabel=,fref=_webout,fmt=Y,missing=NULL ';
|
||||||
put ' ,showmeta=NO ';
|
put ' ,showmeta=N ';
|
||||||
put '); ';
|
put '); ';
|
||||||
put '%global _webin_file_count _webin_fileref1 _webin_name1 _program _debug ';
|
put '%global _webin_file_count _webin_fileref1 _webin_name1 _program _debug ';
|
||||||
put ' sasjs_tables; ';
|
put ' sasjs_tables; ';
|
||||||
@@ -382,9 +443,10 @@ data _null_;
|
|||||||
put ' ) ';
|
put ' ) ';
|
||||||
put '%end; ';
|
put '%end; ';
|
||||||
put '%else %if &action=CLOSE %then %do; ';
|
put '%else %if &action=CLOSE %then %do; ';
|
||||||
|
put ' /* To avoid issues with _webout on EBI we use a temporary file */ ';
|
||||||
|
put ' filename _sjsref temp lrecl=131068; ';
|
||||||
put ' %if %str(&_debug) ge 131 %then %do; ';
|
put ' %if %str(&_debug) ge 131 %then %do; ';
|
||||||
put ' /* if debug mode, send back first 10 records of each work table also */ ';
|
put ' /* if debug mode, send back first 10 records of each work table also */ ';
|
||||||
put ' options obs=10; ';
|
|
||||||
put ' data;run;%let tempds=%scan(&syslast,2,.); ';
|
put ' data;run;%let tempds=%scan(&syslast,2,.); ';
|
||||||
put ' ods output Members=&tempds; ';
|
put ' ods output Members=&tempds; ';
|
||||||
put ' proc datasets library=WORK memtype=data; ';
|
put ' proc datasets library=WORK memtype=data; ';
|
||||||
@@ -395,11 +457,11 @@ data _null_;
|
|||||||
put ' i+1; ';
|
put ' i+1; ';
|
||||||
put ' call symputx(cats(''wt'',i),name,''l''); ';
|
put ' call symputx(cats(''wt'',i),name,''l''); ';
|
||||||
put ' call symputx(''wtcnt'',i,''l''); ';
|
put ' call symputx(''wtcnt'',i,''l''); ';
|
||||||
put ' data _null_; file &fref mod encoding=''utf-8''; ';
|
put ' data _null_; file _sjsref mod encoding=''utf-8''; ';
|
||||||
put ' put ",""WORK"":{"; ';
|
put ' put ",""WORK"":{"; ';
|
||||||
put ' %do i=1 %to &wtcnt; ';
|
put ' %do i=1 %to &wtcnt; ';
|
||||||
put ' %let wt=&&wt&i; ';
|
put ' %let wt=&&wt&i; ';
|
||||||
put ' data _null_; file &fref mod encoding=''utf-8''; ';
|
put ' data _null_; file _sjsref mod encoding=''utf-8''; ';
|
||||||
put ' dsid=open("WORK.&wt",''is''); ';
|
put ' dsid=open("WORK.&wt",''is''); ';
|
||||||
put ' nlobs=attrn(dsid,''NLOBS''); ';
|
put ' nlobs=attrn(dsid,''NLOBS''); ';
|
||||||
put ' nvars=attrn(dsid,''NVARS''); ';
|
put ' nvars=attrn(dsid,''NVARS''); ';
|
||||||
@@ -408,16 +470,16 @@ data _null_;
|
|||||||
put ' put " ""&wt"" : {"; ';
|
put ' put " ""&wt"" : {"; ';
|
||||||
put ' put ''"nlobs":'' nlobs; ';
|
put ' put ''"nlobs":'' nlobs; ';
|
||||||
put ' put '',"nvars":'' nvars; ';
|
put ' put '',"nvars":'' nvars; ';
|
||||||
put ' %mp_jsonout(OBJ,&wt,jref=&fref,dslabel=first10rows,showmeta=YES) ';
|
put ' %mp_jsonout(OBJ,&wt,jref=_sjsref,dslabel=first10rows,showmeta=Y,maxobs=10) ';
|
||||||
put ' data _null_; file &fref mod encoding=''utf-8''; ';
|
put ' data _null_; file _sjsref mod encoding=''utf-8''; ';
|
||||||
put ' put "}"; ';
|
put ' put "}"; ';
|
||||||
put ' %end; ';
|
put ' %end; ';
|
||||||
put ' data _null_; file &fref mod encoding=''utf-8''; ';
|
put ' data _null_; file _sjsref mod encoding=''utf-8''; ';
|
||||||
put ' put "}"; ';
|
put ' put "}"; ';
|
||||||
put ' run; ';
|
put ' run; ';
|
||||||
put ' %end; ';
|
put ' %end; ';
|
||||||
put ' /* close off json */ ';
|
put ' /* close off json */ ';
|
||||||
put ' data _null_;file &fref mod encoding=''utf-8''; ';
|
put ' data _null_;file _sjsref mod encoding=''utf-8''; ';
|
||||||
put ' _PROGRAM=quote(trim(resolve(symget(''_PROGRAM'')))); ';
|
put ' _PROGRAM=quote(trim(resolve(symget(''_PROGRAM'')))); ';
|
||||||
put ' put ",""SYSUSERID"" : ""&sysuserid"" "; ';
|
put ' put ",""SYSUSERID"" : ""&sysuserid"" "; ';
|
||||||
put ' put ",""MF_GETUSER"" : ""%mf_getuser()"" "; ';
|
put ' put ",""MF_GETUSER"" : ""%mf_getuser()"" "; ';
|
||||||
@@ -428,14 +490,17 @@ data _null_;
|
|||||||
put ' put '',"_METAPERSON": '' _METAPERSON; ';
|
put ' put '',"_METAPERSON": '' _METAPERSON; ';
|
||||||
put ' put '',"_PROGRAM" : '' _PROGRAM ; ';
|
put ' put '',"_PROGRAM" : '' _PROGRAM ; ';
|
||||||
put ' put ",""SYSCC"" : ""&syscc"" "; ';
|
put ' put ",""SYSCC"" : ""&syscc"" "; ';
|
||||||
put ' put ",""SYSERRORTEXT"" : ""&syserrortext"" "; ';
|
put ' put ",""SYSENCODING"" : ""&sysencoding"" "; ';
|
||||||
|
put ' syserrortext=cats(''"'',tranwrd(symget(''syserrortext''),''"'',''\"''),''"''); ';
|
||||||
|
put ' put '',"SYSERRORTEXT" : '' syserrortext; ';
|
||||||
put ' put ",""SYSHOSTNAME"" : ""&syshostname"" "; ';
|
put ' put ",""SYSHOSTNAME"" : ""&syshostname"" "; ';
|
||||||
put ' put ",""SYSJOBID"" : ""&sysjobid"" "; ';
|
put ' put ",""SYSJOBID"" : ""&sysjobid"" "; ';
|
||||||
put ' put ",""SYSSCPL"" : ""&sysscpl"" "; ';
|
put ' put ",""SYSSCPL"" : ""&sysscpl"" "; ';
|
||||||
put ' put ",""SYSSITE"" : ""&syssite"" "; ';
|
put ' put ",""SYSSITE"" : ""&syssite"" "; ';
|
||||||
put ' sysvlong=quote(trim(symget(''sysvlong''))); ';
|
put ' sysvlong=quote(trim(symget(''sysvlong''))); ';
|
||||||
put ' put '',"SYSVLONG" : '' sysvlong; ';
|
put ' put '',"SYSVLONG" : '' sysvlong; ';
|
||||||
put ' put ",""SYSWARNINGTEXT"" : ""&syswarningtext"" "; ';
|
put ' syswarningtext=cats(''"'',tranwrd(symget(''syswarningtext''),''"'',''\"''),''"''); ';
|
||||||
|
put ' put '',"SYSWARNINGTEXT" : '' syswarningtext; ';
|
||||||
put ' put '',"END_DTTM" : "'' "%sysfunc(datetime(),E8601DT26.6)" ''" ''; ';
|
put ' put '',"END_DTTM" : "'' "%sysfunc(datetime(),E8601DT26.6)" ''" ''; ';
|
||||||
put ' length memsize $32; ';
|
put ' length memsize $32; ';
|
||||||
put ' memsize="%sysfunc(INPUTN(%sysfunc(getoption(memsize)), best.),sizekmg.)"; ';
|
put ' memsize="%sysfunc(INPUTN(%sysfunc(getoption(memsize)), best.),sizekmg.)"; ';
|
||||||
@@ -446,28 +511,19 @@ data _null_;
|
|||||||
put ' put ''>>weboutEND<<''; ';
|
put ' put ''>>weboutEND<<''; ';
|
||||||
put ' %end; ';
|
put ' %end; ';
|
||||||
put ' run; ';
|
put ' run; ';
|
||||||
|
put ' /* now write to _webout 1 char at a time */ ';
|
||||||
|
put ' data _null_; ';
|
||||||
|
put ' infile _sjsref lrecl=1 recfm=n; ';
|
||||||
|
put ' file &fref mod lrecl=1 recfm=n; ';
|
||||||
|
put ' input sourcechar $char1. @@; ';
|
||||||
|
put ' format sourcechar hex2.; ';
|
||||||
|
put ' put sourcechar char1. @@; ';
|
||||||
|
put ' run; ';
|
||||||
|
put ' filename _sjsref clear; ';
|
||||||
|
put ' ';
|
||||||
put '%end; ';
|
put '%end; ';
|
||||||
put ' ';
|
put ' ';
|
||||||
put '%mend mm_webout; ';
|
put '%mend mm_webout; ';
|
||||||
put ' ';
|
|
||||||
put '%macro mf_getuser(type=META ';
|
|
||||||
put ')/*/STORE SOURCE*/; ';
|
|
||||||
put ' %local user metavar; ';
|
|
||||||
put ' %if &type=OS %then %let metavar=_secureusername; ';
|
|
||||||
put ' %else %let metavar=_metaperson; ';
|
|
||||||
put ' ';
|
|
||||||
put ' %if %symexist(SYS_COMPUTE_SESSION_OWNER) %then %let user=&SYS_COMPUTE_SESSION_OWNER; ';
|
|
||||||
put ' %else %if %symexist(&metavar) %then %do; ';
|
|
||||||
put ' %if %length(&&&metavar)=0 %then %let user=&sysuserid; ';
|
|
||||||
put ' /* sometimes SAS will add @domain extension - remove for consistency */ ';
|
|
||||||
put ' /* but be sure to quote in case of usernames with commas */ ';
|
|
||||||
put ' %else %let user=%unquote(%scan(%quote(&&&metavar),1,@)); ';
|
|
||||||
put ' %end; ';
|
|
||||||
put ' %else %let user=&sysuserid; ';
|
|
||||||
put ' ';
|
|
||||||
put ' %quote(&user) ';
|
|
||||||
put ' ';
|
|
||||||
put '%mend mf_getuser; ';
|
|
||||||
/* WEBOUT END */
|
/* WEBOUT END */
|
||||||
put '%macro webout(action,ds,dslabel=,fmt=,missing=NULL,showmeta=NO);';
|
put '%macro webout(action,ds,dslabel=,fmt=,missing=NULL,showmeta=NO);';
|
||||||
put ' %mm_webout(&action,ds=&ds,dslabel=&dslabel,fmt=&fmt,missing=&missing';
|
put ' %mm_webout(&action,ds=&ds,dslabel=&dslabel,fmt=&fmt,missing=&missing';
|
||||||
@@ -486,7 +542,7 @@ run;
|
|||||||
%if &x>1 %then %let mod=mod;
|
%if &x>1 %then %let mod=mod;
|
||||||
|
|
||||||
%let fref=%scan(&freflist,&x);
|
%let fref=%scan(&freflist,&x);
|
||||||
%put &sysmacroname: adding &fref;
|
%&mD.put &sysmacroname: adding &fref;
|
||||||
data _null_;
|
data _null_;
|
||||||
file "&work/&tmpfile" lrecl=3000 &mod;
|
file "&work/&tmpfile" lrecl=3000 &mod;
|
||||||
infile &fref;
|
infile &fref;
|
||||||
@@ -522,12 +578,10 @@ data _null_;
|
|||||||
if rc=0 then call symputx('url',url,'l');
|
if rc=0 then call symputx('url',url,'l');
|
||||||
run;
|
run;
|
||||||
|
|
||||||
%put ;%put ;%put ;%put ;%put ;%put ;
|
|
||||||
%put &sysmacroname: STP &name successfully created in &path;
|
%put &sysmacroname: STP &name successfully created in &path;
|
||||||
%put ;%put ;%put ;
|
|
||||||
%put Check it out here:;
|
%put Check it out here:;
|
||||||
%put ;%put ;%put ;
|
%put ;%put ;%put ;
|
||||||
%put &url?_PROGRAM=&path/&name;
|
%put &url?_PROGRAM=&path/&name;
|
||||||
%put ;%put ;%put ;%put ;%put ;%put ;
|
%put ;%put ;%put ;
|
||||||
|
|
||||||
%mend mm_createwebservice;
|
%mend mm_createwebservice;
|
||||||
|
|||||||
@@ -8,17 +8,15 @@
|
|||||||
|
|
||||||
Usage:
|
Usage:
|
||||||
|
|
||||||
options ps=max nonotes nosource;
|
options ps=max nonotes nosource;
|
||||||
%mm_getfoldertree(root=/My/Meta/Path, outds=iwantthisdataset)
|
%mm_getfoldertree(root=/My/Meta/Path, outds=iwantthisdataset)
|
||||||
options notes source;
|
options notes source;
|
||||||
|
|
||||||
@param [in] root= the parent folder under which to return all contents
|
@param [in] root= the parent folder under which to return all contents
|
||||||
@param [out] outds= the dataset to create that contains the list of
|
@param [out] outds= the dataset to create that contains the list of
|
||||||
directories
|
directories
|
||||||
@param [in] mDebug= set to 1 to show debug messages in the log
|
@param [in] mDebug= set to 1 to show debug messages in the log
|
||||||
|
|
||||||
<h4> SAS Macros </h4>
|
|
||||||
|
|
||||||
@version 9.4
|
@version 9.4
|
||||||
@author Allan Bowe
|
@author Allan Bowe
|
||||||
|
|
||||||
|
|||||||
@@ -21,6 +21,7 @@
|
|||||||
|
|
||||||
<h4> SAS Macros </h4>
|
<h4> SAS Macros </h4>
|
||||||
@li mf_getuniquefileref.sas
|
@li mf_getuniquefileref.sas
|
||||||
|
@li mp_abort.sas
|
||||||
|
|
||||||
@author Allan Bowe
|
@author Allan Bowe
|
||||||
|
|
||||||
@@ -57,7 +58,7 @@ data _null_;
|
|||||||
cnt=1;
|
cnt=1;
|
||||||
do while (metadata_getnasn(uri,"Notes",cnt,tsuri)>0);
|
do while (metadata_getnasn(uri,"Notes",cnt,tsuri)>0);
|
||||||
rc=metadata_getattr(tsuri,"Name",value);
|
rc=metadata_getattr(tsuri,"Name",value);
|
||||||
put tsuri= value=;
|
&mD.put tsuri= value=;
|
||||||
if value="SourceCode" then do;
|
if value="SourceCode" then do;
|
||||||
/* found it! */
|
/* found it! */
|
||||||
rc=metadata_getattr(tsuri,"Id",value);
|
rc=metadata_getattr(tsuri,"Id",value);
|
||||||
@@ -70,11 +71,10 @@ data _null_;
|
|||||||
else put (_all_)(=);
|
else put (_all_)(=);
|
||||||
run;
|
run;
|
||||||
|
|
||||||
%if &tsuri=stopifempty %then %do;
|
%mp_abort(iftrue= (&tsuri=stopifempty)
|
||||||
%put %str(WARN)ING: &tree&name.(StoredProcess) not found!;
|
,mac=mm_getstpcode
|
||||||
%return;
|
,msg=%str(&tree&name.(StoredProcess) not found!)
|
||||||
%end;
|
)
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Now we can extract the textstore
|
* Now we can extract the textstore
|
||||||
|
|||||||
@@ -8,17 +8,18 @@
|
|||||||
|
|
||||||
%mm_getusers()
|
%mm_getusers()
|
||||||
|
|
||||||
|
Optionally, filter for a user (useful to get the uri):
|
||||||
|
|
||||||
|
%mm_getusers(user=&_metaperson)
|
||||||
|
|
||||||
@param outds the dataset to create that contains the list of libraries
|
@param outds the dataset to create that contains the list of libraries
|
||||||
|
|
||||||
@returns outds dataset containing all users, with the following columns:
|
@returns outds dataset containing all users, with the following columns:
|
||||||
- uri
|
- uri
|
||||||
- name
|
- name
|
||||||
|
|
||||||
@warning The following filenames are created and then de-assigned:
|
@param user= (0) Set to a metadata user to filter on that user
|
||||||
|
@param outds= (work.mm_getusers) The output table to create
|
||||||
filename sxlemap clear;
|
|
||||||
filename response clear;
|
|
||||||
libname _XML_ clear;
|
|
||||||
|
|
||||||
@version 9.3
|
@version 9.3
|
||||||
@author Allan Bowe
|
@author Allan Bowe
|
||||||
@@ -26,23 +27,49 @@
|
|||||||
**/
|
**/
|
||||||
|
|
||||||
%macro mm_getusers(
|
%macro mm_getusers(
|
||||||
outds=work.mm_getusers
|
outds=work.mm_getusers,
|
||||||
|
user=0
|
||||||
)/*/STORE SOURCE*/;
|
)/*/STORE SOURCE*/;
|
||||||
|
|
||||||
filename response temp;
|
filename response temp;
|
||||||
proc metadata in= '<GetMetadataObjects>
|
%if %superq(user)=0 %then %do;
|
||||||
<Reposid>$METAREPOSITORY</Reposid>
|
proc metadata in= '<GetMetadataObjects>
|
||||||
<Type>Person</Type>
|
<Reposid>$METAREPOSITORY</Reposid>
|
||||||
<NS>SAS</NS>
|
<Type>Person</Type>
|
||||||
<Flags>0</Flags>
|
<NS>SAS</NS>
|
||||||
<Options>
|
<Flags>0</Flags>
|
||||||
<Templates>
|
<Options>
|
||||||
<Person Name=""/>
|
<Templates>
|
||||||
</Templates>
|
<Person Name=""/>
|
||||||
</Options>
|
</Templates>
|
||||||
</GetMetadataObjects>'
|
</Options>
|
||||||
out=response;
|
</GetMetadataObjects>'
|
||||||
run;
|
out=response;
|
||||||
|
run;
|
||||||
|
%end;
|
||||||
|
%else %do;
|
||||||
|
filename inref temp;
|
||||||
|
data _null_;
|
||||||
|
file inref;
|
||||||
|
put "<GetMetadataObjects>";
|
||||||
|
put "<Reposid>$METAREPOSITORY</Reposid>";
|
||||||
|
put "<Type>Person</Type>";
|
||||||
|
put "<NS>SAS</NS>";
|
||||||
|
put "<!-- Specify the OMI_XMLSELECT (128) flag -->";
|
||||||
|
put "<Flags>128</Flags>";
|
||||||
|
put "<Options>";
|
||||||
|
put "<Templates>";
|
||||||
|
put '<Person Name=""/>';
|
||||||
|
put "</Templates>";
|
||||||
|
length string $10000;
|
||||||
|
string=cats('<XMLSELECT search="Person[@Name=',"'&user'",']"/>');
|
||||||
|
put string;
|
||||||
|
put "</Options>";
|
||||||
|
put "</GetMetadataObjects>";
|
||||||
|
run;
|
||||||
|
proc metadata in=inref out=response;
|
||||||
|
run;
|
||||||
|
%end;
|
||||||
|
|
||||||
filename sxlemap temp;
|
filename sxlemap temp;
|
||||||
data _null_;
|
data _null_;
|
||||||
|
|||||||
@@ -43,7 +43,9 @@ data _null_;
|
|||||||
cnt=1;
|
cnt=1;
|
||||||
do while (metadata_getnasn(uri,"Notes",cnt,tsuri)>0);
|
do while (metadata_getnasn(uri,"Notes",cnt,tsuri)>0);
|
||||||
rc=metadata_getattr(tsuri,"Name",value);
|
rc=metadata_getattr(tsuri,"Name",value);
|
||||||
|
%if &mdebug=1 %then %do;
|
||||||
put tsuri= value=;
|
put tsuri= value=;
|
||||||
|
%end;
|
||||||
if value="SourceCode" then do;
|
if value="SourceCode" then do;
|
||||||
/* found it! */
|
/* found it! */
|
||||||
rc=metadata_getattr(tsuri,"Id",value);
|
rc=metadata_getattr(tsuri,"Id",value);
|
||||||
@@ -126,4 +128,4 @@ run;
|
|||||||
filename &frefout clear;
|
filename &frefout clear;
|
||||||
%end;
|
%end;
|
||||||
|
|
||||||
%mend mm_updatestpsourcecode;
|
%mend mm_updatestpsourcecode;
|
||||||
|
|||||||
@@ -4,23 +4,23 @@
|
|||||||
@details This macro should be added to the start of each Stored Process,
|
@details This macro should be added to the start of each Stored Process,
|
||||||
**immediately** followed by a call to:
|
**immediately** followed by a call to:
|
||||||
|
|
||||||
%mm_webout(FETCH)
|
%mm_webout(FETCH)
|
||||||
|
|
||||||
This will read all the input data and create same-named SAS datasets in the
|
This will read all the input data and create same-named SAS datasets in the
|
||||||
WORK library. You can then insert your code, and send data back using the
|
WORK library. You can then insert your code, and send data back using the
|
||||||
following syntax:
|
following syntax:
|
||||||
|
|
||||||
data some datasets; * make some data ;
|
data some datasets; * make some data ;
|
||||||
retain some columns;
|
retain some columns;
|
||||||
run;
|
run;
|
||||||
|
|
||||||
%mm_webout(OPEN)
|
%mm_webout(OPEN)
|
||||||
%mm_webout(ARR,some) * Array format, fast, suitable for large tables ;
|
%mm_webout(ARR,some) * Array format, fast, suitable for large tables ;
|
||||||
%mm_webout(OBJ,datasets) * Object format, easier to work with ;
|
%mm_webout(OBJ,datasets) * Object format, easier to work with ;
|
||||||
|
|
||||||
Finally, wrap everything up send some helpful system variables too
|
Finally, wrap everything up send some helpful system variables too
|
||||||
|
|
||||||
%mm_webout(CLOSE)
|
%mm_webout(CLOSE)
|
||||||
|
|
||||||
|
|
||||||
@param [in] action Either FETCH, OPEN, ARR, OBJ or CLOSE
|
@param [in] action Either FETCH, OPEN, ARR, OBJ or CLOSE
|
||||||
@@ -30,17 +30,21 @@
|
|||||||
@param [out] fref= (_webout) The fileref to which to write the JSON
|
@param [out] fref= (_webout) The fileref to which to write the JSON
|
||||||
@param [in] missing= (NULL) Special numeric missing values can be sent as NULL
|
@param [in] missing= (NULL) Special numeric missing values can be sent as NULL
|
||||||
(eg `null`) or as STRING values (eg `".a"` or `".b"`)
|
(eg `null`) or as STRING values (eg `".a"` or `".b"`)
|
||||||
@param [in] showmeta= (NO) Set to YES to output metadata alongside each table,
|
@param [in] showmeta= (N) Set to Y to output metadata alongside each table,
|
||||||
such as the column formats and types. The metadata is contained inside an
|
such as the column formats and types. The metadata is contained inside an
|
||||||
object with the same name as the table but prefixed with a dollar sign - ie,
|
object with the same name as the table but prefixed with a dollar sign - ie,
|
||||||
`,"$tablename":{"formats":{"col1":"$CHAR1"},"types":{"COL1":"C"}}`
|
`,"$tablename":{"formats":{"col1":"$CHAR1"},"types":{"COL1":"C"}}`
|
||||||
|
|
||||||
|
|
||||||
|
<h4> SAS Macros </h4>
|
||||||
|
@li mp_jsonout.sas
|
||||||
|
|
||||||
@version 9.3
|
@version 9.3
|
||||||
@author Allan Bowe
|
@author Allan Bowe
|
||||||
|
|
||||||
**/
|
**/
|
||||||
%macro mm_webout(action,ds,dslabel=,fref=_webout,fmt=Y,missing=NULL
|
%macro mm_webout(action,ds,dslabel=,fref=_webout,fmt=Y,missing=NULL
|
||||||
,showmeta=NO
|
,showmeta=N
|
||||||
);
|
);
|
||||||
%global _webin_file_count _webin_fileref1 _webin_name1 _program _debug
|
%global _webin_file_count _webin_fileref1 _webin_name1 _program _debug
|
||||||
sasjs_tables;
|
sasjs_tables;
|
||||||
@@ -111,9 +115,10 @@
|
|||||||
)
|
)
|
||||||
%end;
|
%end;
|
||||||
%else %if &action=CLOSE %then %do;
|
%else %if &action=CLOSE %then %do;
|
||||||
|
/* To avoid issues with _webout on EBI we use a temporary file */
|
||||||
|
filename _sjsref temp lrecl=131068;
|
||||||
%if %str(&_debug) ge 131 %then %do;
|
%if %str(&_debug) ge 131 %then %do;
|
||||||
/* if debug mode, send back first 10 records of each work table also */
|
/* if debug mode, send back first 10 records of each work table also */
|
||||||
options obs=10;
|
|
||||||
data;run;%let tempds=%scan(&syslast,2,.);
|
data;run;%let tempds=%scan(&syslast,2,.);
|
||||||
ods output Members=&tempds;
|
ods output Members=&tempds;
|
||||||
proc datasets library=WORK memtype=data;
|
proc datasets library=WORK memtype=data;
|
||||||
@@ -124,11 +129,11 @@
|
|||||||
i+1;
|
i+1;
|
||||||
call symputx(cats('wt',i),name,'l');
|
call symputx(cats('wt',i),name,'l');
|
||||||
call symputx('wtcnt',i,'l');
|
call symputx('wtcnt',i,'l');
|
||||||
data _null_; file &fref mod encoding='utf-8';
|
data _null_; file _sjsref mod encoding='utf-8';
|
||||||
put ",""WORK"":{";
|
put ",""WORK"":{";
|
||||||
%do i=1 %to &wtcnt;
|
%do i=1 %to &wtcnt;
|
||||||
%let wt=&&wt&i;
|
%let wt=&&wt&i;
|
||||||
data _null_; file &fref mod encoding='utf-8';
|
data _null_; file _sjsref mod encoding='utf-8';
|
||||||
dsid=open("WORK.&wt",'is');
|
dsid=open("WORK.&wt",'is');
|
||||||
nlobs=attrn(dsid,'NLOBS');
|
nlobs=attrn(dsid,'NLOBS');
|
||||||
nvars=attrn(dsid,'NVARS');
|
nvars=attrn(dsid,'NVARS');
|
||||||
@@ -137,16 +142,16 @@
|
|||||||
put " ""&wt"" : {";
|
put " ""&wt"" : {";
|
||||||
put '"nlobs":' nlobs;
|
put '"nlobs":' nlobs;
|
||||||
put ',"nvars":' nvars;
|
put ',"nvars":' nvars;
|
||||||
%mp_jsonout(OBJ,&wt,jref=&fref,dslabel=first10rows,showmeta=YES)
|
%mp_jsonout(OBJ,&wt,jref=_sjsref,dslabel=first10rows,showmeta=Y,maxobs=10)
|
||||||
data _null_; file &fref mod encoding='utf-8';
|
data _null_; file _sjsref mod encoding='utf-8';
|
||||||
put "}";
|
put "}";
|
||||||
%end;
|
%end;
|
||||||
data _null_; file &fref mod encoding='utf-8';
|
data _null_; file _sjsref mod encoding='utf-8';
|
||||||
put "}";
|
put "}";
|
||||||
run;
|
run;
|
||||||
%end;
|
%end;
|
||||||
/* close off json */
|
/* close off json */
|
||||||
data _null_;file &fref mod encoding='utf-8';
|
data _null_;file _sjsref mod encoding='utf-8';
|
||||||
_PROGRAM=quote(trim(resolve(symget('_PROGRAM'))));
|
_PROGRAM=quote(trim(resolve(symget('_PROGRAM'))));
|
||||||
put ",""SYSUSERID"" : ""&sysuserid"" ";
|
put ",""SYSUSERID"" : ""&sysuserid"" ";
|
||||||
put ",""MF_GETUSER"" : ""%mf_getuser()"" ";
|
put ",""MF_GETUSER"" : ""%mf_getuser()"" ";
|
||||||
@@ -157,14 +162,17 @@
|
|||||||
put ',"_METAPERSON": ' _METAPERSON;
|
put ',"_METAPERSON": ' _METAPERSON;
|
||||||
put ',"_PROGRAM" : ' _PROGRAM ;
|
put ',"_PROGRAM" : ' _PROGRAM ;
|
||||||
put ",""SYSCC"" : ""&syscc"" ";
|
put ",""SYSCC"" : ""&syscc"" ";
|
||||||
put ",""SYSERRORTEXT"" : ""&syserrortext"" ";
|
put ",""SYSENCODING"" : ""&sysencoding"" ";
|
||||||
|
syserrortext=cats('"',tranwrd(symget('syserrortext'),'"','\"'),'"');
|
||||||
|
put ',"SYSERRORTEXT" : ' syserrortext;
|
||||||
put ",""SYSHOSTNAME"" : ""&syshostname"" ";
|
put ",""SYSHOSTNAME"" : ""&syshostname"" ";
|
||||||
put ",""SYSJOBID"" : ""&sysjobid"" ";
|
put ",""SYSJOBID"" : ""&sysjobid"" ";
|
||||||
put ",""SYSSCPL"" : ""&sysscpl"" ";
|
put ",""SYSSCPL"" : ""&sysscpl"" ";
|
||||||
put ",""SYSSITE"" : ""&syssite"" ";
|
put ",""SYSSITE"" : ""&syssite"" ";
|
||||||
sysvlong=quote(trim(symget('sysvlong')));
|
sysvlong=quote(trim(symget('sysvlong')));
|
||||||
put ',"SYSVLONG" : ' sysvlong;
|
put ',"SYSVLONG" : ' sysvlong;
|
||||||
put ",""SYSWARNINGTEXT"" : ""&syswarningtext"" ";
|
syswarningtext=cats('"',tranwrd(symget('syswarningtext'),'"','\"'),'"');
|
||||||
|
put ',"SYSWARNINGTEXT" : ' syswarningtext;
|
||||||
put ',"END_DTTM" : "' "%sysfunc(datetime(),E8601DT26.6)" '" ';
|
put ',"END_DTTM" : "' "%sysfunc(datetime(),E8601DT26.6)" '" ';
|
||||||
length memsize $32;
|
length memsize $32;
|
||||||
memsize="%sysfunc(INPUTN(%sysfunc(getoption(memsize)), best.),sizekmg.)";
|
memsize="%sysfunc(INPUTN(%sysfunc(getoption(memsize)), best.),sizekmg.)";
|
||||||
@@ -175,6 +183,16 @@
|
|||||||
put '>>weboutEND<<';
|
put '>>weboutEND<<';
|
||||||
%end;
|
%end;
|
||||||
run;
|
run;
|
||||||
|
/* now write to _webout 1 char at a time */
|
||||||
|
data _null_;
|
||||||
|
infile _sjsref lrecl=1 recfm=n;
|
||||||
|
file &fref mod lrecl=1 recfm=n;
|
||||||
|
input sourcechar $char1. @@;
|
||||||
|
format sourcechar hex2.;
|
||||||
|
put sourcechar char1. @@;
|
||||||
|
run;
|
||||||
|
filename _sjsref clear;
|
||||||
|
|
||||||
%end;
|
%end;
|
||||||
|
|
||||||
%mend mm_webout;
|
%mend mm_webout;
|
||||||
|
|||||||
2443
package-lock.json
generated
2443
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -26,14 +26,13 @@
|
|||||||
"homepage": "https://core.sasjs.io",
|
"homepage": "https://core.sasjs.io",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "sasjs cbd -t viya",
|
"build": "sasjs cbd -t server",
|
||||||
"docs": "sasjs doc -t docsonly && ./sasjs/utils/build.sh",
|
"docs": "sasjs doc -t docsonly && ./sasjs/utils/build.sh",
|
||||||
"test": "sasjs test -t viya",
|
"test": "sasjs test -t server",
|
||||||
"lint": "sasjs lint",
|
"lint": "sasjs lint",
|
||||||
"prepare": "git rev-parse --git-dir && git config core.hooksPath ./.git-hooks || true"
|
"prepare": "git rev-parse --git-dir && git config core.hooksPath ./.git-hooks || true"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@sasjs/cli": "3.6.0",
|
"@sasjs/cli": "3.13.0"
|
||||||
"@sasjs/core": "4.4.7"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,8 +6,10 @@
|
|||||||
"fcmp",
|
"fcmp",
|
||||||
"lua",
|
"lua",
|
||||||
"server",
|
"server",
|
||||||
"tests/crossplatform",
|
"xplatform",
|
||||||
"tests/ddl"
|
"tests/base",
|
||||||
|
"tests/ddlonly",
|
||||||
|
"tests/x-platform"
|
||||||
],
|
],
|
||||||
"docConfig": {
|
"docConfig": {
|
||||||
"displayMacroCore": false,
|
"displayMacroCore": false,
|
||||||
@@ -25,31 +27,25 @@
|
|||||||
"mcTestAppLoc": "/Public/temp/macrocore"
|
"mcTestAppLoc": "/Public/temp/macrocore"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"defaultTarget": "viya",
|
"defaultTarget": "server",
|
||||||
"targets": [
|
"targets": [
|
||||||
{
|
{
|
||||||
"name": "viya",
|
"name": "viya",
|
||||||
"serverUrl": "https://sas.analytium.co.uk",
|
"serverUrl": "",
|
||||||
"serverType": "SASVIYA",
|
"serverType": "SASVIYA",
|
||||||
"httpsAgentOptions": {
|
"httpsAgentOptions": {
|
||||||
"allowInsecureRequests": false
|
"allowInsecureRequests": false
|
||||||
},
|
},
|
||||||
"appLoc": "/Public/temp/macrocore",
|
"appLoc": "/Public/app/macrocore",
|
||||||
"macroFolders": [
|
"macroFolders": [
|
||||||
"viya",
|
"viya",
|
||||||
"tests/viyaonly"
|
"tests/viyaonly"
|
||||||
],
|
],
|
||||||
"programFolders": [],
|
|
||||||
"binaryFolders": [],
|
|
||||||
"deployConfig": {
|
|
||||||
"deployServicePack": true,
|
|
||||||
"deployScripts": []
|
|
||||||
},
|
|
||||||
"contextName": "SAS Job Execution compute context"
|
"contextName": "SAS Job Execution compute context"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "sas9",
|
"name": "sas9",
|
||||||
"serverUrl": "https://sas.analytium.co.uk:8343",
|
"serverUrl": "",
|
||||||
"serverType": "SAS9",
|
"serverType": "SAS9",
|
||||||
"httpsAgentOptions": {
|
"httpsAgentOptions": {
|
||||||
"allowInsecureRequests": false
|
"allowInsecureRequests": false
|
||||||
@@ -71,7 +67,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "server",
|
"name": "server",
|
||||||
"serverUrl": " ",
|
"serverUrl": "https://sas.4gl.io",
|
||||||
"serverType": "SASJS",
|
"serverType": "SASJS",
|
||||||
"httpsAgentOptions": {
|
"httpsAgentOptions": {
|
||||||
"allowInsecureRequests": false
|
"allowInsecureRequests": false
|
||||||
@@ -80,17 +76,11 @@
|
|||||||
"macroFolders": [
|
"macroFolders": [
|
||||||
"server",
|
"server",
|
||||||
"tests/serveronly"
|
"tests/serveronly"
|
||||||
],
|
]
|
||||||
"programFolders": [],
|
|
||||||
"binaryFolders": [],
|
|
||||||
"deployConfig": {
|
|
||||||
"deployServicePack": true,
|
|
||||||
"deployScripts": []
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "docsonly",
|
"name": "docsonly",
|
||||||
"serverType": "SAS9",
|
"serverType": "SASJS",
|
||||||
"appLoc": "dummy",
|
"appLoc": "dummy",
|
||||||
"macroFolders": [
|
"macroFolders": [
|
||||||
"meta",
|
"meta",
|
||||||
@@ -98,6 +88,7 @@
|
|||||||
"server",
|
"server",
|
||||||
"viya",
|
"viya",
|
||||||
"tests/sas9only",
|
"tests/sas9only",
|
||||||
|
"tests/serveronly",
|
||||||
"tests/viyaonly"
|
"tests/viyaonly"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@@ -116,4 +107,4 @@
|
|||||||
"contextName": "SAS Job Execution compute context"
|
"contextName": "SAS Job Execution compute context"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@@ -12,7 +12,7 @@
|
|||||||
|
|
||||||
Usage:
|
Usage:
|
||||||
|
|
||||||
%mfs_httpheader(Content-type,application/csv)
|
%mfs_httpheader(Content-Type,application/csv)
|
||||||
|
|
||||||
@param [in] header_name Name of the http header to set
|
@param [in] header_name Name of the http header to set
|
||||||
@param [in] header_value Value of the http header to set
|
@param [in] header_value Value of the http header to set
|
||||||
@@ -28,6 +28,7 @@
|
|||||||
%macro mfs_httpheader(header_name
|
%macro mfs_httpheader(header_name
|
||||||
,header_value
|
,header_value
|
||||||
)/*/STORE SOURCE*/;
|
)/*/STORE SOURCE*/;
|
||||||
|
%global sasjs_stpsrv_header_loc;
|
||||||
%local fref fid i;
|
%local fref fid i;
|
||||||
|
|
||||||
%if %sysfunc(filename(fref,&sasjs_stpsrv_header_loc)) ne 0 %then %do;
|
%if %sysfunc(filename(fref,&sasjs_stpsrv_header_loc)) ne 0 %then %do;
|
||||||
|
|||||||
122
server/ms_adduser2group.sas
Normal file
122
server/ms_adduser2group.sas
Normal file
@@ -0,0 +1,122 @@
|
|||||||
|
/**
|
||||||
|
@file
|
||||||
|
@brief Adds a user to a group on SASjs Server
|
||||||
|
@details Adds a user to a group based on userid and groupid. Both user and
|
||||||
|
group must already exist.
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
|
||||||
|
%ms_adduser2group(uid=1,gid=1)
|
||||||
|
|
||||||
|
|
||||||
|
@param [in] uid= (0) The User ID to be added
|
||||||
|
@param [in] gid= (0) The Group ID to contain the new user
|
||||||
|
@param [in] mdebug= (0) Set to 1 to enable DEBUG messages
|
||||||
|
@param [out] outds= (work.ms_adduser2group) This output dataset will contain
|
||||||
|
the new list of group members, eg:
|
||||||
|
|DISPLAYNAME:$18.|USERNAME:$10.|ID:best.|
|
||||||
|
|---|---|---|
|
||||||
|
|`Super Admin `|`secretuser `|`1`|
|
||||||
|
|`Sabir Hassan`|`sabir`|`2`|
|
||||||
|
|`Mihajlo Medjedovic `|`mihajlo `|`3`|
|
||||||
|
|`Ivor Townsend `|`ivor `|`4`|
|
||||||
|
|`New User `|`newuser `|`5`|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<h4> SAS Macros </h4>
|
||||||
|
@li mf_getuniquefileref.sas
|
||||||
|
@li mf_getuniquelibref.sas
|
||||||
|
@li mp_abort.sas
|
||||||
|
|
||||||
|
<h4> Related Files </h4>
|
||||||
|
@li ms_creategroup.sas
|
||||||
|
@li ms_createuser.sas
|
||||||
|
|
||||||
|
**/
|
||||||
|
|
||||||
|
%macro ms_adduser2group(uid=0
|
||||||
|
,gid=0
|
||||||
|
,outds=work.ms_adduser2group
|
||||||
|
,mdebug=0
|
||||||
|
);
|
||||||
|
|
||||||
|
%mp_abort(
|
||||||
|
iftrue=(&syscc ne 0)
|
||||||
|
,mac=ms_adduser2group.sas
|
||||||
|
,msg=%str(syscc=&syscc on macro entry)
|
||||||
|
)
|
||||||
|
|
||||||
|
%local fref0 fref1 fref2 libref optval rc msg;
|
||||||
|
%let fref0=%mf_getuniquefileref();
|
||||||
|
%let fref1=%mf_getuniquefileref();
|
||||||
|
%let libref=%mf_getuniquelibref();
|
||||||
|
|
||||||
|
/* avoid sending bom marker to API */
|
||||||
|
%let optval=%sysfunc(getoption(bomfile));
|
||||||
|
options nobomfile;
|
||||||
|
|
||||||
|
data _null_;
|
||||||
|
file &fref0 lrecl=1000;
|
||||||
|
infile "&_sasjs_tokenfile" lrecl=1000;
|
||||||
|
input;
|
||||||
|
if _n_=1 then put "accept: application/json";
|
||||||
|
put _infile_;
|
||||||
|
run;
|
||||||
|
|
||||||
|
%if &mdebug=1 %then %do;
|
||||||
|
%put _local_;
|
||||||
|
data _null_;
|
||||||
|
infile &fref0;
|
||||||
|
input;
|
||||||
|
put _infile_;
|
||||||
|
run;
|
||||||
|
%end;
|
||||||
|
|
||||||
|
proc http method='POST' headerin=&fref0 out=&fref1
|
||||||
|
url="&_sasjs_apiserverurl/SASjsApi/group/&gid/&uid";
|
||||||
|
%if &mdebug=1 %then %do;
|
||||||
|
debug level=1;
|
||||||
|
%end;
|
||||||
|
run;
|
||||||
|
|
||||||
|
%mp_abort(
|
||||||
|
iftrue=(&syscc ne 0)
|
||||||
|
,mac=ms_adduser2group.sas
|
||||||
|
,msg=%str(Issue submitting query to SASjsApi/group)
|
||||||
|
)
|
||||||
|
|
||||||
|
libname &libref JSON fileref=&fref1;
|
||||||
|
|
||||||
|
data &outds;
|
||||||
|
set &libref..users;
|
||||||
|
drop ordinal_root ordinal_users;
|
||||||
|
%if &mdebug=1 %then %do;
|
||||||
|
putlog _all_;
|
||||||
|
%end;
|
||||||
|
run;
|
||||||
|
|
||||||
|
|
||||||
|
%mp_abort(
|
||||||
|
iftrue=(&syscc ne 0)
|
||||||
|
,mac=ms_creategroup.sas
|
||||||
|
,msg=%str(Issue reading response JSON)
|
||||||
|
)
|
||||||
|
|
||||||
|
/* reset options */
|
||||||
|
options &optval;
|
||||||
|
|
||||||
|
%if &mdebug=0 %then %do;
|
||||||
|
filename &fref0 clear;
|
||||||
|
filename &fref1 clear;
|
||||||
|
libname &libref clear;
|
||||||
|
%end;
|
||||||
|
%else %do;
|
||||||
|
data _null_;
|
||||||
|
infile &fref1;
|
||||||
|
input;
|
||||||
|
putlog _infile_;
|
||||||
|
run;
|
||||||
|
%end;
|
||||||
|
|
||||||
|
%mend ms_adduser2group;
|
||||||
@@ -21,6 +21,7 @@
|
|||||||
@li mf_getuniquefileref.sas
|
@li mf_getuniquefileref.sas
|
||||||
@li mf_getuniquename.sas
|
@li mf_getuniquename.sas
|
||||||
@li mp_abort.sas
|
@li mp_abort.sas
|
||||||
|
@li ms_deletefile.sas
|
||||||
|
|
||||||
**/
|
**/
|
||||||
|
|
||||||
@@ -29,15 +30,22 @@
|
|||||||
,mdebug=0
|
,mdebug=0
|
||||||
);
|
);
|
||||||
|
|
||||||
%local fname0 fname1 fname2 boundary fname statcd msg;
|
/* first, delete in case it exists */
|
||||||
|
%ms_deletefile(&driveloc,mdebug=&mdebug)
|
||||||
|
|
||||||
|
%local fname0 fname1 fname2 boundary fname statcd msg optval;
|
||||||
%let fname0=%mf_getuniquefileref();
|
%let fname0=%mf_getuniquefileref();
|
||||||
%let fname1=%mf_getuniquefileref();
|
%let fname1=%mf_getuniquefileref();
|
||||||
%let fname2=%mf_getuniquefileref();
|
%let fname2=%mf_getuniquefileref();
|
||||||
%let boundary=%mf_getuniquename();
|
%let boundary=%mf_getuniquename();
|
||||||
|
|
||||||
|
/* avoid sending bom marker to API */
|
||||||
|
%let optval=%sysfunc(getoption(bomfile));
|
||||||
|
options nobomfile;
|
||||||
|
|
||||||
data _null_;
|
data _null_;
|
||||||
file &fname0 termstr=crlf;
|
file &fname0 termstr=crlf lrecl=32767;
|
||||||
infile &inref end=eof;
|
infile &inref end=eof lrecl=32767;
|
||||||
if _n_ = 1 then do;
|
if _n_ = 1 then do;
|
||||||
put "--&boundary.";
|
put "--&boundary.";
|
||||||
put 'Content-Disposition: form-data; name="filePath"';
|
put 'Content-Disposition: form-data; name="filePath"';
|
||||||
@@ -57,17 +65,20 @@ data _null_;
|
|||||||
run;
|
run;
|
||||||
|
|
||||||
data _null_;
|
data _null_;
|
||||||
file &fname1;
|
file &fname1 lrecl=1000;
|
||||||
put "Content-Type: multipart/form-data; boundary=&boundary";
|
infile "&_sasjs_tokenfile" lrecl=1000;
|
||||||
|
input;
|
||||||
|
if _n_=1 then put "Content-Type: multipart/form-data; boundary=&boundary";
|
||||||
|
put _infile_;
|
||||||
run;
|
run;
|
||||||
|
|
||||||
%if &mdebug=1 %then %do;
|
%if &mdebug=1 %then %do;
|
||||||
data _null_;
|
data _null_;
|
||||||
infile &fname0;
|
infile &fname0 lrecl=32767;
|
||||||
input;
|
input;
|
||||||
put _infile_;
|
put _infile_;
|
||||||
data _null_;
|
data _null_;
|
||||||
infile &fname1;
|
infile &fname1 lrecl=32767;
|
||||||
input;
|
input;
|
||||||
put _infile_;
|
put _infile_;
|
||||||
run;
|
run;
|
||||||
@@ -95,4 +106,7 @@ run;
|
|||||||
,msg=%superq(msg)
|
,msg=%superq(msg)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
/* reset options */
|
||||||
|
options &optval;
|
||||||
|
|
||||||
%mend ms_createfile;
|
%mend ms_createfile;
|
||||||
|
|||||||
149
server/ms_creategroup.sas
Normal file
149
server/ms_creategroup.sas
Normal file
@@ -0,0 +1,149 @@
|
|||||||
|
/**
|
||||||
|
@file
|
||||||
|
@brief Creates a group on SASjs Server
|
||||||
|
@details Creates a group on SASjs Server with the following attributes:
|
||||||
|
|
||||||
|
@li name
|
||||||
|
@li description
|
||||||
|
@li isActive
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
|
||||||
|
%ms_creategroup(mynewgroup)
|
||||||
|
|
||||||
|
%ms_creategroup(mynewergroup, desc=The group description)
|
||||||
|
|
||||||
|
@param [in] groupname The group name to create. No spaces or special chars.
|
||||||
|
@param [in] desc= (0) If no description provided, group name will be used.
|
||||||
|
@param [in] isactive= (true) Set to false to create an inactive group.
|
||||||
|
@param [in] mdebug= (0) Set to 1 to enable DEBUG messages
|
||||||
|
@param [out] outds= (work.ms_creategroup) This output dataset will contain the
|
||||||
|
values from the JSON response (such as the id of the new group)
|
||||||
|
|DESCRIPTION:$1.|GROUPID:best.|ISACTIVE:best.|NAME:$11.|
|
||||||
|
|---|---|---|---|
|
||||||
|
|`The group description`|`2 `|`1 `|`mynewergroup `|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<h4> SAS Macros </h4>
|
||||||
|
@li mf_getuniquefileref.sas
|
||||||
|
@li mf_getuniquelibref.sas
|
||||||
|
@li mp_abort.sas
|
||||||
|
|
||||||
|
<h4> Related Files </h4>
|
||||||
|
@li ms_creategroup.test.sas
|
||||||
|
@li ms_getgroups.sas
|
||||||
|
|
||||||
|
**/
|
||||||
|
|
||||||
|
%macro ms_creategroup(groupname
|
||||||
|
,desc=0
|
||||||
|
,isactive=true
|
||||||
|
,outds=work.ms_creategroup
|
||||||
|
,mdebug=0
|
||||||
|
);
|
||||||
|
|
||||||
|
%mp_abort(
|
||||||
|
iftrue=(&syscc ne 0)
|
||||||
|
,mac=ms_creategroup.sas
|
||||||
|
,msg=%str(syscc=&syscc on macro entry)
|
||||||
|
)
|
||||||
|
|
||||||
|
%local fref0 fref1 fref2 libref optval rc msg;
|
||||||
|
%let fref0=%mf_getuniquefileref();
|
||||||
|
%let fref1=%mf_getuniquefileref();
|
||||||
|
%let fref2=%mf_getuniquefileref();
|
||||||
|
%let libref=%mf_getuniquelibref();
|
||||||
|
|
||||||
|
/* avoid sending bom marker to API */
|
||||||
|
%let optval=%sysfunc(getoption(bomfile));
|
||||||
|
options nobomfile;
|
||||||
|
|
||||||
|
data _null_;
|
||||||
|
file &fref0 termstr=crlf;
|
||||||
|
name=quote(cats(symget('groupname')));
|
||||||
|
description=quote(cats(symget('desc')));
|
||||||
|
if cats(description)='"0"' then description=name;
|
||||||
|
isactive=symget('isactive');
|
||||||
|
%if &mdebug=1 %then %do;
|
||||||
|
putlog _all_;
|
||||||
|
%end;
|
||||||
|
|
||||||
|
put '{'@;
|
||||||
|
put '"name":' name @;
|
||||||
|
put ',"description":' description @;
|
||||||
|
put ',"isActive":' isactive @;
|
||||||
|
put '}';
|
||||||
|
run;
|
||||||
|
|
||||||
|
data _null_;
|
||||||
|
file &fref1 lrecl=1000;
|
||||||
|
infile "&_sasjs_tokenfile" lrecl=1000;
|
||||||
|
input;
|
||||||
|
if _n_=1 then do;
|
||||||
|
put "Content-Type: application/json";
|
||||||
|
put "accept: application/json";
|
||||||
|
end;
|
||||||
|
put _infile_;
|
||||||
|
run;
|
||||||
|
|
||||||
|
%if &mdebug=1 %then %do;
|
||||||
|
data _null_;
|
||||||
|
infile &fref0;
|
||||||
|
input;
|
||||||
|
put _infile_;
|
||||||
|
data _null_;
|
||||||
|
infile &fref1;
|
||||||
|
input;
|
||||||
|
put _infile_;
|
||||||
|
run;
|
||||||
|
%end;
|
||||||
|
|
||||||
|
proc http method='POST' in=&fref0 headerin=&fref1 out=&fref2
|
||||||
|
url="&_sasjs_apiserverurl/SASjsApi/group";
|
||||||
|
%if &mdebug=1 %then %do;
|
||||||
|
debug level=1;
|
||||||
|
%end;
|
||||||
|
run;
|
||||||
|
|
||||||
|
%mp_abort(
|
||||||
|
iftrue=(&syscc ne 0)
|
||||||
|
,mac=ms_creategroup.sas
|
||||||
|
,msg=%str(Issue submitting query to SASjsApi/group)
|
||||||
|
)
|
||||||
|
|
||||||
|
libname &libref JSON fileref=&fref2;
|
||||||
|
|
||||||
|
data &outds;
|
||||||
|
set &libref..root;
|
||||||
|
drop ordinal_root;
|
||||||
|
%if &mdebug=1 %then %do;
|
||||||
|
putlog _all_;
|
||||||
|
%end;
|
||||||
|
run;
|
||||||
|
|
||||||
|
|
||||||
|
%mp_abort(
|
||||||
|
iftrue=(&syscc ne 0)
|
||||||
|
,mac=ms_creategroup.sas
|
||||||
|
,msg=%str(Issue reading response JSON)
|
||||||
|
)
|
||||||
|
|
||||||
|
/* reset options */
|
||||||
|
options &optval;
|
||||||
|
|
||||||
|
%if &mdebug=0 %then %do;
|
||||||
|
filename &fref0 clear;
|
||||||
|
filename &fref1 clear;
|
||||||
|
filename &fref2 clear;
|
||||||
|
libname &libref clear;
|
||||||
|
%end;
|
||||||
|
%else %do;
|
||||||
|
data _null_;
|
||||||
|
infile &fref2;
|
||||||
|
input;
|
||||||
|
putlog _infile_;
|
||||||
|
run;
|
||||||
|
%end;
|
||||||
|
|
||||||
|
%mend ms_creategroup;
|
||||||
146
server/ms_createuser.sas
Normal file
146
server/ms_createuser.sas
Normal file
@@ -0,0 +1,146 @@
|
|||||||
|
/**
|
||||||
|
@file
|
||||||
|
@brief Creates a user on SASjs Server
|
||||||
|
@details Creates a user on SASjs Server with the following attributes:
|
||||||
|
|
||||||
|
@li UserName
|
||||||
|
@li Password
|
||||||
|
@li isAdmin
|
||||||
|
@li displayName
|
||||||
|
|
||||||
|
The userid is created by sasjs/server. All users are created with `isActive`
|
||||||
|
set to `true`.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
%ms_createuser(newuser,secretpass,displayname=New User!)
|
||||||
|
|
||||||
|
@param [in] username The username to apply. No spaces or special characters.
|
||||||
|
@param [in] password The initial password to set.
|
||||||
|
@param [in] isadmin= (false) Set to true to give the user admin rights
|
||||||
|
@param [in] displayName= (0) Set a friendly name (spaces & special characters
|
||||||
|
are ok). If not provided, username will be used instead.
|
||||||
|
@param [in] mdebug= (0) Set to 1 to enable DEBUG messages
|
||||||
|
@param [out] outds= (work.ms_createuser) This output dataset will contain the
|
||||||
|
values from the JSON response (such as the id of the new user)
|
||||||
|
|ID:best.|DISPLAYNAME:$8.|USERNAME:$8.|ISACTIVE:best.|ISADMIN:best.|
|
||||||
|
|---|---|---|---|---|
|
||||||
|
|`6 `|`New User `|`newuser `|`1 `|`0 `|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<h4> SAS Macros </h4>
|
||||||
|
@li mf_getuniquefileref.sas
|
||||||
|
@li mf_getuniquelibref.sas
|
||||||
|
@li mp_abort.sas
|
||||||
|
|
||||||
|
<h4> Related Files </h4>
|
||||||
|
@li ms_createuser.test.sas
|
||||||
|
@li ms_getusers.sas
|
||||||
|
|
||||||
|
**/
|
||||||
|
|
||||||
|
%macro ms_createuser(username,password
|
||||||
|
,isadmin=false
|
||||||
|
,displayname=0
|
||||||
|
,outds=work.ms_createuser
|
||||||
|
,mdebug=0
|
||||||
|
);
|
||||||
|
|
||||||
|
%mp_abort(
|
||||||
|
iftrue=(&syscc ne 0)
|
||||||
|
,mac=ms_createuser.sas
|
||||||
|
,msg=%str(syscc=&syscc on macro entry)
|
||||||
|
)
|
||||||
|
|
||||||
|
%local fref0 fref1 fref2 libref optval rc msg;
|
||||||
|
%let fref0=%mf_getuniquefileref();
|
||||||
|
%let fref1=%mf_getuniquefileref();
|
||||||
|
%let fref2=%mf_getuniquefileref();
|
||||||
|
%let libref=%mf_getuniquelibref();
|
||||||
|
|
||||||
|
/* avoid sending bom marker to API */
|
||||||
|
%let optval=%sysfunc(getoption(bomfile));
|
||||||
|
options nobomfile;
|
||||||
|
|
||||||
|
data _null_;
|
||||||
|
file &fref0 termstr=crlf;
|
||||||
|
username=quote(cats(symget('username')));
|
||||||
|
password=quote(cats(symget('password')));
|
||||||
|
isadmin=symget('isadmin');
|
||||||
|
displayname=quote(cats(symget('displayname')));
|
||||||
|
if displayname='"0"' then displayname=username;
|
||||||
|
|
||||||
|
%if &mdebug=1 %then %do;
|
||||||
|
putlog _all_;
|
||||||
|
%end;
|
||||||
|
|
||||||
|
put '{'@;
|
||||||
|
put '"displayName":' displayname @;
|
||||||
|
put ',"username":' username @;
|
||||||
|
put ',"password":' password @;
|
||||||
|
put ',"isAdmin":' isadmin @;
|
||||||
|
put ',"isActive": true }';
|
||||||
|
run;
|
||||||
|
|
||||||
|
data _null_;
|
||||||
|
file &fref1 lrecl=1000;
|
||||||
|
infile "&_sasjs_tokenfile" lrecl=1000;
|
||||||
|
input;
|
||||||
|
if _n_=1 then do;
|
||||||
|
put "Content-Type: application/json";
|
||||||
|
put "accept: application/json";
|
||||||
|
end;
|
||||||
|
put _infile_;
|
||||||
|
run;
|
||||||
|
|
||||||
|
%if &mdebug=1 %then %do;
|
||||||
|
data _null_;
|
||||||
|
infile &fref0;
|
||||||
|
input;
|
||||||
|
put _infile_;
|
||||||
|
data _null_;
|
||||||
|
infile &fref1;
|
||||||
|
input;
|
||||||
|
put _infile_;
|
||||||
|
run;
|
||||||
|
%end;
|
||||||
|
|
||||||
|
proc http method='POST' in=&fref0 headerin=&fref1 out=&fref2
|
||||||
|
url="&_sasjs_apiserverurl/SASjsApi/user";
|
||||||
|
%if &mdebug=1 %then %do;
|
||||||
|
debug level=1;
|
||||||
|
%end;
|
||||||
|
run;
|
||||||
|
|
||||||
|
%mp_abort(
|
||||||
|
iftrue=(&syscc ne 0)
|
||||||
|
,mac=ms_createuser.sas
|
||||||
|
,msg=%str(Issue submitting query to SASjsApi/user)
|
||||||
|
)
|
||||||
|
|
||||||
|
libname &libref JSON fileref=&fref2;
|
||||||
|
|
||||||
|
data &outds;
|
||||||
|
set &libref..root;
|
||||||
|
drop ordinal_root;
|
||||||
|
run;
|
||||||
|
|
||||||
|
|
||||||
|
%mp_abort(
|
||||||
|
iftrue=(&syscc ne 0)
|
||||||
|
,mac=ms_createuser.sas
|
||||||
|
,msg=%str(Issue reading response JSON)
|
||||||
|
)
|
||||||
|
|
||||||
|
/* reset options */
|
||||||
|
options &optval;
|
||||||
|
|
||||||
|
%if &mdebug=1 %then %do;
|
||||||
|
filename &fref0 clear;
|
||||||
|
filename &fref1 clear;
|
||||||
|
filename &fref2 clear;
|
||||||
|
libname &libref clear;
|
||||||
|
%end;
|
||||||
|
|
||||||
|
%mend ms_createuser;
|
||||||
574
server/ms_createwebservice.sas
Normal file
574
server/ms_createwebservice.sas
Normal file
@@ -0,0 +1,574 @@
|
|||||||
|
/**
|
||||||
|
@file ms_createwebservice.sas
|
||||||
|
@brief Create a Web-Ready Stored Program
|
||||||
|
@details This macro creates a Stored Program along with the necessary precode
|
||||||
|
to enable the %webout() macro
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
<code>
|
||||||
|
%* compile macros ;
|
||||||
|
filename mc url "https://raw.githubusercontent.com/sasjs/core/main/all.sas";
|
||||||
|
%inc mc;
|
||||||
|
|
||||||
|
%* parmcards lets us write to a text file from open code ;
|
||||||
|
filename ft15f001 temp;
|
||||||
|
parmcards4;
|
||||||
|
%webout(FETCH)
|
||||||
|
%* do some sas, any inputs are now already WORK tables;
|
||||||
|
data example1 example2;
|
||||||
|
set sashelp.class;
|
||||||
|
run;
|
||||||
|
%* send data back;
|
||||||
|
%webout(OPEN)
|
||||||
|
%webout(ARR,example1) * Array format, fast, suitable for large tables ;
|
||||||
|
%webout(OBJ,example2) * Object format, easier to work with ;
|
||||||
|
%webout(CLOSE)
|
||||||
|
;;;;
|
||||||
|
%ms_createwebservice(path=/Public/app/common,name=appInit,code=ft15f001)
|
||||||
|
|
||||||
|
</code>
|
||||||
|
|
||||||
|
For more examples of using these web services with the SASjs Adapter, see:
|
||||||
|
https://github.com/sasjs/adapter#readme
|
||||||
|
|
||||||
|
@param [in] path= (0) The full SASjs Drive path in which to create the service
|
||||||
|
@param [in] name= Stored Program name
|
||||||
|
@param [in] desc= The description of the service (not implemented yet)
|
||||||
|
@param [in] precode= Space separated list of filerefs, pointing to the code
|
||||||
|
that needs to be attached to the beginning of the service (optional)
|
||||||
|
@param [in] code= (ft15f001) Space seperated fileref(s) of the actual code to
|
||||||
|
be added
|
||||||
|
@param [in] mDebug= (0) set to 1 to show debug messages in the log
|
||||||
|
|
||||||
|
<h4> SAS Macros </h4>
|
||||||
|
@li ms_createfile.sas
|
||||||
|
@li mf_getuser.sas
|
||||||
|
@li mf_getuniquename.sas
|
||||||
|
@li mf_getuniquefileref.sas
|
||||||
|
@li mp_abort.sas
|
||||||
|
|
||||||
|
<h4> Related Files </h4>
|
||||||
|
@li ms_createwebservice.test.sas
|
||||||
|
|
||||||
|
@version 9.2
|
||||||
|
@author Allan Bowe
|
||||||
|
|
||||||
|
**/
|
||||||
|
|
||||||
|
%macro ms_createwebservice(path=0
|
||||||
|
,name=initService
|
||||||
|
,precode=
|
||||||
|
,code=ft15f001
|
||||||
|
,desc=Not currently used
|
||||||
|
,mDebug=0
|
||||||
|
)/*/STORE SOURCE*/;
|
||||||
|
|
||||||
|
%local dbg;
|
||||||
|
%if &mdebug=1 %then %do;
|
||||||
|
%put &sysmacroname entry vars:;
|
||||||
|
%put _local_;
|
||||||
|
%end;
|
||||||
|
%else %let dbg=*;
|
||||||
|
|
||||||
|
%mp_abort(iftrue=(&syscc ge 4 )
|
||||||
|
,mac=ms_createwebservice
|
||||||
|
,msg=%str(syscc=&syscc on macro entry)
|
||||||
|
)
|
||||||
|
%mp_abort(iftrue=("&path"="0")
|
||||||
|
,mac=ms_createwebservice
|
||||||
|
,msg=%str(Path not provided)
|
||||||
|
)
|
||||||
|
|
||||||
|
/* remove any trailing slash */
|
||||||
|
%if "%substr(&path,%length(&path),1)" = "/" %then
|
||||||
|
%let path=%substr(&path,1,%length(&path)-1);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add webout macro
|
||||||
|
* These put statements are auto generated - to change the macro, change the
|
||||||
|
* source (ms_webout) and run `build.py`
|
||||||
|
*/
|
||||||
|
%local sasjsref;
|
||||||
|
%let sasjsref=%mf_getuniquefileref();
|
||||||
|
data _null_;
|
||||||
|
file &sasjsref termstr=crlf lrecl=512;
|
||||||
|
put "/* Created on %sysfunc(datetime(),datetime19.) by %mf_getuser() */";
|
||||||
|
/* WEBOUT BEGIN */
|
||||||
|
put ' ';
|
||||||
|
put '%macro mp_jsonout(action,ds,jref=_webout,dslabel=,fmt=Y ';
|
||||||
|
put ' ,engine=DATASTEP ';
|
||||||
|
put ' ,missing=NULL ';
|
||||||
|
put ' ,showmeta=N ';
|
||||||
|
put ' ,maxobs=MAX ';
|
||||||
|
put ')/*/STORE SOURCE*/; ';
|
||||||
|
put '%local tempds colinfo fmtds i numcols stmt_obs; ';
|
||||||
|
put '%let numcols=0; ';
|
||||||
|
put '%if &maxobs ne MAX %then %let stmt_obs=%str(if _n_>&maxobs then stop;); ';
|
||||||
|
put ' ';
|
||||||
|
put '%if &action=OPEN %then %do; ';
|
||||||
|
put ' options nobomfile; ';
|
||||||
|
put ' data _null_;file &jref encoding=''utf-8'' lrecl=200; ';
|
||||||
|
put ' put ''{"PROCESSED_DTTM" : "'' "%sysfunc(datetime(),E8601DT26.6)" ''"''; ';
|
||||||
|
put ' run; ';
|
||||||
|
put '%end; ';
|
||||||
|
put '%else %if (&action=ARR or &action=OBJ) %then %do; ';
|
||||||
|
put ' /* force variable names to always be uppercase in the JSON */ ';
|
||||||
|
put ' options validvarname=upcase; ';
|
||||||
|
put ' /* To avoid issues with _webout on EBI - such as encoding diffs and truncation ';
|
||||||
|
put ' (https://support.sas.com/kb/49/325.html) we use temporary files */ ';
|
||||||
|
put ' filename _sjs1 temp lrecl=200 ; ';
|
||||||
|
put ' data _null_; file _sjs1 encoding=''utf-8''; ';
|
||||||
|
put ' put ", ""%lowcase(%sysfunc(coalescec(&dslabel,&ds)))"":"; ';
|
||||||
|
put ' run; ';
|
||||||
|
put ' /* now write to _webout 1 char at a time */ ';
|
||||||
|
put ' data _null_; ';
|
||||||
|
put ' infile _sjs1 lrecl=1 recfm=n; ';
|
||||||
|
put ' file &jref mod lrecl=1 recfm=n; ';
|
||||||
|
put ' input sourcechar $char1. @@; ';
|
||||||
|
put ' format sourcechar hex2.; ';
|
||||||
|
put ' put sourcechar char1. @@; ';
|
||||||
|
put ' run; ';
|
||||||
|
put ' filename _sjs1 clear; ';
|
||||||
|
put ' ';
|
||||||
|
put ' /* grab col defs */ ';
|
||||||
|
put ' proc contents noprint data=&ds ';
|
||||||
|
put ' out=_data_(keep=name type length format formatl formatd varnum label); ';
|
||||||
|
put ' run; ';
|
||||||
|
put ' %let colinfo=%scan(&syslast,2,.); ';
|
||||||
|
put ' proc sort data=&colinfo; ';
|
||||||
|
put ' by varnum; ';
|
||||||
|
put ' run; ';
|
||||||
|
put ' /* move meta to mac vars */ ';
|
||||||
|
put ' data _null_; ';
|
||||||
|
put ' if _n_=1 then call symputx(''numcols'',nobs,''l''); ';
|
||||||
|
put ' set &colinfo end=last nobs=nobs; ';
|
||||||
|
put ' name=upcase(name); ';
|
||||||
|
put ' /* fix formats */ ';
|
||||||
|
put ' if type=2 or type=6 then do; ';
|
||||||
|
put ' typelong=''char''; ';
|
||||||
|
put ' length fmt $49.; ';
|
||||||
|
put ' if format='''' then fmt=cats(''$'',length,''.''); ';
|
||||||
|
put ' else if formatl=0 then fmt=cats(format,''.''); ';
|
||||||
|
put ' else fmt=cats(format,formatl,''.''); ';
|
||||||
|
put ' newlen=max(formatl,length); ';
|
||||||
|
put ' end; ';
|
||||||
|
put ' else do; ';
|
||||||
|
put ' typelong=''num''; ';
|
||||||
|
put ' if format='''' then fmt=''best.''; ';
|
||||||
|
put ' else if formatl=0 then fmt=cats(format,''.''); ';
|
||||||
|
put ' else if formatd=0 then fmt=cats(format,formatl,''.''); ';
|
||||||
|
put ' else fmt=cats(format,formatl,''.'',formatd); ';
|
||||||
|
put ' /* needs to be wide, for datetimes etc */ ';
|
||||||
|
put ' newlen=max(length,formatl,24); ';
|
||||||
|
put ' end; ';
|
||||||
|
put ' /* 32 char unique name */ ';
|
||||||
|
put ' newname=''sasjs''!!substr(cats(put(md5(name),$hex32.)),1,27); ';
|
||||||
|
put ' ';
|
||||||
|
put ' call symputx(cats(''name'',_n_),name,''l''); ';
|
||||||
|
put ' call symputx(cats(''newname'',_n_),newname,''l''); ';
|
||||||
|
put ' call symputx(cats(''len'',_n_),newlen,''l''); ';
|
||||||
|
put ' call symputx(cats(''length'',_n_),length,''l''); ';
|
||||||
|
put ' call symputx(cats(''fmt'',_n_),fmt,''l''); ';
|
||||||
|
put ' call symputx(cats(''type'',_n_),type,''l''); ';
|
||||||
|
put ' call symputx(cats(''typelong'',_n_),typelong,''l''); ';
|
||||||
|
put ' call symputx(cats(''label'',_n_),coalescec(label,name),''l''); ';
|
||||||
|
put ' run; ';
|
||||||
|
put ' ';
|
||||||
|
put ' %let tempds=%substr(_%sysfunc(compress(%sysfunc(uuidgen()),-)),1,32); ';
|
||||||
|
put ' ';
|
||||||
|
put ' %if &engine=PROCJSON %then %do; ';
|
||||||
|
put ' %if &missing=STRING %then %do; ';
|
||||||
|
put ' %put &sysmacroname: Special Missings not supported in proc json.; ';
|
||||||
|
put ' %put &sysmacroname: Switching to DATASTEP engine; ';
|
||||||
|
put ' %goto datastep; ';
|
||||||
|
put ' %end; ';
|
||||||
|
put ' data &tempds; ';
|
||||||
|
put ' set &ds; ';
|
||||||
|
put ' &stmt_obs; ';
|
||||||
|
put ' %if &fmt=N %then format _numeric_ best32.;; ';
|
||||||
|
put ' /* PRETTY is necessary to avoid line truncation in large files */ ';
|
||||||
|
put ' filename _sjs2 temp lrecl=131068 encoding=''utf-8''; ';
|
||||||
|
put ' proc json out=_sjs2 pretty ';
|
||||||
|
put ' %if &action=ARR %then nokeys ; ';
|
||||||
|
put ' ;export &tempds / nosastags fmtnumeric; ';
|
||||||
|
put ' run; ';
|
||||||
|
put ' /* send back to webout */ ';
|
||||||
|
put ' data _null_; ';
|
||||||
|
put ' infile _sjs2 lrecl=1 recfm=n; ';
|
||||||
|
put ' file &jref mod lrecl=1 recfm=n; ';
|
||||||
|
put ' input sourcechar $char1. @@; ';
|
||||||
|
put ' format sourcechar hex2.; ';
|
||||||
|
put ' put sourcechar char1. @@; ';
|
||||||
|
put ' run; ';
|
||||||
|
put ' filename _sjs2 clear; ';
|
||||||
|
put ' %end; ';
|
||||||
|
put ' %else %if &engine=DATASTEP %then %do; ';
|
||||||
|
put ' %datastep: ';
|
||||||
|
put ' %if %sysfunc(exist(&ds)) ne 1 & %sysfunc(exist(&ds,VIEW)) ne 1 ';
|
||||||
|
put ' %then %do; ';
|
||||||
|
put ' %put &sysmacroname: &ds NOT FOUND!!!; ';
|
||||||
|
put ' %return; ';
|
||||||
|
put ' %end; ';
|
||||||
|
put ' ';
|
||||||
|
put ' %if &fmt=Y %then %do; ';
|
||||||
|
put ' data _data_; ';
|
||||||
|
put ' /* rename on entry */ ';
|
||||||
|
put ' set &ds(rename=( ';
|
||||||
|
put ' %do i=1 %to &numcols; ';
|
||||||
|
put ' &&name&i=&&newname&i ';
|
||||||
|
put ' %end; ';
|
||||||
|
put ' )); ';
|
||||||
|
put ' %do i=1 %to &numcols; ';
|
||||||
|
put ' length &&name&i $&&len&i; ';
|
||||||
|
put ' %if &&typelong&i=num %then %do; ';
|
||||||
|
put ' &&name&i=left(put(&&newname&i,&&fmt&i)); ';
|
||||||
|
put ' %end; ';
|
||||||
|
put ' %else %do; ';
|
||||||
|
put ' &&name&i=put(&&newname&i,&&fmt&i); ';
|
||||||
|
put ' %end; ';
|
||||||
|
put ' drop &&newname&i; ';
|
||||||
|
put ' %end; ';
|
||||||
|
put ' if _error_ then call symputx(''syscc'',1012); ';
|
||||||
|
put ' run; ';
|
||||||
|
put ' %let fmtds=&syslast; ';
|
||||||
|
put ' %end; ';
|
||||||
|
put ' ';
|
||||||
|
put ' proc format; /* credit yabwon for special null removal */ ';
|
||||||
|
put ' value bart (default=40) ';
|
||||||
|
put ' %if &missing=NULL %then %do; ';
|
||||||
|
put ' ._ - .z = null ';
|
||||||
|
put ' %end; ';
|
||||||
|
put ' %else %do; ';
|
||||||
|
put ' ._ = [quote()] ';
|
||||||
|
put ' . = null ';
|
||||||
|
put ' .a - .z = [quote()] ';
|
||||||
|
put ' %end; ';
|
||||||
|
put ' other = [best.]; ';
|
||||||
|
put ' ';
|
||||||
|
put ' data &tempds; ';
|
||||||
|
put ' attrib _all_ label=''''; ';
|
||||||
|
put ' %do i=1 %to &numcols; ';
|
||||||
|
put ' %if &&typelong&i=char or &fmt=Y %then %do; ';
|
||||||
|
put ' length &&name&i $32767; ';
|
||||||
|
put ' format &&name&i $32767.; ';
|
||||||
|
put ' %end; ';
|
||||||
|
put ' %end; ';
|
||||||
|
put ' %if &fmt=Y %then %do; ';
|
||||||
|
put ' set &fmtds; ';
|
||||||
|
put ' %end; ';
|
||||||
|
put ' %else %do; ';
|
||||||
|
put ' set &ds; ';
|
||||||
|
put ' %end; ';
|
||||||
|
put ' &stmt_obs; ';
|
||||||
|
put ' format _numeric_ bart.; ';
|
||||||
|
put ' %do i=1 %to &numcols; ';
|
||||||
|
put ' %if &&typelong&i=char or &fmt=Y %then %do; ';
|
||||||
|
put ' if findc(&&name&i,''"\''!!''0A0D09000E0F01021011''x) then do; ';
|
||||||
|
put ' &&name&i=''"''!!trim( ';
|
||||||
|
put ' prxchange(''s/"/\\"/'',-1, /* double quote */ ';
|
||||||
|
put ' prxchange(''s/\x0A/\n/'',-1, /* new line */ ';
|
||||||
|
put ' prxchange(''s/\x0D/\r/'',-1, /* carriage return */ ';
|
||||||
|
put ' prxchange(''s/\x09/\\t/'',-1, /* tab */ ';
|
||||||
|
put ' prxchange(''s/\x00/\\u0000/'',-1, /* NUL */ ';
|
||||||
|
put ' prxchange(''s/\x0E/\\u000E/'',-1, /* SS */ ';
|
||||||
|
put ' prxchange(''s/\x0F/\\u000F/'',-1, /* SF */ ';
|
||||||
|
put ' prxchange(''s/\x01/\\u0001/'',-1, /* SOH */ ';
|
||||||
|
put ' prxchange(''s/\x02/\\u0002/'',-1, /* STX */ ';
|
||||||
|
put ' prxchange(''s/\x10/\\u0010/'',-1, /* DLE */ ';
|
||||||
|
put ' prxchange(''s/\x11/\\u0011/'',-1, /* DC1 */ ';
|
||||||
|
put ' prxchange(''s/\\/\\\\/'',-1,&&name&i) ';
|
||||||
|
put ' ))))))))))))!!''"''; ';
|
||||||
|
put ' end; ';
|
||||||
|
put ' else &&name&i=quote(cats(&&name&i)); ';
|
||||||
|
put ' %end; ';
|
||||||
|
put ' %end; ';
|
||||||
|
put ' run; ';
|
||||||
|
put ' ';
|
||||||
|
put ' filename _sjs3 temp lrecl=131068 ; ';
|
||||||
|
put ' data _null_; ';
|
||||||
|
put ' file _sjs3 encoding=''utf-8''; ';
|
||||||
|
put ' if _n_=1 then put "["; ';
|
||||||
|
put ' set &tempds; ';
|
||||||
|
put ' if _n_>1 then put "," @; put ';
|
||||||
|
put ' %if &action=ARR %then "[" ; %else "{" ; ';
|
||||||
|
put ' %do i=1 %to &numcols; ';
|
||||||
|
put ' %if &i>1 %then "," ; ';
|
||||||
|
put ' %if &action=OBJ %then """&&name&i"":" ; ';
|
||||||
|
put ' "&&name&i"n /* name literal for reserved variable names */ ';
|
||||||
|
put ' %end; ';
|
||||||
|
put ' %if &action=ARR %then "]" ; %else "}" ; ; ';
|
||||||
|
put ' ';
|
||||||
|
put ' /* close out the table */ ';
|
||||||
|
put ' data _null_; ';
|
||||||
|
put ' file _sjs3 mod encoding=''utf-8''; ';
|
||||||
|
put ' put '']''; ';
|
||||||
|
put ' run; ';
|
||||||
|
put ' data _null_; ';
|
||||||
|
put ' infile _sjs3 lrecl=1 recfm=n; ';
|
||||||
|
put ' file &jref mod lrecl=1 recfm=n; ';
|
||||||
|
put ' input sourcechar $char1. @@; ';
|
||||||
|
put ' format sourcechar hex2.; ';
|
||||||
|
put ' put sourcechar char1. @@; ';
|
||||||
|
put ' run; ';
|
||||||
|
put ' filename _sjs3 clear; ';
|
||||||
|
put ' %end; ';
|
||||||
|
put ' ';
|
||||||
|
put ' proc sql; ';
|
||||||
|
put ' drop table &colinfo, &tempds; ';
|
||||||
|
put ' ';
|
||||||
|
put ' %if %substr(&showmeta,1,1)=Y %then %do; ';
|
||||||
|
put ' filename _sjs4 temp lrecl=131068 encoding=''utf-8''; ';
|
||||||
|
put ' data _null_; ';
|
||||||
|
put ' file _sjs4; ';
|
||||||
|
put ' length label $350; ';
|
||||||
|
put ' put ", ""$%lowcase(%sysfunc(coalescec(&dslabel,&ds)))"":{""vars"":{"; ';
|
||||||
|
put ' do i=1 to &numcols; ';
|
||||||
|
put ' name=quote(trim(symget(cats(''name'',i)))); ';
|
||||||
|
put ' format=quote(trim(symget(cats(''fmt'',i)))); ';
|
||||||
|
put ' label=quote(prxchange(''s/\\/\\\\/'',-1,trim(symget(cats(''label'',i))))); ';
|
||||||
|
put ' length=quote(trim(symget(cats(''length'',i)))); ';
|
||||||
|
put ' type=quote(trim(symget(cats(''typelong'',i)))); ';
|
||||||
|
put ' if i>1 then put "," @@; ';
|
||||||
|
put ' put name '':{"format":'' format '',"label":'' label ';
|
||||||
|
put ' '',"length":'' length '',"type":'' type ''}''; ';
|
||||||
|
put ' end; ';
|
||||||
|
put ' put ''}}''; ';
|
||||||
|
put ' run; ';
|
||||||
|
put ' /* send back to webout */ ';
|
||||||
|
put ' data _null_; ';
|
||||||
|
put ' infile _sjs4 lrecl=1 recfm=n; ';
|
||||||
|
put ' file &jref mod lrecl=1 recfm=n; ';
|
||||||
|
put ' input sourcechar $char1. @@; ';
|
||||||
|
put ' format sourcechar hex2.; ';
|
||||||
|
put ' put sourcechar char1. @@; ';
|
||||||
|
put ' run; ';
|
||||||
|
put ' filename _sjs4 clear; ';
|
||||||
|
put ' %end; ';
|
||||||
|
put '%end; ';
|
||||||
|
put ' ';
|
||||||
|
put '%else %if &action=CLOSE %then %do; ';
|
||||||
|
put ' data _null_; file &jref encoding=''utf-8'' mod ; ';
|
||||||
|
put ' put "}"; ';
|
||||||
|
put ' run; ';
|
||||||
|
put '%end; ';
|
||||||
|
put '%mend mp_jsonout; ';
|
||||||
|
put ' ';
|
||||||
|
put '%macro mf_getuser( ';
|
||||||
|
put ')/*/STORE SOURCE*/; ';
|
||||||
|
put ' %local user; ';
|
||||||
|
put ' ';
|
||||||
|
put ' %if %symexist(_sasjs_username) %then %let user=&_sasjs_username; ';
|
||||||
|
put ' %else %if %symexist(SYS_COMPUTE_SESSION_OWNER) %then %do; ';
|
||||||
|
put ' %let user=&SYS_COMPUTE_SESSION_OWNER; ';
|
||||||
|
put ' %end; ';
|
||||||
|
put ' %else %if %symexist(_metaperson) %then %do; ';
|
||||||
|
put ' %if %length(&_metaperson)=0 %then %let user=&sysuserid; ';
|
||||||
|
put ' /* sometimes SAS will add @domain extension - remove for consistency */ ';
|
||||||
|
put ' /* but be sure to quote in case of usernames with commas */ ';
|
||||||
|
put ' %else %let user=%unquote(%scan(%quote(&_metaperson),1,@)); ';
|
||||||
|
put ' %end; ';
|
||||||
|
put ' %else %let user=&sysuserid; ';
|
||||||
|
put ' ';
|
||||||
|
put ' %quote(&user) ';
|
||||||
|
put ' ';
|
||||||
|
put '%mend mf_getuser; ';
|
||||||
|
put ' ';
|
||||||
|
put '%macro ms_webout(action,ds,dslabel=,fref=_webout,fmt=Y,missing=NULL ';
|
||||||
|
put ' ,showmeta=N ';
|
||||||
|
put '); ';
|
||||||
|
put '%global _webin_file_count _webin_fileref1 _webin_name1 _program _debug ';
|
||||||
|
put ' sasjs_tables; ';
|
||||||
|
put ' ';
|
||||||
|
put '%local i tempds; ';
|
||||||
|
put '%let action=%upcase(&action); ';
|
||||||
|
put ' ';
|
||||||
|
put '%if &action=FETCH %then %do; ';
|
||||||
|
put ' %if %str(&_debug) ge 131 %then %do; ';
|
||||||
|
put ' options mprint notes mprintnest; ';
|
||||||
|
put ' %end; ';
|
||||||
|
put ' %let _webin_file_count=%eval(&_webin_file_count+0); ';
|
||||||
|
put ' /* now read in the data */ ';
|
||||||
|
put ' %do i=1 %to &_webin_file_count; ';
|
||||||
|
put ' %if &_webin_file_count=1 %then %do; ';
|
||||||
|
put ' %let _webin_fileref1=&_webin_fileref; ';
|
||||||
|
put ' %let _webin_name1=&_webin_name; ';
|
||||||
|
put ' %end; ';
|
||||||
|
put ' data _null_; ';
|
||||||
|
put ' infile &&_webin_fileref&i termstr=crlf lrecl=32767; ';
|
||||||
|
put ' input; ';
|
||||||
|
put ' call symputx(''input_statement'',_infile_); ';
|
||||||
|
put ' putlog "&&_webin_name&i input statement: " _infile_; ';
|
||||||
|
put ' stop; ';
|
||||||
|
put ' data &&_webin_name&i; ';
|
||||||
|
put ' infile &&_webin_fileref&i firstobs=2 dsd termstr=crlf encoding=''utf-8'' ';
|
||||||
|
put ' lrecl=32767; ';
|
||||||
|
put ' input &input_statement; ';
|
||||||
|
put ' %if %str(&_debug) ge 131 %then %do; ';
|
||||||
|
put ' if _n_<20 then putlog _infile_; ';
|
||||||
|
put ' %end; ';
|
||||||
|
put ' run; ';
|
||||||
|
put ' %let sasjs_tables=&sasjs_tables &&_webin_name&i; ';
|
||||||
|
put ' %end; ';
|
||||||
|
put '%end; ';
|
||||||
|
put ' ';
|
||||||
|
put '%else %if &action=OPEN %then %do; ';
|
||||||
|
put ' /* fix encoding and ensure enough lrecl */ ';
|
||||||
|
put ' OPTIONS NOBOMFILE lrecl=32767; ';
|
||||||
|
put ' ';
|
||||||
|
put ' /* set the header */ ';
|
||||||
|
put ' %mfs_httpheader(Content-type,application/json) ';
|
||||||
|
put ' ';
|
||||||
|
put ' /* setup json. */ ';
|
||||||
|
put ' data _null_;file &fref encoding=''utf-8'' termstr=lf ; ';
|
||||||
|
put ' put ''{"SYSDATE" : "'' "&SYSDATE" ''"''; ';
|
||||||
|
put ' put '',"SYSTIME" : "'' "&SYSTIME" ''"''; ';
|
||||||
|
put ' run; ';
|
||||||
|
put ' ';
|
||||||
|
put '%end; ';
|
||||||
|
put ' ';
|
||||||
|
put '%else %if &action=ARR or &action=OBJ %then %do; ';
|
||||||
|
put ' %if "%substr(&sysver,1,1)"="4" or "%substr(&sysver,1,1)"="5" %then %do; ';
|
||||||
|
put ' /* functions in formats unsupported */ ';
|
||||||
|
put ' %put &sysmacroname: forcing missing back to NULL as feature not supported; ';
|
||||||
|
put ' %let missing=NULL; ';
|
||||||
|
put ' %end; ';
|
||||||
|
put ' %mp_jsonout(&action,&ds,dslabel=&dslabel,fmt=&fmt,jref=&fref ';
|
||||||
|
put ' ,engine=DATASTEP,missing=&missing,showmeta=&showmeta ';
|
||||||
|
put ' ) ';
|
||||||
|
put '%end; ';
|
||||||
|
put '%else %if &action=CLOSE %then %do; ';
|
||||||
|
put ' %if %str(&_debug) ge 131 %then %do; ';
|
||||||
|
put ' /* if debug mode, send back first 10 records of each work table also */ ';
|
||||||
|
put ' data;run;%let tempds=%scan(&syslast,2,.); ';
|
||||||
|
put ' ods output Members=&tempds; ';
|
||||||
|
put ' proc datasets library=WORK memtype=data; ';
|
||||||
|
put ' %local wtcnt;%let wtcnt=0; ';
|
||||||
|
put ' data _null_; ';
|
||||||
|
put ' set &tempds; ';
|
||||||
|
put ' if not (upcase(name) =:"DATA"); /* ignore temp datasets */ ';
|
||||||
|
put ' if not (upcase(name)=:"_DATA_"); ';
|
||||||
|
put ' i+1; ';
|
||||||
|
put ' call symputx(cats(''wt'',i),name,''l''); ';
|
||||||
|
put ' call symputx(''wtcnt'',i,''l''); ';
|
||||||
|
put ' data _null_; file &fref mod encoding=''utf-8'' termstr=lf; ';
|
||||||
|
put ' put ",""WORK"":{"; ';
|
||||||
|
put ' %do i=1 %to &wtcnt; ';
|
||||||
|
put ' %let wt=&&wt&i; ';
|
||||||
|
put ' data _null_; file &fref mod encoding=''utf-8'' termstr=lf; ';
|
||||||
|
put ' dsid=open("WORK.&wt",''is''); ';
|
||||||
|
put ' nlobs=attrn(dsid,''NLOBS''); ';
|
||||||
|
put ' nvars=attrn(dsid,''NVARS''); ';
|
||||||
|
put ' rc=close(dsid); ';
|
||||||
|
put ' if &i>1 then put '',''@; ';
|
||||||
|
put ' put " ""&wt"" : {"; ';
|
||||||
|
put ' put ''"nlobs":'' nlobs; ';
|
||||||
|
put ' put '',"nvars":'' nvars; ';
|
||||||
|
put ' %mp_jsonout(OBJ,&wt,jref=&fref,dslabel=first10rows,showmeta=Y,maxobs=10) ';
|
||||||
|
put ' data _null_; file &fref mod encoding=''utf-8'' termstr=lf; ';
|
||||||
|
put ' put "}"; ';
|
||||||
|
put ' %end; ';
|
||||||
|
put ' data _null_; file &fref mod encoding=''utf-8'' termstr=lf; ';
|
||||||
|
put ' put "}"; ';
|
||||||
|
put ' run; ';
|
||||||
|
put ' %end; ';
|
||||||
|
put ' /* close off json */ ';
|
||||||
|
put ' data _null_;file &fref mod encoding=''utf-8'' termstr=lf lrecl=32767; ';
|
||||||
|
put ' _PROGRAM=quote(trim(resolve(symget(''_PROGRAM'')))); ';
|
||||||
|
put ' put ",""SYSUSERID"" : ""&sysuserid"" "; ';
|
||||||
|
put ' put ",""MF_GETUSER"" : ""%mf_getuser()"" "; ';
|
||||||
|
put ' put ",""_DEBUG"" : ""&_debug"" "; ';
|
||||||
|
put ' put '',"_PROGRAM" : '' _PROGRAM ; ';
|
||||||
|
put ' put ",""SYSCC"" : ""&syscc"" "; ';
|
||||||
|
put ' put ",""SYSENCODING"" : ""&sysencoding"" "; ';
|
||||||
|
put ' syserrortext=cats(''"'',tranwrd(symget(''syserrortext''),''"'',''\"''),''"''); ';
|
||||||
|
put ' put '',"SYSERRORTEXT" : '' syserrortext; ';
|
||||||
|
put ' SYSHOSTINFOLONG=quote(trim(symget(''SYSHOSTINFOLONG''))); ';
|
||||||
|
put ' put '',"SYSHOSTINFOLONG" : '' SYSHOSTINFOLONG; ';
|
||||||
|
put ' put ",""SYSHOSTNAME"" : ""&syshostname"" "; ';
|
||||||
|
put ' put ",""SYSPROCESSID"" : ""&SYSPROCESSID"" "; ';
|
||||||
|
put ' put ",""SYSPROCESSMODE"" : ""&SYSPROCESSMODE"" "; ';
|
||||||
|
put ' length SYSPROCESSNAME $512; ';
|
||||||
|
put ' SYSPROCESSNAME=quote(urlencode(cats(SYSPROCESSNAME))); ';
|
||||||
|
put ' put ",""SYSPROCESSNAME"" : " SYSPROCESSNAME; ';
|
||||||
|
put ' put ",""SYSJOBID"" : ""&sysjobid"" "; ';
|
||||||
|
put ' put ",""SYSSCPL"" : ""&sysscpl"" "; ';
|
||||||
|
put ' put ",""SYSSITE"" : ""&syssite"" "; ';
|
||||||
|
put ' put ",""SYSTCPIPHOSTNAME"" : ""&SYSTCPIPHOSTNAME"" "; ';
|
||||||
|
put ' sysvlong=quote(trim(symget(''sysvlong''))); ';
|
||||||
|
put ' put '',"SYSVLONG" : '' sysvlong; ';
|
||||||
|
put ' syswarningtext=cats(''"'',tranwrd(symget(''syswarningtext''),''"'',''\"''),''"''); ';
|
||||||
|
put ' put '',"SYSWARNINGTEXT" : '' syswarningtext; ';
|
||||||
|
put ' put '',"END_DTTM" : "'' "%sysfunc(datetime(),E8601DT26.6)" ''" ''; ';
|
||||||
|
put ' length autoexec $512; ';
|
||||||
|
put ' autoexec=quote(urlencode(trim(getoption(''autoexec'')))); ';
|
||||||
|
put ' put '',"AUTOEXEC" : '' autoexec; ';
|
||||||
|
put ' length memsize $32; ';
|
||||||
|
put ' memsize="%sysfunc(INPUTN(%sysfunc(getoption(memsize)), best.),sizekmg.)"; ';
|
||||||
|
put ' memsize=quote(cats(memsize)); ';
|
||||||
|
put ' put '',"MEMSIZE" : '' memsize; ';
|
||||||
|
put ' put "}" @; ';
|
||||||
|
put ' run; ';
|
||||||
|
put '%end; ';
|
||||||
|
put ' ';
|
||||||
|
put '%mend ms_webout; ';
|
||||||
|
put ' ';
|
||||||
|
put '%macro mfs_httpheader(header_name ';
|
||||||
|
put ' ,header_value ';
|
||||||
|
put ')/*/STORE SOURCE*/; ';
|
||||||
|
put '%global sasjs_stpsrv_header_loc; ';
|
||||||
|
put '%local fref fid i; ';
|
||||||
|
put ' ';
|
||||||
|
put '%if %sysfunc(filename(fref,&sasjs_stpsrv_header_loc)) ne 0 %then %do; ';
|
||||||
|
put ' %put &=fref &=sasjs_stpsrv_header_loc; ';
|
||||||
|
put ' %put %str(ERR)OR: %sysfunc(sysmsg()); ';
|
||||||
|
put ' %return; ';
|
||||||
|
put '%end; ';
|
||||||
|
put ' ';
|
||||||
|
put '%let fid=%sysfunc(fopen(&fref,A)); ';
|
||||||
|
put ' ';
|
||||||
|
put '%if &fid=0 %then %do; ';
|
||||||
|
put ' %put %str(ERR)OR: %sysfunc(sysmsg()); ';
|
||||||
|
put ' %return; ';
|
||||||
|
put '%end; ';
|
||||||
|
put ' ';
|
||||||
|
put '%let rc=%sysfunc(fput(&fid,%str(&header_name): %str(&header_value))); ';
|
||||||
|
put '%let rc=%sysfunc(fwrite(&fid)); ';
|
||||||
|
put ' ';
|
||||||
|
put '%let rc=%sysfunc(fclose(&fid)); ';
|
||||||
|
put '%let rc=%sysfunc(filename(&fref)); ';
|
||||||
|
put ' ';
|
||||||
|
put '%mend mfs_httpheader; ';
|
||||||
|
/* WEBOUT END */
|
||||||
|
put '%macro webout(action,ds,dslabel=,fmt=,missing=NULL,showmeta=NO);';
|
||||||
|
put ' %ms_webout(&action,ds=&ds,dslabel=&dslabel,fmt=&fmt,missing=&missing';
|
||||||
|
put ' ,showmeta=&showmeta';
|
||||||
|
put ' )';
|
||||||
|
put '%mend;';
|
||||||
|
run;
|
||||||
|
|
||||||
|
/* add precode and code */
|
||||||
|
%local x fref freflist;
|
||||||
|
%let freflist=&precode &code ;
|
||||||
|
%do x=1 %to %sysfunc(countw(&freflist));
|
||||||
|
%let fref=%scan(&freflist,&x);
|
||||||
|
%put &sysmacroname: adding &fref;
|
||||||
|
data _null_;
|
||||||
|
file &sasjsref lrecl=3000 termstr=crlf mod;
|
||||||
|
infile &fref lrecl=3000;
|
||||||
|
input;
|
||||||
|
put _infile_;
|
||||||
|
run;
|
||||||
|
%end;
|
||||||
|
|
||||||
|
/* create the web service */
|
||||||
|
%ms_createfile(&path/&name..sas, inref=&sasjsref, mdebug=&mdebug)
|
||||||
|
|
||||||
|
%put ;%put ;%put ;%put ;%put ;%put ;
|
||||||
|
%put &sysmacroname: STP &name successfully created in &path;
|
||||||
|
%put ;%put ;%put ;
|
||||||
|
%put Check it out here:;
|
||||||
|
%put ;%put ;%put ;
|
||||||
|
%put &_sasjs_apiserverurl.&_sasjs_apipath?_PROGRAM=&path/&name;
|
||||||
|
%put ;%put ;%put ;%put ;%put ;%put ;
|
||||||
|
|
||||||
|
%mend ms_createwebservice;
|
||||||
@@ -17,6 +17,8 @@
|
|||||||
@param [in] driveloc The full path to the file in SASjs Drive
|
@param [in] driveloc The full path to the file in SASjs Drive
|
||||||
@param [in] mdebug= (0) Set to 1 to enable DEBUG messages
|
@param [in] mdebug= (0) Set to 1 to enable DEBUG messages
|
||||||
|
|
||||||
|
<h4> SAS Macros </h4>
|
||||||
|
@li mf_getuniquefileref.sas
|
||||||
|
|
||||||
**/
|
**/
|
||||||
|
|
||||||
@@ -24,12 +26,23 @@
|
|||||||
,mdebug=0
|
,mdebug=0
|
||||||
);
|
);
|
||||||
|
|
||||||
proc http method='DELETE'
|
%local headref;
|
||||||
|
%let headref=%mf_getuniquefileref();
|
||||||
|
|
||||||
|
data _null_;
|
||||||
|
file &headref lrecl=1000;
|
||||||
|
infile "&_sasjs_tokenfile" lrecl=1000;
|
||||||
|
input;
|
||||||
|
put _infile_;
|
||||||
|
run;
|
||||||
|
|
||||||
|
proc http method='DELETE' headerin=&headref
|
||||||
url="&_sasjs_apiserverurl/SASjsApi/drive/file?_filePath=&driveloc";
|
url="&_sasjs_apiserverurl/SASjsApi/drive/file?_filePath=&driveloc";
|
||||||
%if &mdebug=1 %then %do;
|
%if &mdebug=1 %then %do;
|
||||||
debug level=2;
|
debug level=2;
|
||||||
%end;
|
%end;
|
||||||
run;
|
run;
|
||||||
|
|
||||||
|
filename &headref clear;
|
||||||
|
|
||||||
%mend ms_deletefile;
|
%mend ms_deletefile;
|
||||||
|
|||||||
@@ -5,12 +5,15 @@
|
|||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
%ms_getfile(/some/stored/file.ext, outref=myfile)
|
%ms_getfile(/Public/app/dc/services/public/settings.sas, outref=myfile)
|
||||||
|
|
||||||
@param [in] driveloc The full path to the file in SASjs Drive
|
@param [in] driveloc The full path to the file in SASjs Drive
|
||||||
@param [out] outref= (msgetfil) The fileref to contain the file.
|
@param [out] outref= (msgetfil) The fileref to contain the file.
|
||||||
@param [in] mdebug= (0) Set to 1 to enable DEBUG messages
|
@param [in] mdebug= (0) Set to 1 to enable DEBUG messages
|
||||||
|
|
||||||
|
<h4> SAS Macros </h4>
|
||||||
|
@li mf_getuniquefileref.sas
|
||||||
|
@li mf_getuniquename.sas
|
||||||
|
|
||||||
**/
|
**/
|
||||||
|
|
||||||
@@ -19,14 +22,29 @@
|
|||||||
,mdebug=0
|
,mdebug=0
|
||||||
);
|
);
|
||||||
|
|
||||||
filename &outref temp;
|
/* use the recfm in a separate fileref to avoid issues with subsequent reads */
|
||||||
|
%local binaryfref floc headref;
|
||||||
|
%let binaryfref=%mf_getuniquefileref();
|
||||||
|
%let headref=%mf_getuniquefileref();
|
||||||
|
%let floc=%sysfunc(pathname(work))/%mf_getuniquename().txt;
|
||||||
|
filename &outref "&floc" lrecl=32767;
|
||||||
|
filename &binaryfref "&floc" recfm=n;
|
||||||
|
|
||||||
proc http method='GET' out=&outref
|
data _null_;
|
||||||
|
file &headref lrecl=1000;
|
||||||
|
infile "&_sasjs_tokenfile" lrecl=1000;
|
||||||
|
input;
|
||||||
|
put _infile_;
|
||||||
|
run;
|
||||||
|
|
||||||
|
proc http method='GET' out=&binaryfref headerin=&headref
|
||||||
url="&_sasjs_apiserverurl/SASjsApi/drive/file?_filePath=&driveloc";
|
url="&_sasjs_apiserverurl/SASjsApi/drive/file?_filePath=&driveloc";
|
||||||
%if &mdebug=1 %then %do;
|
%if &mdebug=1 %then %do;
|
||||||
debug level=2;
|
debug level=2;
|
||||||
%end;
|
%end;
|
||||||
run;
|
run;
|
||||||
|
|
||||||
|
filename &binaryfref clear;
|
||||||
|
filename &headref clear;
|
||||||
|
|
||||||
%mend ms_getfile;
|
%mend ms_getfile;
|
||||||
147
server/ms_getgroups.sas
Normal file
147
server/ms_getgroups.sas
Normal file
@@ -0,0 +1,147 @@
|
|||||||
|
/**
|
||||||
|
@file
|
||||||
|
@brief Fetches the list of groups from SASjs Server
|
||||||
|
@details Fetches the list of groups from SASjs Server and writes them to an
|
||||||
|
output dataset. Provide a username to filter for the groups for a particular
|
||||||
|
user.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
%ms_getgroups(outds=userlist)
|
||||||
|
|
||||||
|
With filter on username:
|
||||||
|
|
||||||
|
%ms_getgroups(outds=userlist, user=James)
|
||||||
|
|
||||||
|
With filter on userid:
|
||||||
|
|
||||||
|
%ms_getgroups(outds=userlist, uid=1)
|
||||||
|
|
||||||
|
@param [in] mdebug= (0) Set to 1 to enable DEBUG messages
|
||||||
|
@param [in] user= (0) Provide the username on which to filter
|
||||||
|
@param [in] uid= (0) Provide the userid on which to filter
|
||||||
|
@param [out] outds= (work.ms_getgroups) This output dataset will contain the
|
||||||
|
list of groups. Format:
|
||||||
|
|NAME:$32.|DESCRIPTION:$64.|GROUPID:best.|
|
||||||
|
|---|---|---|
|
||||||
|
|`SomeGroup `|`A group `|`1`|
|
||||||
|
|`Another Group`|`this is a different group`|`2`|
|
||||||
|
|`admin`|`Administrators `|`3`|
|
||||||
|
|
||||||
|
|
||||||
|
<h4> SAS Macros </h4>
|
||||||
|
@li mf_getuniquefileref.sas
|
||||||
|
@li mf_getuniquelibref.sas
|
||||||
|
@li mp_abort.sas
|
||||||
|
|
||||||
|
<h4> Related Files </h4>
|
||||||
|
@li ms_creategroup.sas
|
||||||
|
@li ms_getgroups.test.sas
|
||||||
|
|
||||||
|
**/
|
||||||
|
|
||||||
|
%macro ms_getgroups(
|
||||||
|
user=0,
|
||||||
|
uid=0,
|
||||||
|
outds=work.ms_getgroups,
|
||||||
|
mdebug=0
|
||||||
|
);
|
||||||
|
|
||||||
|
%mp_abort(
|
||||||
|
iftrue=(&syscc ne 0)
|
||||||
|
,mac=ms_getgroups.sas
|
||||||
|
,msg=%str(syscc=&syscc on macro entry)
|
||||||
|
)
|
||||||
|
|
||||||
|
%local fref0 fref1 libref optval rc msg url;
|
||||||
|
|
||||||
|
%if %sysget(MODE)=desktop %then %do;
|
||||||
|
/* groups api does not exist in desktop mode */
|
||||||
|
data &outds;
|
||||||
|
length NAME $32 DESCRIPTION $64. GROUPID 8;
|
||||||
|
name="&sysuserid";
|
||||||
|
description="&sysuserid (group - desktop mode)";
|
||||||
|
groupid=1;
|
||||||
|
output;
|
||||||
|
stop;
|
||||||
|
run;
|
||||||
|
%return;
|
||||||
|
%end;
|
||||||
|
|
||||||
|
%let fref0=%mf_getuniquefileref();
|
||||||
|
%let fref1=%mf_getuniquefileref();
|
||||||
|
%let libref=%mf_getuniquelibref();
|
||||||
|
|
||||||
|
/* avoid sending bom marker to API */
|
||||||
|
%let optval=%sysfunc(getoption(bomfile));
|
||||||
|
options nobomfile;
|
||||||
|
|
||||||
|
data _null_;
|
||||||
|
file &fref0 lrecl=1000;
|
||||||
|
infile "&_sasjs_tokenfile" lrecl=1000;
|
||||||
|
input;
|
||||||
|
if _n_=1 then put "accept: application/json";
|
||||||
|
put _infile_;
|
||||||
|
run;
|
||||||
|
|
||||||
|
%if &mdebug=1 %then %do;
|
||||||
|
data _null_;
|
||||||
|
infile &fref0;
|
||||||
|
input;
|
||||||
|
put _infile_;
|
||||||
|
run;
|
||||||
|
%end;
|
||||||
|
|
||||||
|
%if "&user" ne "0" %then %let url=/SASjsApi/user/by/username/&user;
|
||||||
|
%else %if "&uid" ne "0" %then %let url=/SASjsApi/user/&uid;
|
||||||
|
%else %let url=/SASjsApi/group;
|
||||||
|
|
||||||
|
|
||||||
|
proc http method='GET' headerin=&fref0 out=&fref1
|
||||||
|
url="&_sasjs_apiserverurl.&url";
|
||||||
|
%if &mdebug=1 %then %do;
|
||||||
|
debug level=1;
|
||||||
|
%end;
|
||||||
|
run;
|
||||||
|
|
||||||
|
%mp_abort(
|
||||||
|
iftrue=(&syscc ne 0)
|
||||||
|
,mac=ms_getgroups.sas
|
||||||
|
,msg=%str(Issue submitting GET query to SASjsApi)
|
||||||
|
)
|
||||||
|
|
||||||
|
libname &libref JSON fileref=&fref1;
|
||||||
|
|
||||||
|
%if "&user"="0" and "&uid"="0" %then %do;
|
||||||
|
data &outds;
|
||||||
|
length NAME $32 DESCRIPTION $64. GROUPID 8;
|
||||||
|
if _n_=1 then call missing(of _all_);
|
||||||
|
set &libref..root;
|
||||||
|
drop ordinal_root;
|
||||||
|
run;
|
||||||
|
%end;
|
||||||
|
%else %do;
|
||||||
|
data &outds;
|
||||||
|
length NAME $32 DESCRIPTION $64. GROUPID 8;
|
||||||
|
if _n_=1 then call missing(of _all_);
|
||||||
|
set &libref..groups;
|
||||||
|
drop ordinal_:;
|
||||||
|
run;
|
||||||
|
%end;
|
||||||
|
|
||||||
|
%mp_abort(
|
||||||
|
iftrue=(&syscc ne 0)
|
||||||
|
,mac=ms_getgroups.sas
|
||||||
|
,msg=%str(Issue reading response JSON)
|
||||||
|
)
|
||||||
|
|
||||||
|
/* reset options */
|
||||||
|
options &optval;
|
||||||
|
|
||||||
|
%if &mdebug=1 %then %do;
|
||||||
|
filename &fref0 clear;
|
||||||
|
filename &fref1 clear;
|
||||||
|
libname &libref clear;
|
||||||
|
%end;
|
||||||
|
|
||||||
|
%mend ms_getgroups;
|
||||||
148
server/ms_getusers.sas
Normal file
148
server/ms_getusers.sas
Normal file
@@ -0,0 +1,148 @@
|
|||||||
|
/**
|
||||||
|
@file
|
||||||
|
@brief Fetches the list of users from SASjs Server
|
||||||
|
@details Fetches the list of users from SASjs Server and writes them to an
|
||||||
|
output dataset. Can also be filtered, for a particular group.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
%ms_getusers(outds=userlist)
|
||||||
|
|
||||||
|
Filtering for a group by group name:
|
||||||
|
|
||||||
|
%ms_getusers(outds=work.groupmembers, group=GROUPNAME)
|
||||||
|
|
||||||
|
Filtering for a group by group id:
|
||||||
|
|
||||||
|
%ms_getusers(outds=work.groupmembers, gid=1)
|
||||||
|
|
||||||
|
@param [in] mdebug= (0) Set to 1 to enable DEBUG messages
|
||||||
|
@param [in] group= (0) Set to a group name to filter members for that group
|
||||||
|
@param [in] gid= (0) Set to a group id to filter members for that group
|
||||||
|
@param [out] outds= (work.ms_getusers) This output dataset will contain the
|
||||||
|
list of user accounts. Format:
|
||||||
|
|DISPLAYNAME:$60.|USERNAME:$30.|ID:best.|
|
||||||
|
|---|---|---|
|
||||||
|
|`Super Admin `|`secretuser `|`1`|
|
||||||
|
|`Sabir Hassan`|`sabir`|`2`|
|
||||||
|
|`Mihajlo Medjedovic `|`mihajlo `|`3`|
|
||||||
|
|`Ivor Townsend `|`ivor `|`4`|
|
||||||
|
|`New User `|`newuser `|`5`|
|
||||||
|
|
||||||
|
|
||||||
|
<h4> SAS Macros </h4>
|
||||||
|
@li mf_getuniquefileref.sas
|
||||||
|
@li mf_getuniquelibref.sas
|
||||||
|
@li mp_abort.sas
|
||||||
|
|
||||||
|
<h4> Related Files </h4>
|
||||||
|
@li ms_createuser.sas
|
||||||
|
@li ms_getgroups.sas
|
||||||
|
@li ms_getusers.test.sas
|
||||||
|
|
||||||
|
**/
|
||||||
|
|
||||||
|
%macro ms_getusers(
|
||||||
|
outds=work.ms_getusers,
|
||||||
|
group=0,
|
||||||
|
gid=0,
|
||||||
|
mdebug=0
|
||||||
|
);
|
||||||
|
|
||||||
|
%mp_abort(
|
||||||
|
iftrue=(&syscc ne 0)
|
||||||
|
,mac=ms_getusers.sas
|
||||||
|
,msg=%str(syscc=&syscc on macro entry)
|
||||||
|
)
|
||||||
|
|
||||||
|
%local fref0 fref1 libref optval rc msg url;
|
||||||
|
%let fref0=%mf_getuniquefileref();
|
||||||
|
%let fref1=%mf_getuniquefileref();
|
||||||
|
%let libref=%mf_getuniquelibref();
|
||||||
|
|
||||||
|
%if %sysget(MODE)=desktop %then %do;
|
||||||
|
/* users api does not exist in desktop mode */
|
||||||
|
data &outds;
|
||||||
|
length DISPLAYNAME $60 USERNAME:$30 ID 8;
|
||||||
|
USERNAME="&sysuserid";
|
||||||
|
DISPLAYNAME="&sysuserid (desktop mode)";
|
||||||
|
ID=1;
|
||||||
|
output;
|
||||||
|
stop;
|
||||||
|
run;
|
||||||
|
%return;
|
||||||
|
%end;
|
||||||
|
|
||||||
|
/* avoid sending bom marker to API */
|
||||||
|
%let optval=%sysfunc(getoption(bomfile));
|
||||||
|
options nobomfile;
|
||||||
|
|
||||||
|
data _null_;
|
||||||
|
file &fref0 lrecl=1000;
|
||||||
|
infile "&_sasjs_tokenfile" lrecl=1000;
|
||||||
|
input;
|
||||||
|
if _n_=1 then put "accept: application/json";
|
||||||
|
put _infile_;
|
||||||
|
run;
|
||||||
|
|
||||||
|
%if &mdebug=1 %then %do;
|
||||||
|
data _null_;
|
||||||
|
infile &fref0;
|
||||||
|
input;
|
||||||
|
put _infile_;
|
||||||
|
run;
|
||||||
|
%end;
|
||||||
|
|
||||||
|
%if "&group" ne "0" %then %let url=/SASjsApi/group/by/groupname/&group;
|
||||||
|
%else %if "&gid" ne "0" %then %let url=/SASjsApi/group/&gid;
|
||||||
|
%else %let url=/SASjsApi/user;
|
||||||
|
|
||||||
|
proc http method='GET' headerin=&fref0 out=&fref1
|
||||||
|
url="&_sasjs_apiserverurl.&url";
|
||||||
|
%if &mdebug=1 %then %do;
|
||||||
|
debug level=1;
|
||||||
|
%end;
|
||||||
|
run;
|
||||||
|
|
||||||
|
|
||||||
|
%mp_abort(
|
||||||
|
iftrue=(&syscc ne 0)
|
||||||
|
,mac=ms_getusers.sas
|
||||||
|
,msg=%str(Issue submitting API query)
|
||||||
|
)
|
||||||
|
|
||||||
|
libname &libref JSON fileref=&fref1;
|
||||||
|
|
||||||
|
%if "&group"="0" and "&gid"="0" %then %do;
|
||||||
|
data &outds;
|
||||||
|
length DISPLAYNAME $60 USERNAME:$30 ID 8;
|
||||||
|
if nobs=0 then call missing(of _all_);
|
||||||
|
set &libref..root nobs=nobs;
|
||||||
|
drop ordinal_root;
|
||||||
|
run;
|
||||||
|
%end;
|
||||||
|
%else %do;
|
||||||
|
data &outds;
|
||||||
|
length DISPLAYNAME $60 USERNAME:$30 ID 8;
|
||||||
|
if nobs=0 then call missing(of _all_);
|
||||||
|
set &libref..users nobs=nobs;
|
||||||
|
drop ordinal_root ordinal_users;
|
||||||
|
run;
|
||||||
|
%end;
|
||||||
|
|
||||||
|
%mp_abort(
|
||||||
|
iftrue=(&syscc ne 0)
|
||||||
|
,mac=ms_getusers.sas
|
||||||
|
,msg=%str(Issue reading response JSON)
|
||||||
|
)
|
||||||
|
|
||||||
|
/* reset options */
|
||||||
|
options &optval;
|
||||||
|
|
||||||
|
%if &mdebug=1 %then %do;
|
||||||
|
filename &fref0 clear;
|
||||||
|
filename &fref1 clear;
|
||||||
|
libname &libref clear;
|
||||||
|
%end;
|
||||||
|
|
||||||
|
%mend ms_getusers;
|
||||||
@@ -15,48 +15,155 @@
|
|||||||
parameter)
|
parameter)
|
||||||
@param [in] debug= (131) The value to supply to the _debug URL 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 [in] mdebug= (0) Set to 1 to enable DEBUG messages
|
||||||
|
@param [in] inputparams=(_null_) A dataset containing name/value pairs in the
|
||||||
|
following format:
|
||||||
|
|name:$32|value:$10000|
|
||||||
|
|---|---|
|
||||||
|
|stpmacname|some value|
|
||||||
|
|mustbevalidname|can be anything, oops, %abort!!|
|
||||||
|
@param [in] inputfiles= (_null_) A dataset containing fileref/name/filename in
|
||||||
|
the following format:
|
||||||
|
|fileref:$8|name:$32|filename:$256|
|
||||||
|
|---|---|--|
|
||||||
|
|someref|some_name|some_filename.xls|
|
||||||
|
|fref2|another_file|zyx_v2.csv|
|
||||||
|
|
||||||
@param [out] outref= (outweb) The output fileref to contain the response JSON
|
@param [out] outref= (outweb) The output fileref to contain the response JSON
|
||||||
(will be created using temp engine)
|
(will be created using temp engine)
|
||||||
|
@param [out] outlogds= (_null_) Set to the name of a dataset to contain the
|
||||||
|
log. Table format:
|
||||||
|
|line:$2000|
|
||||||
|
|---|
|
||||||
|
|log line 1|
|
||||||
|
|log line 2|
|
||||||
|
|
||||||
<h4> SAS Macros </h4>
|
<h4> SAS Macros </h4>
|
||||||
@li mf_getuniquefileref.sas
|
@li mf_getuniquefileref.sas
|
||||||
|
@li mf_getuniquelibref.sas
|
||||||
@li mp_abort.sas
|
@li mp_abort.sas
|
||||||
|
|
||||||
**/
|
**/
|
||||||
|
|
||||||
%macro ms_runstp(pgm
|
%macro ms_runstp(pgm
|
||||||
,debug=131
|
,debug=131
|
||||||
|
,inputparams=_null_
|
||||||
|
,inputfiles=_null_
|
||||||
,outref=outweb
|
,outref=outweb
|
||||||
|
,outlogds=_null_
|
||||||
,mdebug=0
|
,mdebug=0
|
||||||
);
|
);
|
||||||
%local dbg fname1;
|
%local dbg mainref authref boundary;
|
||||||
|
%let mainref=%mf_getuniquefileref();
|
||||||
|
%let authref=%mf_getuniquefileref();
|
||||||
|
%let boundary=%mf_getuniquename();
|
||||||
|
%if &inputparams=0 %then %let inputparams=_null_;
|
||||||
|
|
||||||
%if &mdebug=1 %then %do;
|
%if &mdebug=1 %then %do;
|
||||||
%put &sysmacroname entry vars:;
|
%put &sysmacroname entry vars:;
|
||||||
%put _local_;
|
%put _local_;
|
||||||
%end;
|
%end;
|
||||||
%else %let dbg=*;
|
%else %let dbg=*;
|
||||||
%let fname1=%mf_getuniquefileref();
|
|
||||||
|
|
||||||
%mp_abort(iftrue=("&pgm"="")
|
%mp_abort(iftrue=("&pgm"="")
|
||||||
,mac=&sysmacroname
|
,mac=&sysmacroname
|
||||||
,msg=%str(Program not provided)
|
,msg=%str(Program not provided)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
/* avoid sending bom marker to API */
|
||||||
|
%local optval;
|
||||||
|
%let optval=%sysfunc(getoption(bomfile));
|
||||||
|
options nobomfile;
|
||||||
|
|
||||||
|
/* add params */
|
||||||
data _null_;
|
data _null_;
|
||||||
file &fname1;
|
file &mainref termstr=crlf lrecl=32767 mod;
|
||||||
infile "&_sasjs_tokenfile";
|
length line $1000 name $32 value $32767;
|
||||||
input;
|
if _n_=1 then call missing(of _all_);
|
||||||
put 'Authorization: Bearer' _infile_;
|
set &inputparams;
|
||||||
|
put "--&boundary";
|
||||||
|
line=cats('Content-Disposition: form-data; name="',name,'"');
|
||||||
|
put line;
|
||||||
|
put ;
|
||||||
|
put value;
|
||||||
run;
|
run;
|
||||||
|
|
||||||
filename &outref temp;
|
/* parse input file list */
|
||||||
|
%local webcount;
|
||||||
|
%let webcount=0;
|
||||||
|
data _null_;
|
||||||
|
set &inputfiles end=last;
|
||||||
|
length fileref $8 name $32 filename $256;
|
||||||
|
call symputx(cats('webref',_n_),fileref,'l');
|
||||||
|
call symputx(cats('webname',_n_),name,'l');
|
||||||
|
call symputx(cats('webfilename',_n_),filename,'l');
|
||||||
|
if last then do;
|
||||||
|
call symputx('webcount',_n_);
|
||||||
|
call missing(of _all_);
|
||||||
|
end;
|
||||||
|
run;
|
||||||
|
|
||||||
|
/* write out the input files */
|
||||||
|
%local i;
|
||||||
|
%do i=1 %to &webcount;
|
||||||
|
data _null_;
|
||||||
|
file &mainref termstr=crlf lrecl=32767 mod;
|
||||||
|
infile &&webref&i lrecl=32767;
|
||||||
|
if _n_ = 1 then do;
|
||||||
|
length line $32767;
|
||||||
|
line=cats(
|
||||||
|
'Content-Disposition: form-data; name="'
|
||||||
|
,"&&webname&i"
|
||||||
|
,'"; filename="'
|
||||||
|
,"&&webfilename&i"
|
||||||
|
,'"'
|
||||||
|
);
|
||||||
|
put "--&boundary";
|
||||||
|
put line;
|
||||||
|
put "Content-Type: text/plain";
|
||||||
|
put ;
|
||||||
|
end;
|
||||||
|
input;
|
||||||
|
put _infile_; /* add the actual file to be sent */
|
||||||
|
run;
|
||||||
|
%end;
|
||||||
|
|
||||||
|
data _null_;
|
||||||
|
file &mainref termstr=crlf mod;
|
||||||
|
put "--&boundary--";
|
||||||
|
run;
|
||||||
|
|
||||||
|
data _null_;
|
||||||
|
file &authref lrecl=1000;
|
||||||
|
infile "&_sasjs_tokenfile" lrecl=1000;
|
||||||
|
input;
|
||||||
|
if _n_=1 then put "Content-Type: multipart/form-data; boundary=&boundary";
|
||||||
|
put _infile_;
|
||||||
|
run;
|
||||||
|
|
||||||
|
%if &mdebug=1 %then %do;
|
||||||
|
data _null_;
|
||||||
|
infile &authref;
|
||||||
|
input;
|
||||||
|
put _infile_;
|
||||||
|
data _null_;
|
||||||
|
infile &mainref;
|
||||||
|
input;
|
||||||
|
put _infile_;
|
||||||
|
run;
|
||||||
|
%end;
|
||||||
|
|
||||||
|
filename &outref temp lrecl=32767;
|
||||||
/* prepare request*/
|
/* prepare request*/
|
||||||
proc http method='POST' headerin=&fname1 out=&outref
|
proc http method='POST' headerin=&authref in=&mainref out=&outref
|
||||||
url="&_sasjs_apiserverurl.&_sasjs_apipath?_program=&pgm%str(&)_debug=131";
|
url="&_sasjs_apiserverurl.&_sasjs_apipath?_program=&pgm%str(&)_debug=131";
|
||||||
|
%if &mdebug=1 %then %do;
|
||||||
|
debug level=2;
|
||||||
|
%end;
|
||||||
run;
|
run;
|
||||||
%if (&SYS_PROCHTTP_STATUS_CODE ne 200 and &SYS_PROCHTTP_STATUS_CODE ne 201)
|
%if (&SYS_PROCHTTP_STATUS_CODE ne 200 and &SYS_PROCHTTP_STATUS_CODE ne 201)
|
||||||
or &mdebug=1 %then %do;
|
or &mdebug=1
|
||||||
|
%then %do;
|
||||||
data _null_;infile &outref;input;putlog _infile_;run;
|
data _null_;infile &outref;input;putlog _infile_;run;
|
||||||
%end;
|
%end;
|
||||||
%mp_abort(
|
%mp_abort(
|
||||||
@@ -65,6 +172,20 @@ run;
|
|||||||
,msg=%str(&SYS_PROCHTTP_STATUS_CODE &SYS_PROCHTTP_STATUS_PHRASE)
|
,msg=%str(&SYS_PROCHTTP_STATUS_CODE &SYS_PROCHTTP_STATUS_PHRASE)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
/* reset options */
|
||||||
|
options &optval;
|
||||||
|
|
||||||
|
%if &outlogds ne _null_ or &mdebug=1 %then %do;
|
||||||
|
%local dumplib;
|
||||||
|
%let dumplib=%mf_getuniquelibref();
|
||||||
|
libname &dumplib json fileref=&outref;
|
||||||
|
data &outlogds;
|
||||||
|
set &dumplib..log;
|
||||||
|
%if &mdebug=1 %then %do;
|
||||||
|
putlog line=;
|
||||||
|
%end;
|
||||||
|
run;
|
||||||
|
%end;
|
||||||
|
|
||||||
%if &mdebug=1 %then %do;
|
%if &mdebug=1 %then %do;
|
||||||
%put &sysmacroname exit vars:;
|
%put &sysmacroname exit vars:;
|
||||||
@@ -72,6 +193,7 @@ run;
|
|||||||
%end;
|
%end;
|
||||||
%else %do;
|
%else %do;
|
||||||
/* clear refs */
|
/* clear refs */
|
||||||
filename &fname1 clear;
|
filename &authref;
|
||||||
|
filename &mainref;
|
||||||
%end;
|
%end;
|
||||||
%mend ms_runstp;
|
%mend ms_runstp;
|
||||||
|
|||||||
154
server/ms_testservice.sas
Normal file
154
server/ms_testservice.sas
Normal file
@@ -0,0 +1,154 @@
|
|||||||
|
/**
|
||||||
|
@file
|
||||||
|
@brief Will execute a SASjs web service on SASjs Server
|
||||||
|
@details Prepares the input files and retrieves the resulting datasets from
|
||||||
|
the response JSON.
|
||||||
|
|
||||||
|
@param [in] program The Stored Program endpoint to test
|
||||||
|
@param [in] inputfiles=(0) A list of space seperated fileref:filename pairs as
|
||||||
|
follows:
|
||||||
|
inputfiles=inref:filename inref2:filename2
|
||||||
|
@param [in] inputdatasets= (0) All datasets in this space seperated list are
|
||||||
|
converted into SASJS-formatted CSVs (see mp_ds2csv.sas) files and added to
|
||||||
|
the list of `inputfiles` for ingestion. The dataset will be sent with the
|
||||||
|
same name (no need for a colon modifier).
|
||||||
|
@param [in] inputparams=(0) A dataset containing name/value pairs in the
|
||||||
|
following format:
|
||||||
|
|name:$32|value:$1000|
|
||||||
|
|---|---|
|
||||||
|
|stpmacname|some value|
|
||||||
|
|mustbevalidname|can be anything, oops, %abort!!|
|
||||||
|
|
||||||
|
@param [in] debug= (131) Provide the _debug value to pass to the STP
|
||||||
|
@param [in] mdebug= (0) Set to 1 to provide macro debugging (this macro)
|
||||||
|
@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
|
||||||
|
response.
|
||||||
|
@param [out] outlogds= (_null_) Set to the name of a dataset to contain the
|
||||||
|
log. Table format:
|
||||||
|
|line:$2000|
|
||||||
|
|---|
|
||||||
|
|log line 1|
|
||||||
|
|log line 2|
|
||||||
|
|
||||||
|
<h4> SAS Macros </h4>
|
||||||
|
@li mf_getuniquefileref.sas
|
||||||
|
@li mf_getuniquename.sas
|
||||||
|
@li mp_abort.sas
|
||||||
|
@li mp_binarycopy.sas
|
||||||
|
@li mp_chop.sas
|
||||||
|
@li mp_ds2csv.sas
|
||||||
|
@li ms_runstp.sas
|
||||||
|
|
||||||
|
<h4> Related Programs </h4>
|
||||||
|
@li mp_testservice.test.sas
|
||||||
|
|
||||||
|
@version 9.4
|
||||||
|
@author Allan Bowe
|
||||||
|
|
||||||
|
**/
|
||||||
|
|
||||||
|
%macro ms_testservice(program,
|
||||||
|
inputfiles=0,
|
||||||
|
inputdatasets=0,
|
||||||
|
inputparams=0,
|
||||||
|
debug=0,
|
||||||
|
mdebug=0,
|
||||||
|
outlib=0,
|
||||||
|
outref=0,
|
||||||
|
outlogds=_null_
|
||||||
|
)/*/STORE SOURCE*/;
|
||||||
|
%local dbg i var ds1 fref1 chopout1 chopout2;
|
||||||
|
%if &mdebug=1 %then %do;
|
||||||
|
%put &sysmacroname entry vars:;
|
||||||
|
%put _local_;
|
||||||
|
%end;
|
||||||
|
%else %let dbg=*;
|
||||||
|
|
||||||
|
/* convert inputdatasets to filerefs */
|
||||||
|
%if "&inputdatasets" ne "0" %then %do;
|
||||||
|
%if %quote(&inputfiles)=0 %then %let inputfiles=;
|
||||||
|
%do i=1 %to %sysfunc(countw(&inputdatasets,%str( )));
|
||||||
|
%let var=%scan(&inputdatasets,&i,%str( ));
|
||||||
|
%local dsref&i;
|
||||||
|
%let dsref&i=%mf_getuniquefileref();
|
||||||
|
%mp_ds2csv(&var,outref=&&dsref&i,headerformat=SASJS)
|
||||||
|
%let inputfiles=&inputfiles &&dsref&i:%scan(&var,-1,.);
|
||||||
|
%end;
|
||||||
|
%end;
|
||||||
|
|
||||||
|
/* parse the filerefs - convert to a dataset */
|
||||||
|
%let ds1=%mf_getuniquename();
|
||||||
|
data &ds1;
|
||||||
|
length fileref $8 name $32 filename $256 var $300;
|
||||||
|
if "&inputfiles" ne "0" then do;
|
||||||
|
webcount=countw("&inputfiles");
|
||||||
|
do i=1 to webcount;
|
||||||
|
var=scan("&inputfiles",i,' ');
|
||||||
|
fileref=scan(var,1,':');
|
||||||
|
name=scan(var,2,':');
|
||||||
|
filename=cats(name,'.csv');
|
||||||
|
output;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
run;
|
||||||
|
|
||||||
|
|
||||||
|
/* execute the STP */
|
||||||
|
%let fref1=%mf_getuniquefileref();
|
||||||
|
|
||||||
|
%ms_runstp(&program
|
||||||
|
,debug=&debug
|
||||||
|
,inputparams=&inputparams
|
||||||
|
,inputfiles=&ds1
|
||||||
|
,outref=&fref1
|
||||||
|
,mdebug=&mdebug
|
||||||
|
,outlogds=&outlogds
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
/* SASjs services have the _webout embedded in wrapper JSON */
|
||||||
|
/* Files can also be very large - so use a dedicated macro to chop it out */
|
||||||
|
%local matchstr1 matchstr2 ;
|
||||||
|
%let matchstr1={"status":"success","_webout":{;
|
||||||
|
%let matchstr2=},"log":[{;
|
||||||
|
%let chopout1=%sysfunc(pathname(work))/%mf_getuniquename(prefix=chop1);
|
||||||
|
%let chopout2=%sysfunc(pathname(work))/%mf_getuniquename(prefix=chop2);
|
||||||
|
|
||||||
|
%mp_chop("%sysfunc(pathname(&fref1,F))"
|
||||||
|
,matchvar=matchstr1
|
||||||
|
,keep=LAST
|
||||||
|
,matchpoint=END
|
||||||
|
,offset=-1
|
||||||
|
,outfile="&chopout1"
|
||||||
|
,mdebug=&mdebug
|
||||||
|
)
|
||||||
|
|
||||||
|
%mp_chop("&chopout1"
|
||||||
|
,matchvar=matchstr2
|
||||||
|
,keep=FIRST
|
||||||
|
,matchpoint=START
|
||||||
|
,offset=1
|
||||||
|
,outfile="&chopout2"
|
||||||
|
,mdebug=&mdebug
|
||||||
|
)
|
||||||
|
|
||||||
|
%if &outlib ne 0 %then %do;
|
||||||
|
libname &outlib json "&chopout2";
|
||||||
|
%end;
|
||||||
|
%if &outref ne 0 %then %do;
|
||||||
|
filename &outref "&chopout2";
|
||||||
|
%end;
|
||||||
|
|
||||||
|
%if &mdebug=0 %then %do;
|
||||||
|
filename &webref clear;
|
||||||
|
filename &fref1 clear;
|
||||||
|
filename &fref2 clear;
|
||||||
|
%end;
|
||||||
|
%else %do;
|
||||||
|
%put &sysmacroname exit vars:;
|
||||||
|
%put _local_;
|
||||||
|
%end;
|
||||||
|
|
||||||
|
%mend ms_testservice;
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
@file
|
@file
|
||||||
@brief Send data to/from @sasjs/server
|
@brief Send data to/from sasjs/server
|
||||||
@details This macro should be added to the start of each web service,
|
@details This macro should be added to the start of each web service,
|
||||||
**immediately** followed by a call to:
|
**immediately** followed by a call to:
|
||||||
|
|
||||||
@@ -27,7 +27,7 @@
|
|||||||
@param [out] fref= (_webout) The fileref to which to write the JSON
|
@param [out] fref= (_webout) The fileref to which to write the JSON
|
||||||
@param [in] missing= (NULL) Special numeric missing values can be sent as NULL
|
@param [in] missing= (NULL) Special numeric missing values can be sent as NULL
|
||||||
(eg `null`) or as STRING values (eg `".a"` or `".b"`)
|
(eg `null`) or as STRING values (eg `".a"` or `".b"`)
|
||||||
@param [in] showmeta= (NO) Set to YES to output metadata alongside each table,
|
@param [in] showmeta= (N) Set to Y to output metadata alongside each table,
|
||||||
such as the column formats and types. The metadata is contained inside an
|
such as the column formats and types. The metadata is contained inside an
|
||||||
object with the same name as the table but prefixed with a dollar sign - ie,
|
object with the same name as the table but prefixed with a dollar sign - ie,
|
||||||
`,"$tablename":{"formats":{"col1":"$CHAR1"},"types":{"COL1":"C"}}`
|
`,"$tablename":{"formats":{"col1":"$CHAR1"},"types":{"COL1":"C"}}`
|
||||||
@@ -47,7 +47,7 @@
|
|||||||
**/
|
**/
|
||||||
|
|
||||||
%macro ms_webout(action,ds,dslabel=,fref=_webout,fmt=Y,missing=NULL
|
%macro ms_webout(action,ds,dslabel=,fref=_webout,fmt=Y,missing=NULL
|
||||||
,showmeta=NO
|
,showmeta=N
|
||||||
);
|
);
|
||||||
%global _webin_file_count _webin_fileref1 _webin_name1 _program _debug
|
%global _webin_file_count _webin_fileref1 _webin_name1 _program _debug
|
||||||
sasjs_tables;
|
sasjs_tables;
|
||||||
@@ -67,13 +67,14 @@
|
|||||||
%let _webin_name1=&_webin_name;
|
%let _webin_name1=&_webin_name;
|
||||||
%end;
|
%end;
|
||||||
data _null_;
|
data _null_;
|
||||||
infile &&_webin_fileref&i termstr=crlf;
|
infile &&_webin_fileref&i termstr=crlf lrecl=32767;
|
||||||
input;
|
input;
|
||||||
call symputx('input_statement',_infile_);
|
call symputx('input_statement',_infile_);
|
||||||
putlog "&&_webin_name&i input statement: " _infile_;
|
putlog "&&_webin_name&i input statement: " _infile_;
|
||||||
stop;
|
stop;
|
||||||
data &&_webin_name&i;
|
data &&_webin_name&i;
|
||||||
infile &&_webin_fileref&i firstobs=2 dsd termstr=crlf encoding='utf-8';
|
infile &&_webin_fileref&i firstobs=2 dsd termstr=crlf encoding='utf-8'
|
||||||
|
lrecl=32767;
|
||||||
input &input_statement;
|
input &input_statement;
|
||||||
%if %str(&_debug) ge 131 %then %do;
|
%if %str(&_debug) ge 131 %then %do;
|
||||||
if _n_<20 then putlog _infile_;
|
if _n_<20 then putlog _infile_;
|
||||||
@@ -84,14 +85,14 @@
|
|||||||
%end;
|
%end;
|
||||||
|
|
||||||
%else %if &action=OPEN %then %do;
|
%else %if &action=OPEN %then %do;
|
||||||
/* fix encoding */
|
/* fix encoding and ensure enough lrecl */
|
||||||
OPTIONS NOBOMFILE;
|
OPTIONS NOBOMFILE lrecl=32767;
|
||||||
|
|
||||||
/* set the header */
|
/* set the header */
|
||||||
%mfs_httpheader(Content-type,application/json)
|
%mfs_httpheader(Content-type,application/json)
|
||||||
|
|
||||||
/* setup json */
|
/* setup json. */
|
||||||
data _null_;file &fref encoding='utf-8' termstr=lf;
|
data _null_;file &fref encoding='utf-8' termstr=lf ;
|
||||||
put '{"SYSDATE" : "' "&SYSDATE" '"';
|
put '{"SYSDATE" : "' "&SYSDATE" '"';
|
||||||
put ',"SYSTIME" : "' "&SYSTIME" '"';
|
put ',"SYSTIME" : "' "&SYSTIME" '"';
|
||||||
run;
|
run;
|
||||||
@@ -99,6 +100,11 @@
|
|||||||
%end;
|
%end;
|
||||||
|
|
||||||
%else %if &action=ARR or &action=OBJ %then %do;
|
%else %if &action=ARR or &action=OBJ %then %do;
|
||||||
|
%if "%substr(&sysver,1,1)"="4" or "%substr(&sysver,1,1)"="5" %then %do;
|
||||||
|
/* functions in formats unsupported */
|
||||||
|
%put &sysmacroname: forcing missing back to NULL as feature not supported;
|
||||||
|
%let missing=NULL;
|
||||||
|
%end;
|
||||||
%mp_jsonout(&action,&ds,dslabel=&dslabel,fmt=&fmt,jref=&fref
|
%mp_jsonout(&action,&ds,dslabel=&dslabel,fmt=&fmt,jref=&fref
|
||||||
,engine=DATASTEP,missing=&missing,showmeta=&showmeta
|
,engine=DATASTEP,missing=&missing,showmeta=&showmeta
|
||||||
)
|
)
|
||||||
@@ -106,7 +112,6 @@
|
|||||||
%else %if &action=CLOSE %then %do;
|
%else %if &action=CLOSE %then %do;
|
||||||
%if %str(&_debug) ge 131 %then %do;
|
%if %str(&_debug) ge 131 %then %do;
|
||||||
/* if debug mode, send back first 10 records of each work table also */
|
/* if debug mode, send back first 10 records of each work table also */
|
||||||
options obs=10;
|
|
||||||
data;run;%let tempds=%scan(&syslast,2,.);
|
data;run;%let tempds=%scan(&syslast,2,.);
|
||||||
ods output Members=&tempds;
|
ods output Members=&tempds;
|
||||||
proc datasets library=WORK memtype=data;
|
proc datasets library=WORK memtype=data;
|
||||||
@@ -131,23 +136,25 @@
|
|||||||
put " ""&wt"" : {";
|
put " ""&wt"" : {";
|
||||||
put '"nlobs":' nlobs;
|
put '"nlobs":' nlobs;
|
||||||
put ',"nvars":' nvars;
|
put ',"nvars":' nvars;
|
||||||
%mp_jsonout(OBJ,&wt,jref=&fref,dslabel=first10rows,showmeta=YES)
|
%mp_jsonout(OBJ,&wt,jref=&fref,dslabel=first10rows,showmeta=Y,maxobs=10)
|
||||||
data _null_; file &fref mod encoding='utf-8' termstr=lf;
|
data _null_; file &fref mod encoding='utf-8' termstr=lf;
|
||||||
put "}";
|
put "}";
|
||||||
%end;
|
%end;
|
||||||
data _null_; file &fref mod encoding='utf-8' termstr=lf termstr=lf;
|
data _null_; file &fref mod encoding='utf-8' termstr=lf;
|
||||||
put "}";
|
put "}";
|
||||||
run;
|
run;
|
||||||
%end;
|
%end;
|
||||||
/* close off json */
|
/* close off json */
|
||||||
data _null_;file &fref mod encoding='utf-8' termstr=lf;
|
data _null_;file &fref mod encoding='utf-8' termstr=lf lrecl=32767;
|
||||||
_PROGRAM=quote(trim(resolve(symget('_PROGRAM'))));
|
_PROGRAM=quote(trim(resolve(symget('_PROGRAM'))));
|
||||||
put ",""SYSUSERID"" : ""&sysuserid"" ";
|
put ",""SYSUSERID"" : ""&sysuserid"" ";
|
||||||
put ",""MF_GETUSER"" : ""%mf_getuser()"" ";
|
put ",""MF_GETUSER"" : ""%mf_getuser()"" ";
|
||||||
put ",""_DEBUG"" : ""&_debug"" ";
|
put ",""_DEBUG"" : ""&_debug"" ";
|
||||||
put ',"_PROGRAM" : ' _PROGRAM ;
|
put ',"_PROGRAM" : ' _PROGRAM ;
|
||||||
put ",""SYSCC"" : ""&syscc"" ";
|
put ",""SYSCC"" : ""&syscc"" ";
|
||||||
put ",""SYSERRORTEXT"" : ""&syserrortext"" ";
|
put ",""SYSENCODING"" : ""&sysencoding"" ";
|
||||||
|
syserrortext=cats('"',tranwrd(symget('syserrortext'),'"','\"'),'"');
|
||||||
|
put ',"SYSERRORTEXT" : ' syserrortext;
|
||||||
SYSHOSTINFOLONG=quote(trim(symget('SYSHOSTINFOLONG')));
|
SYSHOSTINFOLONG=quote(trim(symget('SYSHOSTINFOLONG')));
|
||||||
put ',"SYSHOSTINFOLONG" : ' SYSHOSTINFOLONG;
|
put ',"SYSHOSTINFOLONG" : ' SYSHOSTINFOLONG;
|
||||||
put ",""SYSHOSTNAME"" : ""&syshostname"" ";
|
put ",""SYSHOSTNAME"" : ""&syshostname"" ";
|
||||||
@@ -162,7 +169,8 @@
|
|||||||
put ",""SYSTCPIPHOSTNAME"" : ""&SYSTCPIPHOSTNAME"" ";
|
put ",""SYSTCPIPHOSTNAME"" : ""&SYSTCPIPHOSTNAME"" ";
|
||||||
sysvlong=quote(trim(symget('sysvlong')));
|
sysvlong=quote(trim(symget('sysvlong')));
|
||||||
put ',"SYSVLONG" : ' sysvlong;
|
put ',"SYSVLONG" : ' sysvlong;
|
||||||
put ",""SYSWARNINGTEXT"" : ""&syswarningtext"" ";
|
syswarningtext=cats('"',tranwrd(symget('syswarningtext'),'"','\"'),'"');
|
||||||
|
put ',"SYSWARNINGTEXT" : ' syswarningtext;
|
||||||
put ',"END_DTTM" : "' "%sysfunc(datetime(),E8601DT26.6)" '" ';
|
put ',"END_DTTM" : "' "%sysfunc(datetime(),E8601DT26.6)" '" ';
|
||||||
length autoexec $512;
|
length autoexec $512;
|
||||||
autoexec=quote(urlencode(trim(getoption('autoexec'))));
|
autoexec=quote(urlencode(trim(getoption('autoexec'))));
|
||||||
|
|||||||
22
tests/base/mf_fmtdttm.test.sas
Normal file
22
tests/base/mf_fmtdttm.test.sas
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
/**
|
||||||
|
@file
|
||||||
|
@brief Testing mf_fmtdttm macro
|
||||||
|
|
||||||
|
<h4> SAS Macros </h4>
|
||||||
|
@li mf_fmtdttm.sas
|
||||||
|
@li mp_assert.sas
|
||||||
|
@li mp_assertscope.sas
|
||||||
|
|
||||||
|
**/
|
||||||
|
|
||||||
|
%global test1;
|
||||||
|
|
||||||
|
%mp_assertscope(SNAPSHOT)
|
||||||
|
%let test1=%mf_fmtdttm();
|
||||||
|
%mp_assertscope(COMPARE,ignorelist=test1)
|
||||||
|
|
||||||
|
%mp_assert(
|
||||||
|
iftrue=("&test1"="DATETIME19.3" or "&test1"="E8601DT26.6"),
|
||||||
|
desc=Basic test,
|
||||||
|
outds=work.test_results
|
||||||
|
)
|
||||||
46
tests/base/mf_getvarcount.test.sas
Normal file
46
tests/base/mf_getvarcount.test.sas
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
/**
|
||||||
|
@file
|
||||||
|
@brief Testing mf_getvarlist macro
|
||||||
|
|
||||||
|
<h4> SAS Macros </h4>
|
||||||
|
@li mf_getvarcount.sas
|
||||||
|
@li mp_assertscope.sas
|
||||||
|
|
||||||
|
**/
|
||||||
|
|
||||||
|
data work.all work.nums(keep=num1 num2) work.chars(keep=char1 char2);
|
||||||
|
length num1 num2 8 char1 char2 char3 $4;
|
||||||
|
call missing (of _all_);
|
||||||
|
output;
|
||||||
|
run;
|
||||||
|
|
||||||
|
%mp_assertscope(SNAPSHOT)
|
||||||
|
%put scope check:%mf_getvarcount(work.all);
|
||||||
|
%mp_assertscope(COMPARE)
|
||||||
|
|
||||||
|
%mp_assert(
|
||||||
|
iftrue=(%mf_getvarcount(work.all)=5),
|
||||||
|
desc=%str(Checking for mixed vars),
|
||||||
|
outds=work.test_results
|
||||||
|
)
|
||||||
|
%mp_assert(
|
||||||
|
iftrue=(%mf_getvarcount(work.all,typefilter=C)=3),
|
||||||
|
desc=%str(Checking for char in mixed vars),
|
||||||
|
outds=work.test_results
|
||||||
|
)
|
||||||
|
%mp_assert(
|
||||||
|
iftrue=(%mf_getvarcount(work.all,typefilter=N)=2),
|
||||||
|
desc=%str(Checking for num in mixed vars),
|
||||||
|
outds=work.test_results
|
||||||
|
)
|
||||||
|
%mp_assert(
|
||||||
|
iftrue=(%mf_getvarcount(work.nums,typefilter=c)=0),
|
||||||
|
desc=%str(Checking for char in num vars),
|
||||||
|
outds=work.test_results
|
||||||
|
)
|
||||||
|
%mp_assert(
|
||||||
|
iftrue=(%mf_getvarcount(work.chars,typefilter=N)=0),
|
||||||
|
desc=%str(Checking for num in char vars),
|
||||||
|
outds=work.test_results
|
||||||
|
)
|
||||||
|
|
||||||
35
tests/base/mf_increment.test.sas
Normal file
35
tests/base/mf_increment.test.sas
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
/**
|
||||||
|
@file
|
||||||
|
@brief Testing mf_increment macro
|
||||||
|
|
||||||
|
<h4> SAS Macros </h4>
|
||||||
|
@li mf_increment.sas
|
||||||
|
@li mp_assert.sas
|
||||||
|
|
||||||
|
**/
|
||||||
|
|
||||||
|
%let var=0;
|
||||||
|
|
||||||
|
%mp_assert(
|
||||||
|
iftrue=(
|
||||||
|
"%mf_increment(var)"="1"
|
||||||
|
),
|
||||||
|
desc=Checking basic mf_increment usage 1,
|
||||||
|
outds=work.test_results
|
||||||
|
)
|
||||||
|
|
||||||
|
%mp_assert(
|
||||||
|
iftrue=(
|
||||||
|
"%mf_increment(var)"="2"
|
||||||
|
),
|
||||||
|
desc=Checking basic mf_increment usage 2,
|
||||||
|
outds=work.test_results
|
||||||
|
)
|
||||||
|
|
||||||
|
%mp_assert(
|
||||||
|
iftrue=(
|
||||||
|
"%mf_increment(var,incr=2)"="4"
|
||||||
|
),
|
||||||
|
desc=Checking incr option,
|
||||||
|
outds=work.test_results
|
||||||
|
)
|
||||||
@@ -30,4 +30,12 @@
|
|||||||
),
|
),
|
||||||
desc=Checking mf_isint(-1),
|
desc=Checking mf_isint(-1),
|
||||||
outds=work.test_results
|
outds=work.test_results
|
||||||
|
)
|
||||||
|
|
||||||
|
%mp_assert(
|
||||||
|
iftrue=(
|
||||||
|
"%mf_isint()"="0"
|
||||||
|
),
|
||||||
|
desc=Checking mf_isint(),
|
||||||
|
outds=work.test_results
|
||||||
)
|
)
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user