mirror of
https://github.com/sasjs/core.git
synced 2025-12-11 06:24:35 +00:00
Compare commits
122 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f301899269 | ||
|
|
fc81f62d2f | ||
|
|
93aea5ed02 | ||
|
|
55d4c7238a | ||
|
|
cd75bf263a | ||
|
|
929a1a9974 | ||
|
|
7cafb4fb36 | ||
|
|
a8d222a0f8 | ||
|
|
ac0ddf38b0 | ||
|
|
ecd389c935 | ||
|
|
06a5ea06f8 | ||
|
|
955471ed3c | ||
|
|
c8d3b43b12 | ||
|
|
3e313b06a9 | ||
|
|
d7371a4505 | ||
|
|
32a6d15c2e | ||
|
|
b109e7cead | ||
| d291d3e287 | |||
|
|
5a2968e798 | ||
|
|
120ad9a7da | ||
|
|
67a81b2690 | ||
|
|
506cf1812f | ||
|
|
8cc0eb0dd7 | ||
|
|
4c1f69da3a | ||
|
|
f160ebe705 | ||
|
|
3f49925d01 | ||
|
|
53ed5dc916 | ||
|
|
808b24e31b | ||
|
|
c51c9c2ca9 | ||
|
|
4d6edf5566 | ||
|
|
41a24677f5 | ||
|
|
e7d8d8ffb3 | ||
|
|
60d23dd618 | ||
|
|
d2764c3cd1 | ||
|
|
f9f4355143 | ||
|
|
18bc6c889d | ||
|
|
42a16ef496 | ||
|
|
8178b801fb | ||
|
|
ce331a23c8 | ||
|
|
1cc9213467 | ||
|
|
aabbf4d0f9 | ||
|
|
2bea8be70d | ||
|
|
9b2368443e | ||
|
|
7915ba2c41 | ||
|
|
cc61e48868 | ||
|
|
62db83dcf6 | ||
|
|
7ea9e0f8e9 | ||
|
|
1c852515f5 | ||
|
|
b7e677bd8e | ||
|
|
f47f0d2cee | ||
|
|
3d0f426a98 | ||
|
|
2cb51f6164 | ||
|
|
b73bf998da | ||
|
|
c9ad38ee98 | ||
|
|
76b1b951c0 | ||
|
|
996054b17a | ||
|
|
7fca3d4e3f | ||
|
|
c4e599c861 | ||
|
|
0f6ff2cc1e | ||
|
|
7cac4c71fb | ||
|
|
1322bdab92 | ||
|
|
f201df606a | ||
|
|
a56fce86b1 | ||
|
|
41ccc5fdd9 | ||
|
|
b2877bd493 | ||
|
|
df8f8893e7 | ||
|
|
27fbdf193b | ||
|
|
6ae892989d | ||
|
|
39a7b332da | ||
|
|
c81794b542 | ||
|
|
e456da846a | ||
|
|
5c144be05b | ||
|
|
055669c133 | ||
|
|
4b67e13b24 | ||
|
|
f1ec3eda81 | ||
|
|
f2d5859675 | ||
|
|
ea057d4655 | ||
|
|
26c085b354 | ||
|
|
d13ac52739 | ||
|
|
bbbc28ad6d | ||
|
|
530cd6e95c | ||
|
|
c4e17e43e8 | ||
|
|
fed217eec3 | ||
|
|
1934dc8332 | ||
|
|
9de512cfc7 | ||
|
|
cadafcc86b | ||
|
|
5f805b006f | ||
|
|
c6b65366b7 | ||
|
|
51ddd9c1e5 | ||
|
|
20bf3b86af | ||
|
|
de67cd329b | ||
|
|
779e4942c7 | ||
|
|
a69a1ac7f0 | ||
|
|
2a644d6c2b | ||
|
|
843930c666 | ||
|
|
90d69af7ee | ||
|
|
b7bafb49f4 | ||
|
|
2fa9e48286 | ||
|
|
5cee93c7bd | ||
|
|
1a595c64c6 | ||
|
|
2c901831b7 | ||
|
|
28209950ab | ||
|
|
44069e9867 | ||
|
|
e26af5c09a | ||
|
|
4ee13c9389 | ||
|
|
15f903aa42 | ||
|
|
58a0cce39e | ||
|
|
9a5574ea0e | ||
|
|
e6146dcbcf | ||
|
|
583c7e0c83 | ||
|
|
223bdd5983 | ||
|
|
aef14543f0 | ||
|
|
c88764c1d8 | ||
|
|
2c952c8b01 | ||
|
|
de3610d1aa | ||
|
|
d35d597437 | ||
|
|
3e8deda008 | ||
|
|
a27496c7b3 | ||
|
|
265389befc | ||
|
|
db2531e0b3 | ||
|
|
5e77494aa6 | ||
|
|
6a2ac51925 |
18
.git-hooks/commit-msg
Executable file
18
.git-hooks/commit-msg
Executable file
@@ -0,0 +1,18 @@
|
||||
#!/bin/sh
|
||||
RED="\033[1;31m"
|
||||
GREEN="\033[1;32m"
|
||||
|
||||
# Get the commit message (the parameter we're given is just the path to the
|
||||
# temporary file which holds the message).
|
||||
commit_message=$(cat "$1")
|
||||
|
||||
if (echo "$commit_message" | grep -Eq "^(build|chore|ci|docs|feat|fix|perf|refactor|revert|style|test)(\([a-z \-]+\))?!?: .+$") then
|
||||
echo "${GREEN} ✔ Commit message meets Conventional Commit standards"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo "${RED}❌ Commit message does not meet the Conventional Commit standard!"
|
||||
echo "An example of a valid message is:"
|
||||
echo " feat(login): add the 'remember me' button"
|
||||
echo "ℹ More details at: https://www.conventionalcommits.org/en/v1.0.0/#summary"
|
||||
exit 1
|
||||
2
.git-hooks/pre-commit
Executable file
2
.git-hooks/pre-commit
Executable file
@@ -0,0 +1,2 @@
|
||||
#!/bin/sh
|
||||
sasjs lint
|
||||
30
.github/vpn/config.ovpn
vendored
Normal file
30
.github/vpn/config.ovpn
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
cipher AES-256-CBC
|
||||
setenv FORWARD_COMPATIBLE 1
|
||||
client
|
||||
server-poll-timeout 4
|
||||
nobind
|
||||
remote vpn.analytium.co.uk 1194 udp
|
||||
remote vpn.analytium.co.uk 1194 udp
|
||||
remote vpn.analytium.co.uk 443 tcp
|
||||
remote vpn.analytium.co.uk 1194 udp
|
||||
remote vpn.analytium.co.uk 1194 udp
|
||||
remote vpn.analytium.co.uk 1194 udp
|
||||
remote vpn.analytium.co.uk 1194 udp
|
||||
remote vpn.analytium.co.uk 1194 udp
|
||||
dev tun
|
||||
dev-type tun
|
||||
ns-cert-type server
|
||||
setenv opt tls-version-min 1.0 or-highest
|
||||
reneg-sec 604800
|
||||
sndbuf 0
|
||||
rcvbuf 0
|
||||
# NOTE: LZO commands are pushed by the Access Server at connect time.
|
||||
# NOTE: The below line doesn't disable LZO.
|
||||
comp-lzo no
|
||||
verb 3
|
||||
setenv PUSH_PEER_INFO
|
||||
|
||||
ca ca.crt
|
||||
cert user.crt
|
||||
key user.key
|
||||
tls-auth tls.key 1
|
||||
84
.github/workflows/run-tests.yml
vendored
Normal file
84
.github/workflows/run-tests.yml
vendored
Normal file
@@ -0,0 +1,84 @@
|
||||
# This workflow will do a clean install of node dependencies, build the source code and run tests across different versions of node
|
||||
# For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions
|
||||
|
||||
name: Node.js CI
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
node-version: [12.x]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Use Node.js ${{ matrix.node-version }}
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
|
||||
- name: Write VPN Files
|
||||
run: |
|
||||
echo "$CA_CRT" > .github/vpn/ca.crt
|
||||
echo "$USER_CRT" > .github/vpn/user.crt
|
||||
echo "$USER_KEY" > .github/vpn/user.key
|
||||
echo "$TLS_KEY" > .github/vpn/tls.key
|
||||
shell: bash
|
||||
env:
|
||||
CA_CRT: ${{ secrets.CA_CRT}}
|
||||
USER_CRT: ${{ secrets.USER_CRT }}
|
||||
USER_KEY: ${{ secrets.USER_KEY }}
|
||||
TLS_KEY: ${{ secrets.TLS_KEY }}
|
||||
|
||||
- name: Install Open VPN
|
||||
run: |
|
||||
sudo apt install apt-transport-https
|
||||
sudo wget https://swupdate.openvpn.net/repos/openvpn-repo-pkg-key.pub
|
||||
sudo apt-key add openvpn-repo-pkg-key.pub
|
||||
sudo wget -O /etc/apt/sources.list.d/openvpn3.list https://swupdate.openvpn.net/community/openvpn3/repos/openvpn3-bionic.list
|
||||
sudo apt update
|
||||
sudo apt install openvpn3
|
||||
|
||||
- name: Start Open VPN 3
|
||||
run: openvpn3 session-start --config .github/vpn/config.ovpn
|
||||
|
||||
- name: Install Doxygen
|
||||
run: sudo apt-get install doxygen
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm ci
|
||||
|
||||
- name: Check code style
|
||||
run: npm run lint
|
||||
|
||||
- name: Add client
|
||||
run: echo "CLIENT=${{secrets.CLIENT}}"> .env.viya
|
||||
|
||||
- name: Add secret
|
||||
run: echo "SECRET=${{secrets.SECRET}}" >> .env.viya
|
||||
|
||||
- name: Add access token
|
||||
run: echo "ACCESS_TOKEN=${{secrets.ACCESS_TOKEN}}" >> .env.viya
|
||||
|
||||
- name: Add refresh token
|
||||
run: echo "REFRESH_TOKEN=${{secrets.REFRESH_TOKEN}}" >> .env.viya
|
||||
|
||||
- name: Build Project
|
||||
run: npm run build
|
||||
|
||||
- name: Run SASjs tests
|
||||
run: npm run test
|
||||
env:
|
||||
CI: true
|
||||
CLIENT: ${{secrets.CLIENT}}
|
||||
SECRET: ${{secrets.SECRET}}
|
||||
SAS_USERNAME: ${{secrets.SAS_USERNAME}}
|
||||
SAS_PASSWORD: ${{secrets.SAS_PASSWORD}}
|
||||
SERVER_URL: ${{secrets.SERVER_URL}}
|
||||
SERVER_TYPE: ${{secrets.SERVER_TYPE}}
|
||||
ACCESS_TOKEN: ${{secrets.ACCESS_TOKEN}}
|
||||
REFRESH_TOKEN: ${{secrets.REFRESH_TOKEN}}
|
||||
@@ -1,5 +1,5 @@
|
||||
tasks:
|
||||
- init: npm i -g @sasjs/cli
|
||||
- init: nvm install --latest-npm && npm i -g @sasjs/cli
|
||||
|
||||
image:
|
||||
file: .gitpod.dockerfile
|
||||
|
||||
11
.npmignore
Normal file
11
.npmignore
Normal file
@@ -0,0 +1,11 @@
|
||||
all.sas
|
||||
build.py
|
||||
.gitpod*
|
||||
tests/
|
||||
sasjs/
|
||||
.github/
|
||||
.git-hooks/
|
||||
.vscode/
|
||||
main.dox
|
||||
make_singlefile.sh
|
||||
*.md
|
||||
@@ -2,11 +2,11 @@
|
||||
"noTrailingSpaces": true,
|
||||
"noEncodedPasswords": true,
|
||||
"hasDoxygenHeader": true,
|
||||
"hasMacroNameInMend": false,
|
||||
"hasMacroNameInMend": true,
|
||||
"hasMacroParentheses": true,
|
||||
"noNestedMacros": false,
|
||||
"noSpacesInFileNames": true,
|
||||
"maxLineLength": 135,
|
||||
"maxLineLength": 230,
|
||||
"lowerCaseFileNames": true,
|
||||
"noTabIndentation": true,
|
||||
"indentationMultiple": 2
|
||||
|
||||
5
CHANGELOG.md
Normal file
5
CHANGELOG.md
Normal file
@@ -0,0 +1,5 @@
|
||||
# CHANGELOG
|
||||
|
||||
As the changes are managed automatically in github, we don't generate an additional changelog. To view the fixes/features in each release, check out the releases page below:
|
||||
|
||||
[https://github.com/sasjs/core/releases](https://github.com/sasjs/core/releases)
|
||||
128
CODE_OF_CONDUCT.md
Normal file
128
CODE_OF_CONDUCT.md
Normal file
@@ -0,0 +1,128 @@
|
||||
# Contributor Covenant Code of Conduct
|
||||
|
||||
## Our Pledge
|
||||
|
||||
We as members, contributors, and leaders pledge to make participation in our
|
||||
community a harassment-free experience for everyone, regardless of age, body
|
||||
size, visible or invisible disability, ethnicity, sex characteristics, gender
|
||||
identity and expression, level of experience, education, socio-economic status,
|
||||
nationality, personal appearance, race, religion, or sexual identity
|
||||
and orientation.
|
||||
|
||||
We pledge to act and interact in ways that contribute to an open, welcoming,
|
||||
diverse, inclusive, and healthy community.
|
||||
|
||||
## Our Standards
|
||||
|
||||
Examples of behavior that contributes to a positive environment for our
|
||||
community include:
|
||||
|
||||
* Demonstrating empathy and kindness toward other people
|
||||
* Being respectful of differing opinions, viewpoints, and experiences
|
||||
* Giving and gracefully accepting constructive feedback
|
||||
* Accepting responsibility and apologizing to those affected by our mistakes,
|
||||
and learning from the experience
|
||||
* Focusing on what is best not just for us as individuals, but for the
|
||||
overall community
|
||||
|
||||
Examples of unacceptable behavior include:
|
||||
|
||||
* The use of sexualized language or imagery, and sexual attention or
|
||||
advances of any kind
|
||||
* Trolling, insulting or derogatory comments, and personal or political attacks
|
||||
* Public or private harassment
|
||||
* Publishing others' private information, such as a physical or email
|
||||
address, without their explicit permission
|
||||
* Other conduct which could reasonably be considered inappropriate in a
|
||||
professional setting
|
||||
|
||||
## Enforcement Responsibilities
|
||||
|
||||
Community leaders are responsible for clarifying and enforcing our standards of
|
||||
acceptable behavior and will take appropriate and fair corrective action in
|
||||
response to any behavior that they deem inappropriate, threatening, offensive,
|
||||
or harmful.
|
||||
|
||||
Community leaders have the right and responsibility to remove, edit, or reject
|
||||
comments, commits, code, wiki edits, issues, and other contributions that are
|
||||
not aligned to this Code of Conduct, and will communicate reasons for moderation
|
||||
decisions when appropriate.
|
||||
|
||||
## Scope
|
||||
|
||||
This Code of Conduct applies within all community spaces, and also applies when
|
||||
an individual is officially representing the community in public spaces.
|
||||
Examples of representing our community include using an official e-mail address,
|
||||
posting via an official social media account, or acting as an appointed
|
||||
representative at an online or offline event.
|
||||
|
||||
## Enforcement
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||
reported to the community leaders responsible for enforcement at
|
||||
https://sasapps.io/contact-us.
|
||||
All complaints will be reviewed and investigated promptly and fairly.
|
||||
|
||||
All community leaders are obligated to respect the privacy and security of the
|
||||
reporter of any incident.
|
||||
|
||||
## Enforcement Guidelines
|
||||
|
||||
Community leaders will follow these Community Impact Guidelines in determining
|
||||
the consequences for any action they deem in violation of this Code of Conduct:
|
||||
|
||||
### 1. Correction
|
||||
|
||||
**Community Impact**: Use of inappropriate language or other behavior deemed
|
||||
unprofessional or unwelcome in the community.
|
||||
|
||||
**Consequence**: A private, written warning from community leaders, providing
|
||||
clarity around the nature of the violation and an explanation of why the
|
||||
behavior was inappropriate. A public apology may be requested.
|
||||
|
||||
### 2. Warning
|
||||
|
||||
**Community Impact**: A violation through a single incident or series
|
||||
of actions.
|
||||
|
||||
**Consequence**: A warning with consequences for continued behavior. No
|
||||
interaction with the people involved, including unsolicited interaction with
|
||||
those enforcing the Code of Conduct, for a specified period of time. This
|
||||
includes avoiding interactions in community spaces as well as external channels
|
||||
like social media. Violating these terms may lead to a temporary or
|
||||
permanent ban.
|
||||
|
||||
### 3. Temporary Ban
|
||||
|
||||
**Community Impact**: A serious violation of community standards, including
|
||||
sustained inappropriate behavior.
|
||||
|
||||
**Consequence**: A temporary ban from any sort of interaction or public
|
||||
communication with the community for a specified period of time. No public or
|
||||
private interaction with the people involved, including unsolicited interaction
|
||||
with those enforcing the Code of Conduct, is allowed during this period.
|
||||
Violating these terms may lead to a permanent ban.
|
||||
|
||||
### 4. Permanent Ban
|
||||
|
||||
**Community Impact**: Demonstrating a pattern of violation of community
|
||||
standards, including sustained inappropriate behavior, harassment of an
|
||||
individual, or aggression toward or disparagement of classes of individuals.
|
||||
|
||||
**Consequence**: A permanent ban from any sort of public interaction within
|
||||
the community.
|
||||
|
||||
## Attribution
|
||||
|
||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
|
||||
version 2.0, available at
|
||||
https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
|
||||
|
||||
Community Impact Guidelines were inspired by [Mozilla's code of conduct
|
||||
enforcement ladder](https://github.com/mozilla/diversity).
|
||||
|
||||
[homepage]: https://www.contributor-covenant.org
|
||||
|
||||
For answers to common questions about this code of conduct, see the FAQ at
|
||||
https://www.contributor-covenant.org/faq. Translations are available at
|
||||
https://www.contributor-covenant.org/translations.
|
||||
36
README.md
36
README.md
@@ -1,4 +1,25 @@
|
||||
# Macro Core
|
||||
[![npm package][npm-image]][npm-url]
|
||||
[![Github Workflow][githubworkflow-image]][githubworkflow-url]
|
||||
[![Dependency Status][dependency-image]][dependency-url]
|
||||
[]()
|
||||

|
||||
[](/LICENSE)
|
||||

|
||||
[](https://github.com/sasjs/core/issues?q=is%3Aissue+is%3Aclosed)
|
||||
[](https://github.com/sasjs/core/issues)
|
||||

|
||||
[](https://gitpod.io/#https://github.com/sasjs/core)
|
||||
|
||||
|
||||
[npm-image]:https://img.shields.io/npm/v/@sasjs/core.svg
|
||||
[npm-url]:http://npmjs.org/package/@sasjs/core
|
||||
[githubworkflow-image]:https://github.com/sasjs/core/actions/workflows/main.yml/badge.svg
|
||||
[githubworkflow-url]:https://github.com/sasjs/core/blob/main/.github/workflows/main.yml
|
||||
[dependency-image]:https://david-dm.org/sasjs/core.svg
|
||||
[dependency-url]:https://github.com/sasjs/core/blob/main/package.json
|
||||
|
||||
|
||||
|
||||
Much quality. Many standards. The **Macro Core** library exists to save time and development effort! Herein ye shall find a veritable host of MIT-licenced, production quality SAS macros. These are a mix of tools, utilities, functions and code generators that are useful in the context of [Application Development](https://sasapps.io) on the SAS platform (eg https://datacontroller.io). [Contributions](https://github.com/sasjs/core/blob/main/CONTRIBUTING.md) are welcomed.
|
||||
|
||||
@@ -116,19 +137,19 @@ The **Macro Core** documentation is created using [doxygen](http://www.doxygen.n
|
||||
All macros must be commented in the doxygen format, to enable the [online documentation](https://core.sasjs.io).
|
||||
|
||||
### Dependencies
|
||||
SAS code can contain one of two types of dependency - SAS Macros, and SAS Programs. When compiling projects using the [SASjs CLI](https://cli.sasjs.io) the doxygen header is scanned for ` @li` items under the following headers:
|
||||
SAS code can contain one of two types of dependency - SAS Macros, and SAS Includes. When compiling projects using the [SASjs CLI](https://cli.sasjs.io) the doxygen header is scanned for ` @li` items under the following headers:
|
||||
|
||||
```sas
|
||||
<h4> SAS Macros </h4>
|
||||
@li mf_nobs.sas
|
||||
@li mm_assignlib.sas
|
||||
|
||||
<h4> SAS Programs </h4>
|
||||
<h4> SAS Includes </h4>
|
||||
@li somefile.ddl SOMEFREF
|
||||
@li someprogram.sas FREFTWO
|
||||
```
|
||||
|
||||
The CLI can then extract all the dependencies and insert as precode (SAS Macros) or in a temp engine fileref (SAS Programs) when creating SAS Jobs and Services.
|
||||
The CLI can then extract all the dependencies and insert as precode (SAS Macros) or in a temp engine fileref (SAS Includes) when creating SAS Jobs and Services.
|
||||
|
||||
When contributing to this library, it is therefore important to ensure that all dependencies are listed in the header in this format.
|
||||
|
||||
@@ -149,3 +170,12 @@ When contributing to this library, it is therefore important to ensure that all
|
||||
# General Notes
|
||||
|
||||
- All macros should be compatible with SAS versions from support level B and above (so currently 9.2 and later). If an earlier version is not supported, then the macro should say as such in the header documentation, and exit gracefully (eg `%if %sysevalf(&sysver<9.3) %then %return`).
|
||||
|
||||
## Star Gazing
|
||||
|
||||
If you find this library useful, please leave a [star](https://github.com/sasjs/core/stargazers) and help us grow our star graph!
|
||||
|
||||

|
||||
|
||||
|
||||
|
||||
|
||||
18
SECURITY.md
Normal file
18
SECURITY.md
Normal file
@@ -0,0 +1,18 @@
|
||||
# Security Policy
|
||||
|
||||
Security is an extremely high priority when it comes to the SASjs product suite. We take a number of steps across all repositories to minimise risk, such as:
|
||||
|
||||
* Regular dependabot updates
|
||||
* Snyk reports
|
||||
* Minimising dependencies, especially production dependencies (sasjs/core has NONE)
|
||||
* Testing & Code review process
|
||||
|
||||
## Supported Versions
|
||||
|
||||
We support only the latest version
|
||||
|
||||
## Reporting a Vulnerability
|
||||
|
||||
We welcome disclosures of all kinds in relation to all the SASjs libraries. You can submit them here: [https://sasapps.io/contact-us](https://sasapps.io/contact-us)
|
||||
|
||||
|
||||
@@ -1,30 +1,17 @@
|
||||
/**
|
||||
@file
|
||||
@brief abort gracefully according to context
|
||||
@details Do not use directly! See bottom of explanation for details.
|
||||
@brief Abort, ungracefully
|
||||
@details Will abort with a straightforward %abort if the condition is true.
|
||||
|
||||
Configures an abort mechanism according to site specific policies or the
|
||||
particulars of an environment. For instance, can stream custom
|
||||
results back to the client in an STP Web App context, or completely stop
|
||||
in the case of a batch run.
|
||||
|
||||
For the sharp eyed readers - this is no longer a macro function!! It became
|
||||
a macro procedure during a project and now it's kinda stuck that way until
|
||||
that project is updated (if it's ever updated). In the meantime we created
|
||||
`mp_abort` which is just a wrapper for this one, and so we recomend you use
|
||||
that for forwards compatibility reasons.
|
||||
|
||||
@param mac= to contain the name of the calling macro
|
||||
@param type= deprecated. Not used.
|
||||
@param msg= message to be returned
|
||||
@param iftrue= supply a condition under which the macro should be executed.
|
||||
<h4> Related Macros </h4>
|
||||
@li mp_abort.sas
|
||||
|
||||
@version 9.2
|
||||
@author Allan Bowe
|
||||
@cond
|
||||
**/
|
||||
|
||||
%macro mf_abort(mac=mf_abort.sas, type=, msg=, iftrue=%str(1=1)
|
||||
%macro mf_abort(mac=mf_abort.sas, type=deprecated, msg=, iftrue=%str(1=1)
|
||||
)/*/STORE SOURCE*/;
|
||||
|
||||
%if not(%eval(%unquote(&iftrue))) %then %return;
|
||||
@@ -33,116 +20,8 @@
|
||||
%if %length(&mac)>0 %then %put NOTE- called by &mac;
|
||||
%put NOTE - &msg;
|
||||
|
||||
/* Stored Process Server web app context */
|
||||
%if %symexist(_metaperson) or "&SYSPROCESSNAME"="Compute Server" %then %do;
|
||||
options obs=max replace nosyntaxcheck mprint;
|
||||
/* extract log err / warn, if exist */
|
||||
%local logloc logline;
|
||||
%global logmsg; /* capture global messages */
|
||||
%if %symexist(SYSPRINTTOLOG) %then %let logloc=&SYSPRINTTOLOG;
|
||||
%else %let logloc=%qsysfunc(getoption(LOG));
|
||||
proc printto log=log;run;
|
||||
%if %length(&logloc)>0 %then %do;
|
||||
%let logline=0;
|
||||
data _null_;
|
||||
infile &logloc lrecl=5000;
|
||||
input; putlog _infile_;
|
||||
i=1;
|
||||
retain logonce 0;
|
||||
if (
|
||||
_infile_=:"%str(WARN)ING" or _infile_=:"%str(ERR)OR"
|
||||
) and logonce=0
|
||||
then do;
|
||||
call symputx('logline',_n_);
|
||||
logonce+1;
|
||||
end;
|
||||
run;
|
||||
/* capture log including lines BEFORE the err */
|
||||
%if &logline>0 %then %do;
|
||||
data _null_;
|
||||
infile &logloc lrecl=5000;
|
||||
input;
|
||||
i=1;
|
||||
stoploop=0;
|
||||
if _n_ ge &logline-5 and stoploop=0 then do until (i>12);
|
||||
call symputx('logmsg',catx('\n',symget('logmsg'),_infile_));
|
||||
input;
|
||||
i+1;
|
||||
stoploop=1;
|
||||
end;
|
||||
if stoploop=1 then stop;
|
||||
run;
|
||||
%end;
|
||||
%end;
|
||||
%abort;
|
||||
|
||||
/* send response in SASjs JSON format */
|
||||
data _null_;
|
||||
file _webout mod lrecl=32000;
|
||||
length msg $32767;
|
||||
sasdatetime=datetime();
|
||||
msg=cats(symget('msg'),'\n\nLog Extract:\n',symget('logmsg'));
|
||||
/* escape the quotes */
|
||||
msg=tranwrd(msg,'"','\"');
|
||||
/* ditch the CRLFs as chrome complains */
|
||||
msg=compress(msg,,'kw');
|
||||
/* quote without quoting the quotes (which are escaped instead) */
|
||||
msg=cats('"',msg,'"');
|
||||
if symexist('_debug') then debug=symget('_debug');
|
||||
if debug ge 131 then put '>>weboutBEGIN<<';
|
||||
put '{"START_DTTM" : "' "%sysfunc(datetime(),datetime20.3)" '"';
|
||||
put ',"sasjsAbort" : [{';
|
||||
put ' "MSG":' msg ;
|
||||
put ' ,"MAC": "' "&mac" '"}]';
|
||||
put ",""SYSUSERID"" : ""&sysuserid"" ";
|
||||
if symexist('_metauser') then do;
|
||||
_METAUSER=quote(trim(symget('_METAUSER')));
|
||||
put ",""_METAUSER"": " _METAUSER;
|
||||
_METAPERSON=quote(trim(symget('_METAPERSON')));
|
||||
put ',"_METAPERSON": ' _METAPERSON;
|
||||
end;
|
||||
_PROGRAM=quote(trim(resolve(symget('_PROGRAM'))));
|
||||
put ',"_PROGRAM" : ' _PROGRAM ;
|
||||
put ",""SYSCC"" : ""&syscc"" ";
|
||||
put ",""SYSERRORTEXT"" : ""&syserrortext"" ";
|
||||
put ",""SYSJOBID"" : ""&sysjobid"" ";
|
||||
put ",""SYSWARNINGTEXT"" : ""&syswarningtext"" ";
|
||||
put ',"END_DTTM" : "' "%sysfunc(datetime(),datetime20.3)" '" ';
|
||||
put "}" @;
|
||||
%if &_debug ge 131 %then %do;
|
||||
put '>>weboutEND<<';
|
||||
%end;
|
||||
run;
|
||||
%let syscc=0;
|
||||
%if %symexist(SYS_JES_JOB_URI) %then %do;
|
||||
/* refer web service output to file service in one hit */
|
||||
filename _webout filesrvc parenturi="&SYS_JES_JOB_URI"
|
||||
name="_webout.json";
|
||||
%let rc=%sysfunc(fcopy(_web,_webout));
|
||||
%end;
|
||||
%else %do;
|
||||
data _null_;
|
||||
if symexist('sysprocessmode')
|
||||
then if symget("sysprocessmode")="SAS Stored Process Server"
|
||||
then rc=stpsrvset('program error', 0);
|
||||
run;
|
||||
%end;
|
||||
/**
|
||||
* endsas is reliable but kills some deployments.
|
||||
* Abort variants are ungraceful (non zero return code)
|
||||
* This approach lets SAS run silently until the end :-)
|
||||
*/
|
||||
%put _all_;
|
||||
filename skip temp;
|
||||
data _null_;
|
||||
file skip;
|
||||
put '%macro skip(); %macro skippy();';
|
||||
run;
|
||||
%inc skip;
|
||||
%end;
|
||||
%else %do;
|
||||
%put _all_;
|
||||
%abort cancel;
|
||||
%end;
|
||||
%mend;
|
||||
%mend mf_abort;
|
||||
|
||||
/** @endcond */
|
||||
@@ -23,4 +23,4 @@
|
||||
%if %sysfunc(exist(&libds)) ne 1 & %sysfunc(exist(&libds,VIEW)) ne 1 %then 0;
|
||||
%else 1;
|
||||
|
||||
%mend;
|
||||
%mend mf_existds;
|
||||
|
||||
@@ -42,6 +42,6 @@
|
||||
-1
|
||||
%put &sysmacroname: &feature not found;
|
||||
%end;
|
||||
%mend;
|
||||
%mend mf_existfeature;
|
||||
|
||||
/** @endcond */
|
||||
@@ -24,4 +24,4 @@
|
||||
0
|
||||
%end;
|
||||
|
||||
%mend;
|
||||
%mend mf_existfileref;
|
||||
@@ -30,6 +30,6 @@
|
||||
%let rc=%sysfunc(close(&dsid));
|
||||
%end;
|
||||
|
||||
%mend;
|
||||
%mend mf_existvar;
|
||||
|
||||
/** @endcond */
|
||||
@@ -54,6 +54,6 @@
|
||||
0
|
||||
%put Vars not found: &found;
|
||||
%end;
|
||||
%mend;
|
||||
%mend mf_existvarlist;
|
||||
|
||||
/** @endcond */
|
||||
@@ -31,4 +31,4 @@
|
||||
%sysfunc(attrc(&dsid,&attr))
|
||||
%let rc=%sysfunc(close(&dsid));
|
||||
%end;
|
||||
%mend;
|
||||
%mend mf_getattrc;
|
||||
@@ -31,4 +31,4 @@
|
||||
%sysfunc(attrn(&dsid,&attr))
|
||||
%let rc=%sysfunc(close(&dsid));
|
||||
%end;
|
||||
%mend;
|
||||
%mend mf_getattrn;
|
||||
@@ -48,6 +48,6 @@
|
||||
|
||||
&engine
|
||||
|
||||
%mend;
|
||||
%mend mf_getengine;
|
||||
|
||||
/** @endcond */
|
||||
@@ -44,4 +44,4 @@
|
||||
%sysfunc(INPUTN(&bytes, best.),sizekmg.)
|
||||
%end;
|
||||
|
||||
%mend ;
|
||||
%mend mf_getfilesize ;
|
||||
@@ -29,4 +29,4 @@
|
||||
&valc
|
||||
%end;
|
||||
%else %put %str(ERR)OR: Unable to find key &key in ds &libds;
|
||||
%mend;
|
||||
%mend mf_getkeyvalue;
|
||||
@@ -62,4 +62,4 @@
|
||||
%else %if &switch=VIYARESTAPI %then %do;
|
||||
%mf_trimstr(%sysfunc(getoption(servicesbaseurl)),/)
|
||||
%end;
|
||||
%mend;
|
||||
%mend mf_getplatform;
|
||||
@@ -50,4 +50,4 @@
|
||||
|
||||
&buffer
|
||||
|
||||
%mend;
|
||||
%mend mf_getquotedstr;
|
||||
@@ -38,6 +38,6 @@
|
||||
|
||||
&schema
|
||||
|
||||
%mend;
|
||||
%mend mf_getschema;
|
||||
|
||||
/** @endcond */
|
||||
|
||||
@@ -34,4 +34,4 @@
|
||||
%end;
|
||||
%end;
|
||||
%put unable to find available fileref in range &prefix.0-&maxtries;
|
||||
%mend;
|
||||
%mend mf_getuniquefileref;
|
||||
@@ -37,4 +37,4 @@
|
||||
%end;
|
||||
%end;
|
||||
%put unable to find available libref in range &prefix.0-&maxtries;
|
||||
%mend;
|
||||
%mend mf_getuniquelibref;
|
||||
@@ -39,4 +39,4 @@
|
||||
|
||||
%quote(&user)
|
||||
|
||||
%mend;
|
||||
%mend mf_getuser;
|
||||
|
||||
@@ -30,4 +30,4 @@
|
||||
%trim(&&&variable)
|
||||
|
||||
%end;
|
||||
%mend;
|
||||
%mend mf_getvalue;
|
||||
@@ -29,4 +29,4 @@
|
||||
%let rc=%sysfunc(close(&dsid));
|
||||
%end;
|
||||
&nvars
|
||||
%mend;
|
||||
%mend mf_getvarcount;
|
||||
@@ -49,4 +49,4 @@
|
||||
%let rc = %sysfunc(close(&dsid));
|
||||
/* Return variable format */
|
||||
&vlen
|
||||
%mend;
|
||||
%mend mf_getVarLen;
|
||||
@@ -66,7 +66,8 @@
|
||||
%let rc=%sysfunc(close(&dsid));
|
||||
%end;
|
||||
%else %do;
|
||||
%put unable to open &libds (rc=&dsid);
|
||||
%put &sysmacroname: Unable to open &libds (rc=&dsid);
|
||||
%put &sysmacroname: SYSMSG= %sysfunc(sysmsg());
|
||||
%let rc=%sysfunc(close(&dsid));
|
||||
%end;
|
||||
&outvar
|
||||
|
||||
@@ -51,4 +51,4 @@ returns:
|
||||
/* Return variable number */
|
||||
&vnum.
|
||||
|
||||
%mend;
|
||||
%mend mf_getVarNum;
|
||||
@@ -40,4 +40,4 @@
|
||||
|
||||
&engine
|
||||
|
||||
%mend;
|
||||
%mend mf_getxengine;
|
||||
|
||||
@@ -24,4 +24,4 @@
|
||||
|
||||
%sysevalf(%superq(param)=,boolean)
|
||||
|
||||
%mend;
|
||||
%mend mf_isblank;
|
||||
@@ -31,4 +31,4 @@
|
||||
|
||||
&is_directory
|
||||
|
||||
%mend;
|
||||
%mend mf_isdir;
|
||||
@@ -26,4 +26,4 @@
|
||||
&root
|
||||
%end;
|
||||
|
||||
%mend;
|
||||
%mend mf_loc;
|
||||
|
||||
@@ -64,4 +64,4 @@ Usage:
|
||||
%end;
|
||||
%end;
|
||||
/* exit quietly if directory did exist.*/
|
||||
%mend;
|
||||
%mend mf_mkdir;
|
||||
|
||||
@@ -16,4 +16,4 @@
|
||||
%if %symexist(&var) %then %do;
|
||||
%superq(&var)
|
||||
%end;
|
||||
%mend;
|
||||
%mend mf_mval;
|
||||
|
||||
@@ -23,4 +23,4 @@
|
||||
%macro mf_nobs(libds
|
||||
)/*/STORE SOURCE*/;
|
||||
%mf_getattrn(&libds,NLOBS)
|
||||
%mend;
|
||||
%mend mf_nobs;
|
||||
@@ -47,4 +47,4 @@
|
||||
&basestr
|
||||
%end;
|
||||
|
||||
%mend;
|
||||
%mend mf_trimstr;
|
||||
@@ -62,4 +62,4 @@
|
||||
%else %mf_abort(mac=mf_verifymacvars,type=&mabort,msg=&abortmsg);
|
||||
%exit_success:
|
||||
|
||||
%mend;
|
||||
%mend mf_verifymacvars;
|
||||
|
||||
@@ -50,5 +50,5 @@
|
||||
|
||||
&outvar
|
||||
|
||||
%mend;
|
||||
%mend mf_wordsInStr1ButNotStr2;
|
||||
|
||||
|
||||
@@ -4,13 +4,24 @@
|
||||
@details Configures an abort mechanism according to site specific policies or
|
||||
the particulars of an environment. For instance, can stream custom
|
||||
results back to the client in an STP Web App context, or completely stop
|
||||
in the case of a batch run.
|
||||
in the case of a batch run. For STP sessions
|
||||
|
||||
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
|
||||
cause hung multibridge sessions and result in a frozen STP server
|
||||
@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
|
||||
recognise this and fetch the log of the parent session instead)
|
||||
@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)
|
||||
and set SYSCC=0. For 9.4m3 we take a unique approach - we open a macro
|
||||
but don't close it! This provides a graceful abort, EXCEPT when called
|
||||
called within a %include within a macro (and that macro contains additional
|
||||
logic). See mp_abort.test.nofix.sas for the example case.
|
||||
If you know of another way to gracefully abort a 9.4m3 STP session, we'd
|
||||
love to hear about it!
|
||||
|
||||
Using SAS Abort Cancel mechanisms can cause hung sessions in some Stored
|
||||
Process environments. This macro takes a unique approach - we set the SAS
|
||||
syscc to 0, run `stpsrvset('program error', 0)` (if SAS 9) and then - we open
|
||||
a macro but don't close it! This provides a graceful abort for SAS web
|
||||
services in all web enabled environments.
|
||||
|
||||
@param mac= to contain the name of the calling macro
|
||||
@param msg= message to be returned
|
||||
@@ -24,6 +35,8 @@
|
||||
%macro mp_abort(mac=mp_abort.sas, type=, msg=, iftrue=%str(1=1)
|
||||
)/*/STORE SOURCE*/;
|
||||
|
||||
%global sysprocessmode sysprocessname;
|
||||
|
||||
%if not(%eval(%unquote(&iftrue))) %then %return;
|
||||
|
||||
%put NOTE: /// mp_abort macro executing //;
|
||||
@@ -31,9 +44,7 @@
|
||||
%put NOTE - &msg;
|
||||
|
||||
/* Stored Process Server web app context */
|
||||
%if %symexist(_metaperson)
|
||||
or (%symexist(SYSPROCESSNAME) and "&SYSPROCESSNAME"="Compute Server" )
|
||||
%then %do;
|
||||
%if %symexist(_metaperson) or "&SYSPROCESSNAME "="Compute Server " %then %do;
|
||||
options obs=max replace nosyntaxcheck mprint;
|
||||
/* extract log errs / warns, if exist */
|
||||
%local logloc logline;
|
||||
@@ -63,7 +74,7 @@
|
||||
input;
|
||||
i=1;
|
||||
stoploop=0;
|
||||
if _n_ ge &logline-5 and stoploop=0 then do until (i>12);
|
||||
if _n_ ge &logline-15 and stoploop=0 then do until (i>22);
|
||||
call symputx('logmsg',catx('\n',symget('logmsg'),_infile_));
|
||||
input;
|
||||
i+1;
|
||||
@@ -88,7 +99,7 @@
|
||||
|
||||
/* send response in SASjs JSON format */
|
||||
data _null_;
|
||||
file _webout mod lrecl=32000;
|
||||
file _webout mod lrecl=32000 encoding='utf-8';
|
||||
length msg $32767 debug $8;
|
||||
sasdatetime=datetime();
|
||||
msg=cats(symget('msg'),'\n\nLog Extract:\n',symget('logmsg'));
|
||||
@@ -122,37 +133,68 @@
|
||||
put ",""SYSCC"" : ""&syscc"" ";
|
||||
put ",""SYSERRORTEXT"" : ""&syserrortext"" ";
|
||||
put ",""SYSJOBID"" : ""&sysjobid"" ";
|
||||
sysvlong=quote(trim(symget('sysvlong')));
|
||||
put ',"SYSVLONG" : ' sysvlong;
|
||||
put ",""SYSWARNINGTEXT"" : ""&syswarningtext"" ";
|
||||
put ',"END_DTTM" : "' "%sysfunc(datetime(),datetime20.3)" '" ';
|
||||
put "}" @;
|
||||
if debug ge '"131"' then put '>>weboutEND<<';
|
||||
run;
|
||||
|
||||
%let syscc=0;
|
||||
%if %symexist(_metaport) %then %do;
|
||||
%put _all_;
|
||||
|
||||
%if "&sysprocessmode " = "SAS Stored Process Server " %then %do;
|
||||
data _null_;
|
||||
if symexist('sysprocessmode')
|
||||
then if symget("sysprocessmode")="SAS Stored Process Server"
|
||||
then rc=stpsrvset('program error', 0);
|
||||
putlog 'stpsrvset program error and syscc';
|
||||
rc=stpsrvset('program error', 0);
|
||||
call symputx("syscc",0,"g");
|
||||
run;
|
||||
%if "%substr(&sysvlong.xxxxxxxxx,1,9)" ne "9.04.01M3" %then %do;
|
||||
%put NOTE: Ending SAS session due to:;
|
||||
%put NOTE- &msg;
|
||||
endsas;
|
||||
%end;
|
||||
%end;
|
||||
%else %if "&sysprocessmode " = "SAS Compute Server " %then %do;
|
||||
/* endsas kills the session making it harder to fetch results */
|
||||
data _null_;
|
||||
syswarningtext=symget('syswarningtext');
|
||||
syserrortext=symget('syserrortext');
|
||||
abort_msg=symget('msg');
|
||||
syscc=symget('syscc');
|
||||
sysuserid=symget('sysuserid');
|
||||
iftrue=symget('iftrue');
|
||||
put (_all_)(/=);
|
||||
call symputx('syscc',0);
|
||||
abort cancel nolist;
|
||||
run;
|
||||
%end;
|
||||
/**
|
||||
* endsas is reliable but kills some deployments.
|
||||
* Abort variants are ungraceful (non zero return code)
|
||||
* This approach lets SAS run silently until the end :-)
|
||||
*/
|
||||
%put _all_;
|
||||
filename skip temp;
|
||||
data _null_;
|
||||
file skip;
|
||||
put '%macro skip(); %macro skippy();';
|
||||
run;
|
||||
%inc skip;
|
||||
%else %if "%substr(&sysvlong.xxxxxxxxx,1,9)" = "9.04.01M3" %then %do;
|
||||
/**
|
||||
* endsas kills 9.4m3 deployments by orphaning multibridges.
|
||||
* Abort variants are ungraceful (non zero return code)
|
||||
* This approach lets SAS run silently until the end :-)
|
||||
* Caution - fails when called within a %include within a macro
|
||||
* See tests/mp_abort.test.1 for an example case.
|
||||
*/
|
||||
filename skip temp;
|
||||
data _null_;
|
||||
file skip;
|
||||
put '%macro skip();';
|
||||
comment '%mend skip; -> fix lint ';
|
||||
put '%macro skippy();';
|
||||
comment '%mend skippy; -> fix lint ';
|
||||
run;
|
||||
%inc skip;
|
||||
%end;
|
||||
%else %do;
|
||||
%abort cancel;
|
||||
%end;
|
||||
%end;
|
||||
%else %do;
|
||||
%put _all_;
|
||||
%abort cancel;
|
||||
%end;
|
||||
%mend;
|
||||
%mend mp_abort;
|
||||
|
||||
/** @endcond */
|
||||
@@ -142,4 +142,4 @@
|
||||
proc sql;
|
||||
drop table &ds;
|
||||
|
||||
%mend;
|
||||
%mend mp_assertcols;
|
||||
@@ -144,4 +144,4 @@
|
||||
proc sql;
|
||||
drop table &ds;
|
||||
|
||||
%mend;
|
||||
%mend mp_assertcolvals;
|
||||
@@ -18,7 +18,11 @@
|
||||
@param [in] test= (HASOBS) The test to apply. Valid values are:
|
||||
@li HASOBS - Test is a PASS if the input dataset has any observations
|
||||
@li EMPTY - Test is a PASS if input dataset is empty
|
||||
@li EQUALS [integer] - Test passes if obs count matches the provided integer
|
||||
@li EQUALS [integer] - Test passes if row count matches the provided integer
|
||||
@LI ATLEAST [integer] - Test passes if row count is more than or equal to
|
||||
the provided integer
|
||||
@LI ATMOST [integer] - Test passes if row count is less than or equal to
|
||||
the provided integer
|
||||
@param [out] outds= (work.test_results) The output dataset to contain the
|
||||
results. If it does not exist, it will be created, with the following format:
|
||||
|TEST_DESCRIPTION:$256|TEST_RESULT:$4|TEST_COMMENTS:$256|
|
||||
@@ -27,6 +31,8 @@
|
||||
|
||||
<h4> Related Macros </h4>
|
||||
@li mp_assertcolvals.sas
|
||||
@li mp_assert.sas
|
||||
@li mp_assertcols.sas
|
||||
|
||||
@version 9.2
|
||||
@author Allan Bowe
|
||||
@@ -51,6 +57,22 @@
|
||||
)
|
||||
%let test=EQUALS;
|
||||
%end;
|
||||
%else %if %substr(&test.xxxxxxx,1,7)=ATLEAST %then %do;
|
||||
%let val=%scan(&test,2,%str( ));
|
||||
%mp_abort(iftrue= (%DATATYP(&val)=CHAR)
|
||||
,mac=&sysmacroname
|
||||
,msg=%str(Invalid test - &test, expected ATLEAST [integer])
|
||||
)
|
||||
%let test=ATLEAST;
|
||||
%end;
|
||||
%else %if %substr(&test.xxxxxxx,1,7)=ATMOST %then %do;
|
||||
%let val=%scan(&test,2,%str( ));
|
||||
%mp_abort(iftrue= (%DATATYP(&val)=CHAR)
|
||||
,mac=&sysmacroname
|
||||
,msg=%str(Invalid test - &test, expected ATMOST [integer])
|
||||
)
|
||||
%let test=ATMOST;
|
||||
%end;
|
||||
%else %if &test ne HASOBS and &test ne EMPTY %then %do;
|
||||
%mp_abort(
|
||||
mac=&sysmacroname,
|
||||
@@ -62,7 +84,8 @@
|
||||
length test_description $256 test_result $4 test_comments $256;
|
||||
test_description=symget('desc');
|
||||
test_result='FAIL';
|
||||
test_comments="&sysmacroname: Dataset &inds has &nobs observations";
|
||||
test_comments="&sysmacroname: Dataset &inds has &nobs observations.";
|
||||
test_comments=test_comments!!" Test was "!!symget('test');
|
||||
%if &test=HASOBS %then %do;
|
||||
if &nobs>0 then test_result='PASS';
|
||||
%end;
|
||||
@@ -72,6 +95,12 @@
|
||||
%else %if &test=EQUALS %then %do;
|
||||
if &nobs=&val then test_result='PASS';
|
||||
%end;
|
||||
%else %if &test=ATLEAST %then %do;
|
||||
if &nobs ge &val then test_result='PASS';
|
||||
%end;
|
||||
%else %if &test=ATMOST %then %do;
|
||||
if &nobs le &val then test_result='PASS';
|
||||
%end;
|
||||
%else %do;
|
||||
test_comments="&sysmacroname: Unsatisfied test condition - &test";
|
||||
%end;
|
||||
@@ -86,4 +115,4 @@
|
||||
proc sql;
|
||||
drop table &ds;
|
||||
|
||||
%mend;
|
||||
%mend mp_assertdsobs;
|
||||
123
base/mp_base64copy.sas
Normal file
123
base/mp_base64copy.sas
Normal file
@@ -0,0 +1,123 @@
|
||||
/**
|
||||
@file
|
||||
@brief Convert a file to/from base64 format
|
||||
@details Creates a new version of a file either encoded or decoded using
|
||||
Base64. Inspired by this post by Michael Dixon:
|
||||
https://support.selerity.com.au/hc/en-us/articles/223345708-Tip-SAS-and-Base64
|
||||
|
||||
Usage:
|
||||
|
||||
filename tmp temp;
|
||||
data _null_;
|
||||
file tmp;
|
||||
put 'base ik ally';
|
||||
run;
|
||||
%mp_base64copy(inref=tmp, outref=myref, action=ENCODE)
|
||||
|
||||
data _null_;
|
||||
infile myref;
|
||||
input;
|
||||
put _infile_;
|
||||
run;
|
||||
|
||||
%mp_base64copy(inref=myref, outref=mynewref, action=DECODE)
|
||||
|
||||
data _null_;
|
||||
infile mynewref;
|
||||
input;
|
||||
put _infile_;
|
||||
run;
|
||||
|
||||
@param [in] inref= Fileref of the input file (should exist)
|
||||
@param [out] outref= Output fileref. If it does not exist, it is created.
|
||||
@param [in] action= (ENCODE) The action to take. Valid values:
|
||||
@li ENCODE - Convert the file to base64 format
|
||||
@li DECODE - Decode the file from base64 format
|
||||
|
||||
@version 9.2
|
||||
@author Allan Bowe, source: https://github.com/sasjs/core
|
||||
|
||||
<h4> SAS Macros </h4>
|
||||
@li mp_abort.sas
|
||||
|
||||
|
||||
**/
|
||||
|
||||
%macro mp_base64copy(
|
||||
inref=0,
|
||||
outref=0,
|
||||
action=ENCODE
|
||||
)/*/STORE SOURCE*/;
|
||||
|
||||
%let inref=%upcase(&inref);
|
||||
%let outref=%upcase(&outref);
|
||||
%let action=%upcase(&action);
|
||||
%local infound outfound;
|
||||
%let infound=0;
|
||||
%let outfound=0;
|
||||
data _null_;
|
||||
set sashelp.vextfl(where=(fileref="&inref" or fileref="&outref"));
|
||||
if fileref="&inref" then call symputx('infound',1,'l');
|
||||
if fileref="&outref" then call symputx('outfound',1,'l');
|
||||
run;
|
||||
|
||||
%mp_abort(iftrue= (&infound=0)
|
||||
,mac=&sysmacroname
|
||||
,msg=%str(INREF &inref NOT FOUND!)
|
||||
)
|
||||
%mp_abort(iftrue= (&outref=0)
|
||||
,mac=&sysmacroname
|
||||
,msg=%str(OUTREF NOT PROVIDED!)
|
||||
)
|
||||
%mp_abort(iftrue= (&action ne ENCODE and &action ne DECODE)
|
||||
,mac=&sysmacroname
|
||||
,msg=%str(Invalid action! Should be ENCODE OR DECODE)
|
||||
)
|
||||
|
||||
%if &outfound=0 %then %do;
|
||||
filename &outref temp lrecl=2097088;
|
||||
%%end;
|
||||
|
||||
%if &action=ENCODE %then %do;
|
||||
data _null_;
|
||||
length b64 $ 76 line $ 57;
|
||||
retain line "";
|
||||
infile &inref recfm=F lrecl= 1 end=eof;
|
||||
input @1 stream $char1.;
|
||||
file &outref lrecl=76;
|
||||
substr(line,(_N_-(CEIL(_N_/57)-1)*57),1) = byte(rank(stream));
|
||||
if mod(_N_,57)=0 or EOF then do;
|
||||
if eof then b64=put(trim(line),$base64X76.);
|
||||
else b64=put(line, $base64X76.);
|
||||
put b64;
|
||||
line="";
|
||||
end;
|
||||
run;
|
||||
%end;
|
||||
%else %if &action=DECODE %then %do;
|
||||
data _null_;
|
||||
length filein 8 fileout 8;
|
||||
filein = fopen("&inref",'I',4,'B');
|
||||
fileout = fopen("&outref",'O',3,'B');
|
||||
char= '20'x;
|
||||
do while(fread(filein)=0);
|
||||
raw="1234";
|
||||
do i=1 to 4;
|
||||
rc=fget(filein,char,1);
|
||||
substr(raw,i,1)=char;
|
||||
end;
|
||||
val="123";
|
||||
val=input(raw,$base64X4.);
|
||||
do i=1 to 3;
|
||||
length byte $1;
|
||||
byte=byte(rank(substr(val,i,1)));
|
||||
rc = fput(fileout, byte);
|
||||
end;
|
||||
rc =fwrite(fileout);
|
||||
end;
|
||||
rc = fclose(filein);
|
||||
rc = fclose(fileout);
|
||||
run;
|
||||
%end;
|
||||
|
||||
%mend mp_base64copy;
|
||||
@@ -53,4 +53,4 @@
|
||||
%if &outref=____out %then %do;
|
||||
filename &outref clear;
|
||||
%end;
|
||||
%mend;
|
||||
%mend mp_binarycopy;
|
||||
@@ -67,5 +67,5 @@
|
||||
else put inchar $char1.;
|
||||
end;
|
||||
run;
|
||||
%mend;
|
||||
%mend mp_cleancsv;
|
||||
/** @endcond */
|
||||
@@ -64,4 +64,4 @@ data &outds;
|
||||
output;
|
||||
run;
|
||||
|
||||
%mend;
|
||||
%mend mp_createconstraints;
|
||||
@@ -80,4 +80,4 @@ Usage:
|
||||
)
|
||||
%end;
|
||||
|
||||
%mend;
|
||||
%mend mp_createwebservice;
|
||||
|
||||
@@ -141,4 +141,4 @@ data &outds
|
||||
%end;
|
||||
run;
|
||||
|
||||
%mend;
|
||||
%mend mp_csv2ds;
|
||||
@@ -49,4 +49,4 @@ data &outds;
|
||||
end;
|
||||
run;
|
||||
|
||||
%mend;
|
||||
%mend mp_deleteconstraints;
|
||||
@@ -167,4 +167,4 @@ run;
|
||||
by filepath file_or_folder filename ext ;
|
||||
run;
|
||||
%end;
|
||||
%mend;
|
||||
%mend mp_dirlist;
|
||||
@@ -47,4 +47,4 @@
|
||||
%end;
|
||||
as &outvar length=&varlen
|
||||
from &libds;
|
||||
%mend;
|
||||
%mend mp_distinctfmtvalues;
|
||||
@@ -1,20 +1,24 @@
|
||||
/**
|
||||
@file
|
||||
@brief Drops tables / views (if they exist) without warnings in the log
|
||||
@details
|
||||
@details Useful for dropping tables when you're not sure they exist, or if
|
||||
you are not sure whether they are a dataset or view. Also efficient for
|
||||
dropping multiple tables / views.
|
||||
|
||||
Example usage:
|
||||
|
||||
proc sql;
|
||||
create table data1 as select * from sashelp.class;
|
||||
create view view2 as select * from sashelp.class;
|
||||
%mp_dropmembers(list=data1 view2)
|
||||
%mp_dropmembers(data1 view2, libref=WORK)
|
||||
|
||||
|
||||
<h4> SAS Macros </h4>
|
||||
@li mf_isblank.sas
|
||||
|
||||
|
||||
@param list space separated list of datasets / views
|
||||
@param libref= can only drop from a single library at a time
|
||||
@param list space separated list of datasets / views, WITHOUT libref
|
||||
@param libref= (WORK) Note - you can only drop from a single library at a time
|
||||
|
||||
@version 9.2
|
||||
@author Allan Bowe
|
||||
@@ -35,4 +39,4 @@
|
||||
delete &list;
|
||||
delete &list /mtype=view;
|
||||
run;
|
||||
%mend;
|
||||
%mend mp_dropmembers;
|
||||
@@ -251,4 +251,4 @@ quit;
|
||||
%put NOTE-;%put NOTE-;
|
||||
%put NOTE- %sysfunc(dequote(&cards_file.));
|
||||
%put NOTE-;%put NOTE-;
|
||||
%mend;
|
||||
%mend mp_ds2cards;
|
||||
@@ -55,4 +55,4 @@ data _null_;
|
||||
run;
|
||||
|
||||
|
||||
%mend;
|
||||
%mend mp_ds2csv;
|
||||
@@ -6,11 +6,14 @@
|
||||
|
||||
Usage:
|
||||
|
||||
%mp_ds2fmtds(sashelp.cars,work.cards)
|
||||
%mp_ds2fmtds(sashelp.cars,work.cars)
|
||||
|
||||
@param [in] libds The library.dataset to be converted
|
||||
@param [out] outds The dataset to create.
|
||||
|
||||
<h4> Related Macros <h4>
|
||||
@li mp_jsonout.sas
|
||||
|
||||
@version 9.2
|
||||
@author Allan Bowe
|
||||
**/
|
||||
|
||||
@@ -33,19 +33,19 @@
|
||||
@returns The &outds table containing any bad rows, plus a REASON_CD column.
|
||||
|
||||
@param [in] inds The table to be checked, with the format above
|
||||
@param [in] targetds= The target dataset against which to verify VARIABLE_NM
|
||||
@param [in] targetds= The target dataset against which to verify VARIABLE_NM.
|
||||
This must be available (ie, the library must be assigned).
|
||||
@param [out] abort= (YES) If YES will call mp_abort.sas on any exceptions
|
||||
@param [out] outds= The output table, which is a copy of the &inds. table
|
||||
plus a REASON_CD column, containing only bad records. If bad records found,
|
||||
the SYSCC value will be set to 1008 (general data problem). Downstream
|
||||
processes should check this table (and return code) before continuing.
|
||||
plus a REASON_CD column, containing only bad records. If bad records found,
|
||||
the SYSCC value will be set to 1008 (general data problem). Downstream
|
||||
processes should check this table (and return code) before continuing.
|
||||
|
||||
<h4> SAS Macros </h4>
|
||||
@li mp_abort.sas
|
||||
@li mf_getuniquefileref.sas
|
||||
@li mf_getvarlist.sas
|
||||
@li mf_getvartype.sas
|
||||
@li mf_nobs.sas
|
||||
@li mp_filtergenerate.sas
|
||||
@li mp_filtervalidate.sas
|
||||
|
||||
@@ -85,41 +85,52 @@
|
||||
* quotes, commas, periods and spaces.
|
||||
* Only numeric values should remain
|
||||
*/
|
||||
%local reason_cd;
|
||||
%local reason_cd nobs;
|
||||
%let nobs=0;
|
||||
data &outds;
|
||||
/*length GROUP_LOGIC SUBGROUP_LOGIC $3 SUBGROUP_ID 8 VARIABLE_NM $32
|
||||
OPERATOR_NM $10 RAW_VALUE $4000;*/
|
||||
set &inds;
|
||||
length reason_cd $32;
|
||||
length reason_cd $4032;
|
||||
|
||||
/* closed list checks */
|
||||
if GROUP_LOGIC not in ('AND','OR') then do;
|
||||
REASON_CD='GROUP_LOGIC should be either AND or OR';
|
||||
REASON_CD='GROUP_LOGIC should be AND/OR, not:'!!cats(GROUP_LOGIC);
|
||||
putlog REASON_CD= GROUP_LOGIC=;
|
||||
call symputx('reason_cd',reason_cd,'l');
|
||||
call symputx('nobs',_n_,'l');
|
||||
output;
|
||||
end;
|
||||
if SUBGROUP_LOGIC not in ('AND','OR') then do;
|
||||
REASON_CD='SUBGROUP_LOGIC should be either AND or OR';
|
||||
REASON_CD='SUBGROUP_LOGIC should be AND/OR, not:'!!cats(SUBGROUP_LOGIC);
|
||||
putlog REASON_CD= SUBGROUP_LOGIC=;
|
||||
call symputx('reason_cd',reason_cd,'l');
|
||||
call symputx('nobs',_n_,'l');
|
||||
output;
|
||||
end;
|
||||
if mod(SUBGROUP_ID,1) ne 0 then do;
|
||||
REASON_CD='SUBGROUP_ID should be integer';
|
||||
REASON_CD='SUBGROUP_ID should be integer, not '!!left(subgroup_id);
|
||||
putlog REASON_CD= SUBGROUP_ID=;
|
||||
call symputx('reason_cd',reason_cd,'l');
|
||||
call symputx('nobs',_n_,'l');
|
||||
output;
|
||||
end;
|
||||
if upcase(VARIABLE_NM) not in
|
||||
(%upcase(%mf_getvarlist(&targetds,dlm=%str(,),quote=SINGLE)))
|
||||
then do;
|
||||
REASON_CD="VARIABLE_NM not in &targetds";
|
||||
REASON_CD="Variable "!!cats(variable_nm)!!" not in &targetds";
|
||||
putlog REASON_CD= VARIABLE_NM=;
|
||||
call symputx('reason_cd',reason_cd,'l');
|
||||
call symputx('nobs',_n_,'l');
|
||||
output;
|
||||
end;
|
||||
if OPERATOR_NM not in
|
||||
('=','>','<','<=','>=','BETWEEN','IN','NOT IN','NE','CONTAINS')
|
||||
then do;
|
||||
REASON_CD='Invalid OPERATOR_NM';
|
||||
REASON_CD='Invalid OPERATOR_NM: '!!left(OPERATOR_NM);
|
||||
putlog REASON_CD= OPERATOR_NM=;
|
||||
call symputx('reason_cd',reason_cd,'l');
|
||||
call symputx('nobs',_n_,'l');
|
||||
output;
|
||||
end;
|
||||
|
||||
@@ -129,8 +140,10 @@ data &outds;
|
||||
if substr(raw_value,1,1) ne '('
|
||||
or substr(cats(reverse(raw_value)),1,1) ne ')'
|
||||
then do;
|
||||
REASON_CD='Missing brackets in RAW_VALUE';
|
||||
REASON_CD='Missing start/end bracket in RAW_VALUE';
|
||||
putlog REASON_CD= OPERATOR_NM= raw_value= raw_value1= ;
|
||||
call symputx('reason_cd',reason_cd,'l');
|
||||
call symputx('nobs',_n_,'l');
|
||||
output;
|
||||
end;
|
||||
else raw_value1=substr(raw_value,2,max(length(raw_value)-2,0));
|
||||
@@ -151,25 +164,27 @@ data &outds;
|
||||
/* output records that contain values other than digits and spaces */
|
||||
if notdigit(compress(raw_value3,' '))>0 then do;
|
||||
putlog raw_value3= $hex32.;
|
||||
REASON_CD='Invalid RAW_VALUE';
|
||||
REASON_CD=cats('Invalid RAW_VALUE:',raw_value);
|
||||
putlog REASON_CD= raw_value= raw_value1= raw_value2= raw_value3=;
|
||||
call symputx('reason_cd',reason_cd,'l');
|
||||
call symputx('nobs',_n_,'l');
|
||||
output;
|
||||
end;
|
||||
|
||||
run;
|
||||
|
||||
|
||||
data _null_;
|
||||
set &outds;
|
||||
call symputx('REASON_CD',reason_cd,'l');
|
||||
stop;
|
||||
set &outds end=last;
|
||||
putlog (_all_)(=);
|
||||
run;
|
||||
|
||||
%mp_abort(iftrue=(&abort=YES and %mf_nobs(&outds)>0),
|
||||
%mp_abort(iftrue=(&abort=YES and &nobs>0),
|
||||
mac=&sysmacroname,
|
||||
msg=%str(Filter issues in &inds, reason: &reason_cd, details in &outds)
|
||||
msg=%str(Data issue: %superq(reason_cd))
|
||||
)
|
||||
|
||||
%if %mf_nobs(&outds)>0 %then %do;
|
||||
%if &nobs>0 %then %do;
|
||||
%let syscc=1008;
|
||||
%return;
|
||||
%end;
|
||||
|
||||
@@ -99,4 +99,4 @@ filename &outref temp;
|
||||
run;
|
||||
%end;
|
||||
|
||||
%mend;
|
||||
%mend mp_filtergenerate;
|
||||
|
||||
@@ -78,7 +78,8 @@ run;
|
||||
|
||||
data &outds;
|
||||
if &sqlrc or &syscc or &syserr then do;
|
||||
REASON_CD=coalescec(symget('SYSERRORTEXT'),symget('SYSWARNINGTEXT'));
|
||||
REASON_CD='VALIDATION_ERROR: '!!
|
||||
coalescec(symget('SYSERRORTEXT'),symget('SYSWARNINGTEXT'));
|
||||
output;
|
||||
end;
|
||||
else stop;
|
||||
@@ -95,10 +96,11 @@ filename &fref1 clear;
|
||||
run;
|
||||
%mp_abort(
|
||||
mac=&sysmacroname,
|
||||
msg=%str(Filter issues in &inref: %quote(&reason_cd))
|
||||
msg=%str(Filter validation issues. ERR=%superq(SYSERRORTEXT)
|
||||
, WARN=%superq(SYSWARNINGTEXT) )
|
||||
)
|
||||
%end;
|
||||
%let syscc=1008;
|
||||
%end;
|
||||
|
||||
%mend;
|
||||
%mend mp_filtervalidate;
|
||||
|
||||
@@ -57,4 +57,4 @@ create table &outds as
|
||||
%end;
|
||||
;
|
||||
|
||||
%mend;
|
||||
%mend mp_getconstraints;
|
||||
@@ -332,4 +332,4 @@ run;
|
||||
run;
|
||||
%end;
|
||||
|
||||
%mend;
|
||||
%mend mp_getdbml;
|
||||
@@ -115,7 +115,7 @@ create table _data_ as
|
||||
end;
|
||||
run;
|
||||
%put &=constraints_used;
|
||||
%mend;
|
||||
%mend addConst;
|
||||
|
||||
data _null_;
|
||||
file &fref;
|
||||
@@ -378,4 +378,4 @@ run;
|
||||
run;
|
||||
%end;
|
||||
|
||||
%mend;
|
||||
%mend mp_getddl;
|
||||
@@ -70,4 +70,4 @@ create table &outds (rename=(
|
||||
out=&outds(rename=(_name_=NAME COL1=MAXLEN));
|
||||
run;
|
||||
|
||||
%mend;
|
||||
%mend mp_getmaxvarlengths;
|
||||
@@ -301,4 +301,4 @@
|
||||
%return;
|
||||
%end;
|
||||
|
||||
%mend;
|
||||
%mend mp_guesspk;
|
||||
@@ -72,4 +72,4 @@
|
||||
if &lastvar then output;
|
||||
run;
|
||||
%end;
|
||||
%mend;
|
||||
%mend mp_hashdataset;
|
||||
@@ -48,6 +48,8 @@
|
||||
@param dbg= DEPRECATED - was used to conditionally add PRETTY to
|
||||
proc json but this can cause line truncation in large files.
|
||||
|
||||
<h4> Related Macros <h4>
|
||||
@li mp_ds2fmtds.sas
|
||||
|
||||
@version 9.2
|
||||
@author Allan Bowe
|
||||
@@ -55,10 +57,11 @@
|
||||
|
||||
**/
|
||||
|
||||
%macro mp_jsonout(action,ds,jref=_webout,dslabel=,fmt=Y,engine=PROCJSON,dbg=0
|
||||
%macro mp_jsonout(action,ds,jref=_webout,dslabel=,fmt=Y,engine=DATASTEP,dbg=0
|
||||
)/*/STORE SOURCE*/;
|
||||
%put output location=&jref;
|
||||
%if &action=OPEN %then %do;
|
||||
options nobomfile;
|
||||
data _null_;file &jref encoding='utf-8';
|
||||
put '{"START_DTTM" : "' "%sysfunc(datetime(),datetime20.3)" '"';
|
||||
run;
|
||||
@@ -86,7 +89,65 @@
|
||||
%put &sysmacroname: &ds NOT FOUND!!!;
|
||||
%return;
|
||||
%end;
|
||||
data _null_;file &jref mod ;
|
||||
%if &fmt=Y %then %do;
|
||||
%put converting every variable to a formatted variable;
|
||||
/* see mp_ds2fmtds.sas for source */
|
||||
proc contents noprint data=&ds
|
||||
out=_data_(keep=name type length format formatl formatd varnum);
|
||||
run;
|
||||
proc sort;
|
||||
by varnum;
|
||||
run;
|
||||
%local fmtds;
|
||||
%let fmtds=%scan(&syslast,2,.);
|
||||
/* prepare formats and varnames */
|
||||
data _null_;
|
||||
if _n_=1 then call symputx('nobs',nobs,'l');
|
||||
set &fmtds end=last nobs=nobs;
|
||||
name=upcase(name);
|
||||
/* fix formats */
|
||||
if type=2 or type=6 then do;
|
||||
length fmt $49.;
|
||||
if format='' then fmt=cats('$',length,'.');
|
||||
else if formatl=0 then fmt=cats(format,'.');
|
||||
else fmt=cats(format,formatl,'.');
|
||||
newlen=max(formatl,length);
|
||||
end;
|
||||
else do;
|
||||
if format='' then fmt='best.';
|
||||
else if formatl=0 then fmt=cats(format,'.');
|
||||
else if formatd=0 then fmt=cats(format,formatl,'.');
|
||||
else fmt=cats(format,formatl,'.',formatd);
|
||||
/* needs to be wide, for datetimes etc */
|
||||
newlen=max(length,formatl,24);
|
||||
end;
|
||||
/* 32 char unique name */
|
||||
newname='sasjs'!!substr(cats(put(md5(name),$hex32.)),1,27);
|
||||
|
||||
call symputx(cats('name',_n_),name,'l');
|
||||
call symputx(cats('newname',_n_),newname,'l');
|
||||
call symputx(cats('len',_n_),newlen,'l');
|
||||
call symputx(cats('fmt',_n_),fmt,'l');
|
||||
call symputx(cats('type',_n_),type,'l');
|
||||
run;
|
||||
data &fmtds;
|
||||
/* rename on entry */
|
||||
set &ds(rename=(
|
||||
%local i;
|
||||
%do i=1 %to &nobs;
|
||||
&&name&i=&&newname&i
|
||||
%end;
|
||||
));
|
||||
%do i=1 %to &nobs;
|
||||
length &&name&i $&&len&i;
|
||||
&&name&i=left(put(&&newname&i,&&fmt&i));
|
||||
drop &&newname&i;
|
||||
%end;
|
||||
if _error_ then call symputx('syscc',1012);
|
||||
run;
|
||||
%let ds=&fmtds;
|
||||
%end; /* &fmt=Y */
|
||||
data _null_;file &jref mod encoding='utf-8';
|
||||
put "["; call symputx('cols',0,'l');
|
||||
proc sort
|
||||
data=sashelp.vcolumn(where=(libname='WORK' & memname="%upcase(&ds)"))
|
||||
@@ -169,4 +230,4 @@
|
||||
put "}";
|
||||
run;
|
||||
%end;
|
||||
%mend;
|
||||
%mend mp_jsonout;
|
||||
|
||||
@@ -75,4 +75,4 @@ select distinct lowcase(memname)
|
||||
)
|
||||
%end;
|
||||
|
||||
%mend;
|
||||
%mend mp_lib2cards;
|
||||
@@ -39,4 +39,4 @@
|
||||
,dttm=%sysfunc(datetime());
|
||||
quit;
|
||||
|
||||
%mend;
|
||||
%mend mp_perflog;
|
||||
@@ -85,4 +85,4 @@
|
||||
"with record &record and " _n_=;
|
||||
%end;
|
||||
|
||||
%mend;
|
||||
%mend mp_prevobs;
|
||||
@@ -87,4 +87,4 @@ insert into &outds select distinct * from &append_ds;
|
||||
)
|
||||
%end;
|
||||
|
||||
%mend;
|
||||
%mend mp_recursivejoin;
|
||||
|
||||
@@ -30,4 +30,4 @@ data _null_;
|
||||
end;
|
||||
run;
|
||||
|
||||
%mend;
|
||||
%mend mp_resetoption;
|
||||
@@ -46,4 +46,4 @@
|
||||
|
||||
|
||||
|
||||
%mend;
|
||||
%mend mp_runddl;
|
||||
@@ -9,7 +9,15 @@
|
||||
|
||||
%mp_searchcols(libs=sashelp work, cols=name sex age)
|
||||
|
||||
@param libs=
|
||||
@param libs=(SASHELP) Space separated list of libraries to search for columns
|
||||
@param cols= Space separated list of column names to search for (not case
|
||||
sensitive)
|
||||
@param outds=(mp_searchcols) the table to create with the results. Will have
|
||||
one line per table match.
|
||||
@param match=(ANY) The match type. Valid values:
|
||||
@li ANY - The table contains at least one of the columns
|
||||
@li WILD - The table contains a column with a name that partially matches
|
||||
|
||||
@version 9.2
|
||||
@author Allan Bowe
|
||||
**/
|
||||
@@ -17,6 +25,7 @@
|
||||
%macro mp_searchcols(libs=sashelp
|
||||
,cols=
|
||||
,outds=mp_searchcols
|
||||
,match=ANY
|
||||
)/*/STORE SOURCE*/;
|
||||
|
||||
%put &sysmacroname process began at %sysfunc(datetime(),datetime19.);
|
||||
@@ -38,8 +47,10 @@ create table _data_ as
|
||||
%end;
|
||||
order by 1,2,3;
|
||||
|
||||
%local tempds;
|
||||
%let tempds=&syslast;
|
||||
data &outds;
|
||||
set &syslast;
|
||||
set &tempds;
|
||||
length cols matchcols $32767;
|
||||
cols=upcase(symget('cols'));
|
||||
colcount=countw(cols);
|
||||
@@ -53,10 +64,29 @@ data &outds;
|
||||
retain matchcols;
|
||||
matchcols='';
|
||||
end;
|
||||
%if &match=ANY %then %do;
|
||||
if findw(cols,name,,'spit') then do;
|
||||
sumcols+1;
|
||||
matchcols=cats(matchcols)!!' '!!cats(name);
|
||||
end;
|
||||
%end;
|
||||
%else %if &match=WILD %then %do;
|
||||
if _n_=1 then do;
|
||||
retain wcount;
|
||||
wcount=countw(cols);
|
||||
drop wcount;
|
||||
end;
|
||||
do i=1 to wcount;
|
||||
length curword $32;
|
||||
curword=scan(cols,i,' ');
|
||||
drop curword;
|
||||
if index(name,cats(curword)) then do;
|
||||
sumcols+1;
|
||||
matchcols=cats(matchcols)!!' '!!cats(curword);
|
||||
end;
|
||||
end;
|
||||
%end;
|
||||
|
||||
if last.memname then do;
|
||||
if sumcols>0 then output;
|
||||
if sumcols=colcount then putlog "Full Match: " libname memname;
|
||||
@@ -66,6 +96,8 @@ run;
|
||||
|
||||
proc sort; by descending sumcols memname libname; run;
|
||||
|
||||
proc sql;
|
||||
drop table &tempds;
|
||||
%put &sysmacroname process finished at %sysfunc(datetime(),datetime19.);
|
||||
|
||||
%mend;
|
||||
%mend mp_searchcols;
|
||||
@@ -117,4 +117,4 @@ proc sql
|
||||
|
||||
%put process finished at %sysfunc(datetime(),datetime19.);
|
||||
|
||||
%mend;
|
||||
%mend mp_searchdata;
|
||||
|
||||
@@ -49,4 +49,4 @@
|
||||
|
||||
quit;
|
||||
|
||||
%mend;
|
||||
%mend mp_setkeyvalue;
|
||||
@@ -71,4 +71,4 @@
|
||||
proc append base=&libds data=&syslast nowarn;run;
|
||||
|
||||
options &etls_syntaxcheck;
|
||||
%mend;
|
||||
%mend mp_stprequests;
|
||||
@@ -134,4 +134,4 @@ run;
|
||||
%mp_binarycopy(inloc="&inloc",outref=_webout)
|
||||
%end;
|
||||
|
||||
%mend;
|
||||
%mend mp_streamfile;
|
||||
|
||||
@@ -89,4 +89,4 @@ quit;
|
||||
libname &lib clear;
|
||||
|
||||
|
||||
%mend;
|
||||
%mend mp_testjob;
|
||||
@@ -22,8 +22,11 @@
|
||||
|mustbevalidname|can be anything, oops, %abort!!|
|
||||
|
||||
@param [in] debug= (log) Provide the _debug value
|
||||
@param [in] viyaresult=(WEBOUT_JSON) The Viya result type to return. For
|
||||
@param [in] mdebug= (0) Set to 1 to provide macro debugging
|
||||
@param [in] viyaresult= (WEBOUT_JSON) The Viya result type to return. For
|
||||
more info, see mv_getjobresult.sas
|
||||
@param [in] viyacontext= (SAS Job Execution compute context) The Viya compute
|
||||
context on which to run the service
|
||||
@param [out] outlib= (0) Output libref to contain the final tables. Set to
|
||||
0 if the service output is not in JSON format.
|
||||
@param [out] outref= (0) Output fileref to create, to contain the full _webout
|
||||
@@ -47,17 +50,18 @@
|
||||
inputfiles=0,
|
||||
inputparams=0,
|
||||
debug=log,
|
||||
mdebug=0,
|
||||
outlib=0,
|
||||
outref=0,
|
||||
viyaresult=WEBOUT_JSON
|
||||
viyaresult=WEBOUT_JSON,
|
||||
viyacontext=SAS Job Execution compute context
|
||||
)/*/STORE SOURCE*/;
|
||||
%local mdebug;
|
||||
%if &debug ne 0 %then %do;
|
||||
%let mdebug=1;
|
||||
%local dbg;
|
||||
%if &mdebug=1 %then %do;
|
||||
%put &sysmacroname entry vars:;
|
||||
%put _local_;
|
||||
%end;
|
||||
%else %let mdebug=0;
|
||||
%else %let dbg=*;
|
||||
|
||||
/* sanitise inputparams */
|
||||
%local pcnt;
|
||||
@@ -212,6 +216,7 @@
|
||||
|
||||
data &ds1;
|
||||
retain _program "&program";
|
||||
retain _contextname "&viyacontext";
|
||||
set &ds1;
|
||||
putlog "&sysmacroname inputparams:";
|
||||
putlog (_all_)(=);
|
||||
|
||||
@@ -56,4 +56,4 @@ data &outds;
|
||||
duration_seconds=end_dttm-start_dttm;
|
||||
run;
|
||||
|
||||
%mend;
|
||||
%mend mp_testwritespeedlibrary;
|
||||
@@ -68,4 +68,4 @@ data &outds ;
|
||||
rc=filename('tmp');
|
||||
run;
|
||||
|
||||
%mend;
|
||||
%mend mp_tree;
|
||||
@@ -63,4 +63,4 @@ data _null_;
|
||||
!!'filename &fname2 clear; filename &fname3 clear;');
|
||||
run;
|
||||
|
||||
%mend;
|
||||
%mend mp_unzip;
|
||||
@@ -90,4 +90,4 @@ alter table &libds modify &var char(&len);
|
||||
|
||||
%mp_createconstraints(inds=&dsconst,outds=&dsconst._addd,execute=YES)
|
||||
|
||||
%mend;
|
||||
%mend mp_updatevarlength;
|
||||
|
||||
@@ -76,4 +76,4 @@ ods package publish archive properties
|
||||
(archive_name="&outname..zip" archive_path="&outpath");
|
||||
ods package close;
|
||||
|
||||
%mend;
|
||||
%mend mp_zip;
|
||||
2
build.py
2
build.py
@@ -23,7 +23,7 @@ for file in files:
|
||||
ml.write(" put '" + line.rstrip().replace("'","''") + " ';\n")
|
||||
ml.write("run;\n\n")
|
||||
ml.write("%inc \"%sysfunc(pathname(work))/" + name + ".lua\";\n\n")
|
||||
ml.write("%mend;\n")
|
||||
ml.write("%mend " + name + ";\n")
|
||||
|
||||
ml.close()
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
-- NOTE - THE COPYRIGHT BELOW IS IN RELATION TO THE JSON.LUA FILE ONLY
|
||||
-- THIS FILE STARTS ON THE NEXT LINE AND WILL FINISH WITH "JSON.LUA ENDS HERE"
|
||||
--
|
||||
-- json.lua
|
||||
--
|
||||
@@ -369,3 +371,5 @@ function json.decode(str)
|
||||
end
|
||||
|
||||
return json
|
||||
|
||||
-- JSON.LUA ENDS HERE
|
||||
@@ -12,6 +12,8 @@
|
||||
%macro ml_json();
|
||||
data _null_;
|
||||
file "%sysfunc(pathname(work))/ml_json.lua";
|
||||
put '-- NOTE - THE COPYRIGHT BELOW IS IN RELATION TO THE JSON.LUA FILE ONLY ';
|
||||
put '-- THIS FILE STARTS ON THE NEXT LINE AND WILL FINISH WITH "JSON.LUA ENDS HERE" ';
|
||||
put '-- ';
|
||||
put '-- json.lua ';
|
||||
put '-- ';
|
||||
@@ -383,8 +385,10 @@ data _null_;
|
||||
put 'end ';
|
||||
put ' ';
|
||||
put 'return json ';
|
||||
put ' ';
|
||||
put '-- JSON.LUA ENDS HERE ';
|
||||
run;
|
||||
|
||||
%inc "%sysfunc(pathname(work))/ml_json.lua";
|
||||
|
||||
%mend;
|
||||
%mend ml_json;
|
||||
|
||||
7
main.dox
7
main.dox
@@ -42,6 +42,13 @@
|
||||
|
||||
*/
|
||||
|
||||
/*! \dir Tests
|
||||
* \brief SASjs Tests
|
||||
* \details These folders contain the macro tests. They are first compiled
|
||||
and deployed (sasjs cbd) then executed (sasjs test).
|
||||
|
||||
*/
|
||||
|
||||
/*! \dir viya
|
||||
* \brief Viya macros
|
||||
* \details These macros have the following attributes:
|
||||
|
||||
@@ -95,4 +95,4 @@ run;
|
||||
|
||||
filename __us2grp clear;
|
||||
|
||||
%mend;
|
||||
%mend mm_adduser2group;
|
||||
@@ -460,4 +460,4 @@ run;
|
||||
%return;
|
||||
%end;
|
||||
|
||||
%mend;
|
||||
%mend mm_assigndirectlib;
|
||||
|
||||
@@ -75,4 +75,4 @@
|
||||
%else %do;
|
||||
%put NOTE: Library &libref is already assigned;
|
||||
%end;
|
||||
%mend;
|
||||
%mend mm_assignlib;
|
||||
|
||||
@@ -152,4 +152,4 @@ run;
|
||||
%else %put NOTE: Application (&name) successfully created in (&tree)!;
|
||||
|
||||
|
||||
%mend;
|
||||
%mend mm_createapplication;
|
||||
@@ -80,4 +80,4 @@ data _null_;
|
||||
if last then call execute('call missing(of _all_);stop;run;');
|
||||
run;
|
||||
|
||||
%mend;
|
||||
%mend mm_createdataset;
|
||||
@@ -122,4 +122,4 @@ run;
|
||||
run;
|
||||
%end;
|
||||
|
||||
%mend;
|
||||
%mend mm_createdocument;
|
||||
@@ -157,4 +157,4 @@ run;
|
||||
%end;
|
||||
|
||||
%put &sysmacroname: execution finished for &path;
|
||||
%mend;
|
||||
%mend mm_createfolder;
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user