1
0
mirror of https://github.com/sasjs/core.git synced 2026-07-23 23:45:28 +00:00

Compare commits

..

1 Commits

Author SHA1 Message Date
github-actions aef9a6325b chore: updating all.sas 2025-05-29 11:25:53 +00:00
98 changed files with 491 additions and 5293 deletions
-59
View File
@@ -1,59 +0,0 @@
# Testing in @sasjs/core
## Overview
Tests are executed on a real SAS server using the SASjs CLI (`sasjs test`), not locally. Each test is a self-contained `.sas` file that is submitted to the server, and results are collected in the `sasjsresults` folder.
## Running Tests
```bash
npm test # runs: npx @sasjs/cli test -t server
```
The `-t server` flag selects the target (server type) from `sasjs/sasjsconfig.json`.
## Test Structure
Test files live under `tests/` in subfolders by platform applicability:
- `tests/base` — run on all platforms (SAS 9 and Viya)
- `tests/sas9only` — SAS 9 only (metadata server macros)
- `tests/viyaonly` — Viya only
- `tests/serveronly` — SASjs Server only
- `tests/x-platform` — cross-platform (both SAS 9 and Viya)
- `tests/ddlonly` — DDL-related tests
Naming convention: `<macroname>.test.sas`, with numbered variants (`<macroname>.test.1.sas`, `.test.2.sas`, ...) for multiple tests of the same macro. File names are lowercase, matching the lint rules.
## Test Flow
1. **Init**: `tests/testinit.sas` runs before every test (configured in `sasjsconfig.json` under `testConfig.initProgram`). It sets up a unique app location (`mcTestAppLoc`), the compute context, calls `%mp_init()`, and enables debug options when `_debug` is set.
2. **Test body**: the test file itself runs. It should use `%mp_assert()` to record results into `work.test_results`:
```sas
%mp_assert(
iftrue=(&syscc=0),
desc=Checking for error condition,
outds=work.test_results
)
```
3. **Term**: `tests/testterm.sas` runs after every test (`testConfig.termProgram`). It adds a final assertion that `&syscc=0`, then writes the results as JSON via `%webout(OPEN) / %webout(OBJ,TEST_RESULTS) / %webout(CLOSE)`.
## Results
After a test run, check the `sasjsresults` folder:
- `testResults.json` / `testResults.xml` / `testResults.csv` — per-test PASS/FAIL with descriptions and comments
- `logs/<testname>.log` — the full SAS log for each test; check here first when a test fails
- `coverage.lcov` — coverage data
## Writing Tests — Things to Know
- Tests follow the same Doxygen header and lint standards as regular macros (`@file`, `@brief`, `<h4> SAS Macros </h4>` listing macros used).
- Macro *calls* are not terminated with semicolons: use `%mp_assert(...)` not `%mp_assert(...);`.
- Use `%mp_assert(iftrue=(...), desc=..., outds=work.test_results)` for every check — always append to `work.test_results`.
- When comparing datasets after a round trip (eg through JSON), do not assert `proc compare` SYSINFO=0 directly — SYSINFO is a bitmask that includes attribute differences (length, format, label) which round trips legitimately change. Mask it to data-related bits only (64=missing obs in compare, 128=extra obs in compare, 4096=unequal values, 32768=obs count differs). Note `SYSINFO` is a read-only automatic macro variable, so store the masked value in a new variable.
- Be careful with character data round trips: `cats()` and the plain `$` informat strip leading blanks; use `trim()` and `$char` where leading blanks must be preserved.
- After any change, run `npx sasjs lint`.
- Do not edit generated copies under `sasjsbuild/` — they are refreshed by the CI build.
+3 -44
View File
@@ -11,23 +11,9 @@ on:
jobs:
release:
runs-on: ubuntu-latest
permissions:
contents: write
issues: write
pull-requests: write
id-token: write # required for npm Trusted Publishing (OIDC)
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 'lts/*'
registry-url: 'https://registry.npmjs.org'
- name: Ensure npm supports Trusted Publishing
run: npm install -g npm@latest
uses: actions/checkout@v4
- name: Install dependencies
run: |
@@ -68,40 +54,13 @@ jobs:
echo "REFRESH_TOKEN=${{secrets.SAS9_4GL_IO_REFRESH_TOKEN}}" >> .env.server
- name: Semantic Release
id: makerelease
uses: cycjimmy/semantic-release-action@v6
uses: cycjimmy/semantic-release-action@v4
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# NPM_TOKEN no longer required: package is published via npm
# Trusted Publishing (OIDC). The id-token: write permission above
# allows npm CLI to authenticate to the registry automatically.
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
- name: SAS Packages Release
run: |
npx @sasjs/cli compile job -s sasjs/utils/create_sas_package.sas -o sasjsbuild -t server
# need long duration token per https://github.com/sasjs/server/issues/307
# npx @sasjs/cli run sasjsbuild/jobs/utils/create_sas_package.sas -t server
- name: Update Docs Site
if: steps.makerelease.outputs.new_release_published == 'true'
run: |
sudo apt-get update
sudo apt-get install -y doxygen
npx @sasjs/cli doc -t docsonly
git clone https://x-access-token:${{ secrets.CORESASJSIO_PAT }}@github.com/sasjs/core.github.io.git
cd core.github.io
rm -rf *.html
rm -rf *.js
rm -rf *.png
rm -rf *.dot
rm -rf *.css
rm -rf *.svg
rm -rf search
cp -R ../sasjsbuild/docs/* .
ls
git config user.name sasjs
echo 'core.sasjs.io' > CNAME
git add .
git commit -m "core pipeline build on $(date +%F:%H:%M:%S)"
git push
echo "check it out: https://sasjs.github.io/core.github.io/files.html"
+1 -2
View File
@@ -13,7 +13,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v6
uses: actions/checkout@v4
- name: Install dependencies
run: |
@@ -26,7 +26,6 @@ jobs:
git config user.email github-actions@github.com
python3 build.py
git add all.sas
git add viya/mv_createwebservice.sas
git commit -m "chore: updating all.sas" --allow-empty
git push
+2 -5
View File
@@ -12,10 +12,10 @@ jobs:
strategy:
matrix:
node-version: [lts/iron]
node-version: [lts/hydrogen]
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v2
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v2
with:
@@ -73,8 +73,6 @@ jobs:
- name: Build & Deploy Project to SAS server
run: npx @sasjs/cli cbd -t server
env:
macroCorePath: .
- name: Run all tests
run: npx @sasjs/cli test -t server
@@ -86,4 +84,3 @@ jobs:
SAS_PASSWORD: ${{secrets.SAS_PASSWORD}}
ACCESS_TOKEN: ${{secrets.ACCESS_TOKEN}}
REFRESH_TOKEN: ${{secrets.REFRESH_TOKEN}}
-1
View File
@@ -14,4 +14,3 @@ mc_*
~
.claude
+6
View File
@@ -0,0 +1,6 @@
FROM gitpod/workspace-full
RUN sudo apt-get update \
&& sudo apt-get install -y doxygen \
&& sudo apt-get install -y graphviz \
&& sudo rm -rf /var/lib/apt/lists/*
+27
View File
@@ -0,0 +1,27 @@
tasks:
- init: npm install -g npm
- command: npm i
- command: npm i -g @sasjs/cli
image:
file: .gitpod.dockerfile
vscode:
extensions:
- sasjs.sasjs-for-vscode
github:
prebuilds:
# enable for the master/default branch (defaults to true)
master: true
# enable for all branches in this repo (defaults to false)
branches: false
# enable for pull requests coming from this repo (defaults to true)
pullRequests: true
# enable for pull requests coming from forks (defaults to false)
pullRequestsFromForks: true
# add a "Review in Gitpod" button as a comment to pull requests (defaults to true)
addComment: true
# add a "Review in Gitpod" button to pull requests (defaults to false)
addBadge: false
# add a label once the prebuild is ready to pull requests (defaults to false)
addLabel: prebuilt-in-gitpod
+1 -1
View File
@@ -1,5 +1,6 @@
all.sas
build.py
.gitpod*
tests/
sasjs/
.github/
@@ -8,4 +9,3 @@ sasjs/
make_singlefile.sh
*.md
.all-contributorsrc
.agents
-1
View File
@@ -1 +0,0 @@
ignore-scripts=true
-37
View File
@@ -1,37 +0,0 @@
# Agent Instructions for @sasjs/core
Follow these rules when editing or generating code for the @sasjs/core SAS macro library.
## Project Context
This repo is the SASjs Macro Core library — a collection of MIT-licensed, production-quality SAS macros for SAS application development.
## Versioning
- NEVER bump or modify the version in `package.json`.
- Versioning is handled entirely by the CI/CD pipeline using semantic-release.
## SAS Style & Standards
- Read and follow the standards documented in `README.md` (Sections: Components, Standards, File Properties, Header Properties, Coding Standards).
- Read and follow `.sasjslint`:
- No trailing spaces.
- Requires a Doxygen header on every macro (`@file`, `@brief`, etc.).
- Lowercase file names without spaces.
- Lowercase macro names.
- Macro definitions must use parentheses, e.g. `%macro x();` not `%macro x;`.
- Indentation = 2 spaces (or multiple thereof); no tabs.
- Max line length 300.
- No gremlins / invisible characters.
- One macro per file; filename must match macro name.
- Macro *calls* should NOT be terminated with a semicolon. Use `%my_macro()` not `%my_macro();`.
- Macro variables must always be local, to prevent scope leakage.
## Testing
- Read `.agents/docs/tests.md` for details on how the testing process works (how to run tests, structure, assertions, and where to find logs/results).
## Markdown Files
- Markdown files must not use word-wrap: never insert carriage returns mid-sentence. Each sentence/paragraph stays on one line.
## Build / Generated Files
- Do not run the build script locally; it is executed in the CI/CD pipeline.
- Generated files, including the consolidated `all.sas`, the per-folder `mc_*.sas` files, and the LUA macro wrappers in the `lua` folder, can generally be ignored unless the pipeline requires an update. Do not edit generated files by hand.
- run sasjs lint after each change
+28 -21
View File
@@ -1,10 +1,19 @@
# Macro Core
[![npm package](https://img.shields.io/npm/v/@sasjs/core.svg)](http://npmjs.org/package/@sasjs/core)
[![Github Workflow](https://github.com/sasjs/core/actions/workflows/main.yml/badge.svg)](https://github.com/sasjs/core/blob/main/.github/workflows/main.yml)
[![npm package][npm-image]][npm-url]
[![Github Workflow][githubworkflow-image]][githubworkflow-url]
![npm](https://img.shields.io/npm/dt/@sasjs/core)
![GitHub top language](https://img.shields.io/github/languages/top/sasjs/core)
[![GitHub closed issues](https://img.shields.io/github/issues-closed-raw/sasjs/core)](https://github.com/sasjs/core/issues?q=is%3Aissue+is%3Aclosed)
[![GitHub issues](https://img.shields.io/github/issues-raw/sasjs/core)](https://github.com/sasjs/core/issues)
![total lines](https://tokei.rs/b1/github/sasjs/core)
[![Gitpod ready-to-code](https://img.shields.io/badge/Gitpod-ready--to--code-908a85?logo=gitpod)](https://gitpod.io/#https://github.com/sasjs/core)
[npm-image]:https://img.shields.io/npm/v/@sasjs/core.svg
[npm-url]:http://npmjs.org/package/@sasjs/core
[githubworkflow-image]:https://github.com/sasjs/core/actions/workflows/main.yml/badge.svg
[githubworkflow-url]:https://github.com/sasjs/core/blob/main/.github/workflows/main.yml
[dependency-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/.github/CONTRIBUTING.md) are welcome.
@@ -138,17 +147,16 @@ filename mc url "https://raw.githubusercontent.com/sasjs/core/main/all.sas";
- macro names must be lowercase
- one macro per file
- prefixes:
- _mcf__: macro compiled functions (proc fcmp)
- _mddl__: macros containing DDL (Data Definition Language)
- _mf__: macro functions (can be used in open code).
- _mfv__: macro functions that work only in Viya
- _ml__: macros that are used to compile LUA modules
- _mm__: metadata macros (interface with the metadata server).
- _mmx__: macros that use metadata and are XCMD enabled (working on both windows and unix)
- _mp__: macro procedures (which generate sas code)
- _ms__: macro procedures that will only work with [@sasjs/server](https://github.com/sasjs/server)
- _mv__: macro procedures that will only work in Viya
- _mx__: macros that work on Viya, SAS 9 EBI and SASjs Server
- _mcf_ for macro compiled functions (proc fcmp)
- _mddl_ for macros containing DDL (Data Definition Language)
- _mf_ for macro functions (can be used in open code).
- _ml_ for macros that are used to compile LUA modules
- _mm_ for metadata macros (interface with the metadata server).
- _mmx_ for macros that use metadata and are XCMD enabled (working on both windows and unix)
- _mp_ for macro procedures (which generate sas code)
- _ms_ for macro procedures that will only work with [@sasjs/server](https://github.com/sasjs/server)
- _mv_ for macro procedures that will only work in Viya
- _mx_ for macros that work on Viya, SAS 9 EBI and SASjs Server
- follow verb-noun convention
- unix style line endings (lf)
- individual lines should be no more than 80 characters long
@@ -199,7 +207,7 @@ When contributing to this library, it is therefore important to ensure that all
- All dataset references must be 2 level (eg `work.blah`, not `blah`). This is to avoid contention when options [DATASTMTCHK](https://support.sas.com/documentation/cdl/en/lrdict/64316/HTML/default/viewer.htm#a000279064.htm)=ALLKEYWORDS is in effect, or the [USER](https://documentation.sas.com/doc/en/pgmsascdc/9.4_3.5/lrcon/n18m1vkqmeo4esn1moikt23zhp8s.htm) library is active.
- Avoid naming collisions! All macro variables should be local scope. Use system generated work tables where possible - eg `data ; set sashelp.class; run; data &output; set &syslast; run;`
- Where global macro variables are absolutely necessary, they should make use of `&sasjs_prefix` - see mp_init.sas
- The use of `quit;` for `proc sql` is essential, to avoid `WARNING: You cannot disconnect or terminate session XXXX until the procedure completes.` when terminating CAS sessions in Viya.
- The use of `quit;` for `proc sql` is optional unless you are looking to benefit from the timing statistics.
- Use [sasjs lint](https://github.com/sasjs/lint)!
## General Notes
@@ -211,13 +219,12 @@ When contributing to this library, it is therefore important to ensure that all
We are currently on major release v4. Breaking changes should be marked with the [deprecated](https://www.doxygen.nl/manual/commands.html#cmddeprecated) doxygen tag. The following changes are planned when the next major/breaking release (v5) becomes necessary:
* mcf_xxx macros to have `insert_cmplib` option deprecated (the option is now checked automatically with value inserted only if needed)
* mcf_xxx macros to have `wrap=` option defaulted to YES for convenience. Set this option explicitly to avoid issues.
* mf_getuniquelibref.sas to have the deprecated maxtries parameter removed (no longer needed)
* mp_abort.sas will have the redundant type= parameter removed.
* mp_coretable.sas will be replaced by the standalone macros in the `ddl` folder (which are already available)
* mp_getddl.sas to be renamed to mp_ds2ddl.sas (consistent with other ds2xxx macros). A wrapper macro is already in place, and you are able to use this immediately. The default for SHOWLOG will also be YES instead of NO.
* mf_getuniquelibref.sas to have the deprecated maxtried parameter removed (no longer needed)
* mp_testservice.sas to be renamed as mp_execute.sas (as it doesn't actually test anything)
* `insert_cmplib` option of mcf_xxx macros will be deprecated (the option is now checked automatically with value inserted only if needed)
* mcf_xxx macros to have `wrap=` option defaulted to YES for convenience. Set this option explicitly to avoid issues.
* mp_getddl.sas to be renamed to mp_ds2ddl.sas (consistent with other ds2xxx macros). A wrapper macro is already in place, and you are able to use this immediately. The default for SHOWLOG will also be YES instead of NO.
* mp_coretable.sas will be replaced by the standalone macros in the `ddl` folder (which are already available)
## Star Gazing
+175 -1592
View File
File diff suppressed because it is too large Load Diff
+1 -2
View File
@@ -25,9 +25,8 @@
%local dsid vnum rc schema;
/* in case the parameter is a libref.tablename, pull off just the libref */
%let libref = %upcase(%scan(&libref, 1, %str(.)));
/* sysname can be 'Schema/Owner' or just 'Schema' (eg snowflake) */
%let dsid=%sysfunc(open(sashelp.vlibnam(where=(
libname="%upcase(&libref)" and sysname=:'Schema'
libname="%upcase(&libref)" and sysname='Schema/Owner'
)),i));
%if (&dsid ^= 0) %then %do;
%let vnum=%sysfunc(varnum(&dsid,SYSVALUE));
+1 -1
View File
@@ -26,7 +26,7 @@
@author Allan Bowe
**/
%macro mf_getuniquelibref(prefix=mc,maxtries=1000);
%macro mf_getuniquelibref(prefix=mclib,maxtries=1000);
%local x;
%if ( %length(&prefix) gt 7 ) %then %do;
+8 -27
View File
@@ -1,8 +1,7 @@
/**
@file
@brief Retrieves a value from a dataset. Returned value is fetched from the
'fetchobs=' record (row 1 by default), after applying the optional filter.
@brief Retrieves a value from a dataset. If no filter supplied, then first
record is used.
@details Be sure to <code>%quote()</code> your where clause. Example usage:
%put %mf_getvalue(sashelp.class,name,filter=%quote(age=15));
@@ -17,39 +16,21 @@
@param [in] libds dataset to query
@param [in] variable the variable which contains the value to return.
@param [in] filter= (1) contents of where clause
@param [in] fetchobs= (1) observation to fetch. NB: Filter applies first.
@version 9.2
@author Allan Bowe
**/
%macro mf_getvalue(libds,variable,filter=1,fetchobs=1
%macro mf_getvalue(libds,variable,filter=1
)/*/STORE SOURCE*/;
%local dsid;
%let dsid=%sysfunc(open(&libds(where=(&filter))));
%if (&dsid) %then %do;
%local rc &variable;
%if %mf_getattrn(&libds,NLOBS)>0 %then %do;
%local dsid rc &variable;
%let dsid=%sysfunc(open(&libds(where=(&filter))));
%syscall set(dsid);
%let rc = %sysfunc(fetchobs(&dsid,&fetchobs));
%if (&rc ne 0) %then %do;
%put NOTE: Problem reading obs &fetchobs from &libds..;
%put %sysfunc(sysmsg());
/* Coerce an rc value of -1 (read past end of data) to a 4
that, in SAS condition code terms, represents the sysmsg
w@rning it generates. */
%if &rc eq -1 %then %let rc = 4;
/* And update SYSCC if the &rc value is higher */
%let syscc = %sysfunc(max(&syscc,&rc));
%end;
%let rc = %sysfunc(fetch(&dsid));
%let rc = %sysfunc(close(&dsid));
%trim(&&&variable)
%end;
%else %do;
%put %sysfunc(sysmsg());
%let syscc = %sysfunc(max(&syscc,%sysfunc(sysrc())));
%end;
%mend mf_getvalue;
%mend mf_getvalue;
+17 -12
View File
@@ -3,12 +3,9 @@
@brief Returns words that are in both string 1 and string 2
@details Compares two space separated strings and returns the words that are
in both.
If either string is empty, nothing is returned.
Usage:
%put %mf_wordsinstr1andstr2(
%put %mf_wordsInStr1andStr2(
Str1=blah sss blaaah brah bram boo
,Str2= blah blaaah brah ssss
);
@@ -26,23 +23,31 @@
**/
%macro mf_wordsinstr1andstr2(
%macro mf_wordsInStr1andStr2(
Str1= /* string containing words to extract */
,Str2= /* used to compare with the extract string */
)/*/STORE SOURCE*/;
%local count_extr i extr_word outvar;
%local count_base count_extr i i2 extr_word base_word match outvar;
%if %length(&str1)=0 or %length(&str2)=0 %then %do;
%put &sysmacroname: empty input string, nothing to compare;
%put base string (str1)= &str1;
%put compare string (str2) = &str2;
%return;
%end;
%let count_base=%sysfunc(countw(&Str2));
%let count_extr=%sysfunc(countw(&Str1));
%do i=1 %to &count_extr;
%let extr_word=%scan(&Str1,&i,%str( ));
%if %sysfunc(indexw(%superq(str2),%superq(extr_word)))>0 %then
%let outvar=&outvar &extr_word;
%let match=0;
%do i2=1 %to &count_base;
%let base_word=%scan(&Str2,&i2,%str( ));
%if &extr_word=&base_word %then %let match=1;
%end;
%if &match=1 %then %let outvar=&outvar &extr_word;
%end;
/* send out the result without any surrounding whitespace */
%do;&outvar%end;
%mend mf_wordsinstr1andstr2;
&outvar
%mend mf_wordsInStr1andStr2;
+18 -13
View File
@@ -6,12 +6,9 @@
Note - case sensitive!
If str1 is empty, nothing is returned. If str2 is empty, all the words in
str1 are returned.
Usage:
%let x= %mf_wordsinstr1butnotstr2(
%let x= %mf_wordsInStr1ButNotStr2(
Str1=blah sss blaaah brah bram boo
,Str2= blah blaaah brah ssss
);
@@ -27,23 +24,31 @@
**/
%macro mf_wordsinstr1butnotstr2(
%macro mf_wordsInStr1ButNotStr2(
Str1= /* string containing words to extract */
,Str2= /* used to compare with the extract string */
)/*/STORE SOURCE*/;
%local count_extr i extr_word outvar;
%if %length(&str1)=0 %then %do;
%put &sysmacroname: str1 is empty, nothing to compare;
%local count_base count_extr i i2 extr_word base_word match outvar;
%if %length(&str1)=0 or %length(&str2)=0 %then %do;
%put base string (str1)= &str1;
%put compare string (str2) = &str2;
%return;
%end;
%let count_base=%sysfunc(countw(&Str2));
%let count_extr=%sysfunc(countw(&Str1));
%do i=1 %to &count_extr;
%let extr_word=%scan(&Str1,&i,%str( ));
%if %sysfunc(indexw(%superq(str2),%superq(extr_word)))=0 %then
%let outvar=&outvar &extr_word;
%let match=0;
%do i2=1 %to &count_base;
%let base_word=%scan(&Str2,&i2,%str( ));
%if &extr_word=&base_word %then %let match=1;
%end;
%if &match=0 %then %let outvar=&outvar &extr_word;
%end;
/* send out the result without any surrounding whitespace */
%do;&outvar%end;
%mend mf_wordsinstr1butnotstr2;
&outvar
%mend mf_wordsInStr1ButNotStr2;
+1 -2
View File
@@ -35,7 +35,7 @@
)/*/STORE SOURCE*/;
data ;
length test_result $4 test_description $256 test_comments $256;
length test_description $256 test_result $4 test_comments $256;
test_description=symget('desc');
test_comments="&sysmacroname: Test result of "!!symget('iftrue');
%if %eval(%unquote(&iftrue)) %then %do;
@@ -52,6 +52,5 @@
run;
proc sql;
drop table &ds;
quit;
%mend mp_assert;
+1 -1
View File
@@ -108,7 +108,7 @@
%end;
data;
length test_result $4 test_description $256 test_comments $256;
length test_description $256 test_result $4 test_comments $256;
test_description=symget('desc');
if test_description='0'
then test_description="Testing &inds for existence of &test of: &cols";
+1 -1
View File
@@ -143,7 +143,7 @@
)
data;
length test_result $4 test_description $256 test_comments $256;
length test_description $256 test_result $4 test_comments $256;
test_description=symget('desc');
test_result='FAIL';
test_comments="&sysmacroname: &lib..&ds..&col has &result/&orig values "
+1 -1
View File
@@ -87,7 +87,7 @@
%end;
data &ds;
length test_result $4 test_description $256 test_comments $256;
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.";
+6 -7
View File
@@ -73,6 +73,10 @@
ignorelist=,
outds=work.test_results
)/*/STORE SOURCE*/;
%local ds test_result test_comments del add mod ilist;
%let ilist=%upcase(&sasjs_prefix._FUNCTIONS SYS_PROCHTTP_STATUS_CODE
SYS_PROCHTTP_STATUS_CODE SYS_PROCHTTP_STATUS_PHRASE &ignorelist);
/**
* this sets up the global vars, it will also enter STRICT mode. If this
* behaviour is not desired, simply initiate the following global macro
@@ -80,10 +84,6 @@
*/
%mp_init()
%local ds test_result test_comments del add mod ilist;
%let ilist=%upcase(&sasjs_prefix._FUNCTIONS SYS_PROCHTTP_STATUS_CODE
SYS_PROCHTTP_STATUS_CODE SYS_PROCHTTP_STATUS_PHRASE &ignorelist);
/* get current variables */
%if &action=SNAPSHOT %then %do;
proc sql;
@@ -92,7 +92,6 @@
from dictionary.macros
where scope="&scope" and upcase(name) not in (%mf_getquotedstr(&ilist))
order by name,offset;
quit;
%end;
%else %if &action=COMPARE %then %do;
@@ -130,8 +129,9 @@
%let test_comments=%str(Mod:(&mod) Add:(&add) Del:(&del));
%end;
data ;
length test_result $4 test_description $256 test_comments $256;
length test_description $256 test_result $4 test_comments $256;
test_description=symget('desc');
test_comments=symget('test_comments');
test_result=symget('test_result');
@@ -142,7 +142,6 @@
run;
proc sql;
drop table &ds;
quit;
%end;
%mend mp_assertscope;
+2 -2
View File
@@ -130,10 +130,10 @@ data _null_;
* there is not much point importing a short length numeric like this,
* eg with best4., as the resulting variable will still be stored as
* length 8. We need a length or format statement to ensure variable
* is created with the smaller length...
* is creatd with the smaller length...
**/
else if vlen<8 then header=cats(varnm,':best',vlen,'.');
else header=cats(varnm,':best32.');
else header=cats(varnm,':best.');
end;
%end;
%else %do;
+7 -7
View File
@@ -17,7 +17,7 @@
@li mf_getattrn.sas
@li mf_getuniquename.sas
@li mf_getvarlist.sas
@li mp_rowhash.sas
@li mp_md5.sas
<h4> Related Files </h4>
@li mp_hashdataset.test.sas
@@ -46,7 +46,8 @@
%local keyvar /* roll up the md5 */
prevkeyvar /* retain prev record md5 */
lastvar /* last var in input ds */;
lastvar /* last var in input ds */
cvars nvars;
%if not(%eval(%unquote(&iftrue))) %then %return;
@@ -78,11 +79,10 @@
retain &prevkeyvar;
if _n_=1 then &prevkeyvar=put(md5("&salt"),$hex32.);
set &libds end=&lastvar;
/* hash should include previous row - listed first so it is hashed first */
%mp_rowhash(
md5_col=&keyvar
,cvars=&prevkeyvar %mf_getvarlist(&libds,typefilter=C)
,nvars=%mf_getvarlist(&libds,typefilter=N)
/* hash should include previous row */
&keyvar=%mp_md5(
cvars=%mf_getvarlist(&libds,typefilter=C) &prevkeyvar,
nvars=%mf_getvarlist(&libds,typefilter=N)
);
&prevkeyvar=&keyvar;
if &lastvar then output;
+1 -3
View File
@@ -37,14 +37,12 @@
%global
SASJS_PREFIX /* the ONLY hard-coded global macro variable in SASjs */
&prefix.PROCESSMODE &prefix._STPSRV_HEADER_LOC /* SASjs server specific vars*/
&prefix._FUNCTIONS /* used in mcf_init() to track core function compilation */
&prefix._INIT_NUM /* initialisation time as numeric */
&prefix._INIT_DTTM /* initialisation time in E8601DT26.6 format */
&prefix.WORK /* avoid typing %sysfunc(pathname(work)) every time */
&prefix.PROCESSMODE
&prefix._STPSRV_HEADER_LOC
;
%let sasjs_prefix=&prefix;
data _null_;
+1 -2
View File
@@ -331,8 +331,7 @@
prxchange('s/\\/\\\\/',-1,&&name&i)
)))))))))))))!!'"';
end;
/* trim (not cats) so leading blanks are retained */
else &&name&i='"'!!trim(&&name&i)!!'"';
else &&name&i=quote(cats(&&name&i));
%end;
%end;
run;
+4 -13
View File
@@ -45,7 +45,7 @@
@li mp_aligndecimal.sas
@li mp_cntlout.sas
@li mp_lockanytable.sas
@li mp_rowhash.sas
@li mp_md5.sas
@li mp_storediffs.sas
<h4> Related Macros </h4>
@@ -174,19 +174,14 @@ select distinct
%let nvars=FMTROW MIN MAX DEFAULT LENGTH FUZZ MULT NOEDIT;
data &base_fmts/note2err;
set &base_fmts;
length fmthash $32;
%mp_rowhash(
md5_col=fmthash
,cvars=&cvars
,nvars=&nvars
)
fmthash=%mp_md5(cvars=&cvars, nvars=&nvars);
run;
/**
* Ensure input table and base_formats have consistent lengths and types
*/
data &inlibds/nonote2err;
length &delete_col $3 FMTROW 8 start end label $32767 fmthash $32;
length &delete_col $3 FMTROW 8 start end label $32767;
if 0 then set &base_fmts;
set &libds;
by type fmtname notsorted;
@@ -208,11 +203,7 @@ data &inlibds/nonote2err;
%mp_aligndecimal(end,width=16)
end;
%mp_rowhash(
md5_col=fmthash
,cvars=&cvars
,nvars=&nvars
)
fmthash=%mp_md5(cvars=&cvars, nvars=&nvars);
run;
/**
-5
View File
@@ -28,16 +28,11 @@
@li Global option: `options dsoptions=nonote2err;`
@li Data step option: `data YOURLIB.YOURDATASET /nonote2err;`
For very wide tables (hundreds or thousands of columns) consider using
`mp_rowhash.sas`, which builds the same type of row hash iteratively and
avoids creating a single, very long concatenated SAS expression.
@param [in] cvars= () Space seperated list of character variables
@param [in] nvars= () Space seperated list of numeric variables
<h4> Related Programs </h4>
@li mp_init.sas
@li mp_rowhash.sas
@version 9.2
@author Allan Bowe
+2 -3
View File
@@ -33,8 +33,7 @@
LUA, you can also use this macro: mp_gsubfile.sas
@param [in] infile The QUOTED path to the file on which to perform the
substitution. Note that you can extract the pathname from a fileref using
the pathname function, eg: `"%sysfunc(pathname(fref))"`;
substitution
@param [in] findvar= Macro variable NAME containing the string to search for
@param [in] replacevar= Macro variable NAME containing the replacement string
@param [out] outfile= (0) Optional QUOTED path to the adjusted output file (to
@@ -148,6 +147,6 @@ data _null_;
run;
/* END */
/* %put &sysmacroname took %sysevalf(%sysfunc(datetime())-&dttm) secs to run; */
%put &sysmacroname took %sysevalf(%sysfunc(datetime())-&dttm) seconds to run;
%mend mp_replace;
+5 -6
View File
@@ -148,8 +148,7 @@
%mp_abort(iftrue=(&iserr=1),mac=mp_retainedkey,msg=%superq(msg))
proc sql noprint;
select sum(max(&retained_key),0) format=32. into: maxkey trimmed
from &base_libds;
select sum(max(&retained_key),0) into: maxkey from &base_libds;
/**
* get base table RK and bus field values for lookup
@@ -218,19 +217,19 @@ quit;
insert into &maxkeytable
set keytable="&base_libds"
,keycolumn="&retained_key"
,max_key=%sysevalf(&maxkey+&newkey_cnt)
,max_key=%eval(&maxkey+&newkey_cnt)
,processed_dttm="%sysfunc(datetime(),%mf_fmtdttm())"dt;
%end;
%else %do;
update &maxkeytable
set max_key=%sysevalf(&maxkey+&newkey_cnt)
set max_key=%eval(&maxkey+&newkey_cnt)
,processed_dttm="%sysfunc(datetime(),%mf_fmtdttm())"dt
where keytable="&base_libds";
%end;
%mp_lockanytable(UNLOCK
,lib=%scan(&maxkeytable,1,.)
,ds=%scan(&maxkeytable,2,.)
,ref=Updating maxkeyvalues with maxkey=%sysevalf(&maxkey+&newkey_cnt)
,ref=Updating maxkeyvalues with maxkey=%eval(&maxkey+&newkey_cnt)
,ctl_ds=&locktable
)
%end;
@@ -238,7 +237,7 @@ quit;
/* fill in the missing retained key values */
%let tempvar=%mf_getuniquename();
data &outds(drop=&tempvar);
retain &tempvar %sysevalf(&maxkey+1);
retain &tempvar %eval(&maxkey+1);
set &tempds2;
if &retained_key =. then &retained_key=&tempvar;
&tempvar=&tempvar+1;
-90
View File
@@ -1,90 +0,0 @@
/**
@file
@brief Iterative row-level MD5 hash generator
@details Generates DATA step statements that compute a deterministic,
row-level MD5 hash using a Merkle-style construction. Each variable is
hashed independently and the resulting raw 16-byte digests are combined
iteratively. This avoids concatenating the per-variable hex digests into a
single SAS character expression, which overflows when datasets contain many
variables.
Temporary variables use five leading underscores.
This macro is called from inside a DATA step. The caller is responsible
for declaring the output hash column if it does not already exist on the
input dataset.
Variables are hashed in the order supplied. If a particular variable
needs to influence the hash first (for example the previous row hash in
`mp_hashdataset`, or business dates in Data Controller's bitemporal
loader) simply list it first in `cvars` or `nvars`.
@param [in] md5_col= Name of the output hash column.
@param [in] cvars= Space separated list of character variables to hash.
@param [in] nvars= Space separated list of numeric variables to hash.
@version 9.3M5
@author Allan Bowe
**/
%macro mp_rowhash(
md5_col=
,cvars=
,nvars=
);
/* DATA step temp variables use five leading underscores. The names
are generated at macro invocation to avoid clashing with data columns. */
%local state digest pair numtext normal i chars nums;
%let state=%mf_getuniquename(prefix=_____state_);
%let digest=%mf_getuniquename(prefix=_____digest_);
%let pair=%mf_getuniquename(prefix=_____pair_);
%let numtext=%mf_getuniquename(prefix=_____numtext_);
%let normal=%mf_getuniquename(prefix=_____normal_);
%let i=%mf_getuniquename(prefix=_____i_);
%let chars=%mf_getuniquename(prefix=_____chars_);
%let nums=%mf_getuniquename(prefix=_____nums_);
length &state $16
&digest $16
&pair $32
&numtext $64
&normal &i 8;
if _n_=1 then call missing(&state,&digest,&pair,&numtext,&normal,&i);
drop &state &digest &pair &numtext &normal &i;
/* Versioned seed prevents confusion with other hashing schemes. */
&state = md5('DC HASH v2');
%if %length(&cvars)>0 %then %do;
array &chars{*} $ &cvars;
do &i = 1 to dim(&chars);
/* Leading blanks are retained. */
&digest = md5(trimn(&chars[&i]));
substr(&pair, 1, 16) = &state;
substr(&pair, 17, 16) = &digest;
&state = md5(&pair);
end;
%end;
%if %length(&nvars)>0 %then %do;
array &nums{*} &nvars;
do &i = 1 to dim(&nums);
/*
multiply-by-one for consistent cross-system precision.
Ignore null to protect SAS special missing values.
Cannot use IFN() as it evaluates both sides
*/
if missing(&nums[&i]) then &normal = &nums[&i];
else &normal = &nums[&i] * 1;
&numtext = put(&normal, binary64.);
&digest = md5(trim(&numtext));
substr(&pair, 1, 16) = &state;
substr(&pair, 17, 16) = &digest;
&state = md5(&pair);
end;
%end;
&md5_col = put(&state, $hex32.);
%mend mp_rowhash;
-1
View File
@@ -36,7 +36,6 @@
@li mf_islibds.sas
@li mf_wordsinstr1butnotstr2.sas
@li mp_abort.sas
@li mp_ds2squeeze.sas
<h4> Related Macros </h4>
@li mddl_dc_difftable.sas
+1 -15
View File
@@ -1,6 +1,6 @@
/**
@file
@brief Used to validate values in a data step
@brief Used to validate variables in a dataset
@details Useful when sanitising inputs, to ensure that they arrive with a
certain pattern.
Usage:
@@ -27,7 +27,6 @@
@param [in] incol The column to be validated
@param [in] rule The rule to apply. Current rules:
@li ISINT - checks if the variable is an integer
@li ISLIB - checks if the value is a valid libref (NOT whether it exists)
@li ISNUM - checks if the variable is numeric
@li LIBDS - matches LIBREF.DATASET format
@li FORMAT - checks if the provided format is syntactically valid
@@ -66,19 +65,6 @@
else &outcol=1;
drop &tempcol;
%end;
%else %if &rule=ISLIB %then %do;
if _n_=1 then do;
retain &tempcol;
&tempcol=prxparse('/^[_a-z]\w{0,7}$/i');
if missing(&tempcol) then do;
putlog 'ERR' +(-1) "OR: Invalid expression for ISLIB";
stop;
end;
drop &tempcol;
end;
if prxmatch(&tempcol, trim(&incol)) then &outcol=1;
else &outcol=0;
%end;
%else %if &rule=LIBDS %then %do;
/* match libref.dataset */
if _n_=1 then do;
Regular → Executable
+1 -51
View File
@@ -213,8 +213,7 @@ run;
%else %if &engine=ODBC %then %do;
%&mD.put NOTE: Retrieving ODBC connection details;
data _null_;
length connx_uri conprop_uri value datasource up_uri schema domprop_uri
authdomain $256.;
length connx_uri conprop_uri value datasource up_uri schema domprop_uri authdomain $256.;
call missing (of _all_);
/* get source connection ID */
rc=metadata_getnasn("&liburi",'LibraryConnection',1,connx_uri);
@@ -414,55 +413,6 @@ run;
libname &libref SQLSVR datasrc=&path schema=&schema user="&user" pass="&pass";
%end;
%else %if &engine=SASIOSNF or &engine=SNOW %then %do;
%&mD.put NOTE: Retrieving SNOW connection details;
data _null_;
length connx_uri conprop_uri value server up_uri schema domprop_uri
authdomain database $256.;
call missing (of _all_);
/* get source connection ID */
rc=metadata_getnasn("&liburi",'LibraryConnection',1,connx_uri);
/* get connection properties */
i=0;
do until (rc2<0);
i+1;
rc2=metadata_getnasn(connx_uri,'Properties',i,conprop_uri);
rc3=metadata_getattr(conprop_uri,'Name',value);
if value='Connection.DBMS.Property.SERVER.Name.xmlKey.txt' then do;
rc4=metadata_getattr(conprop_uri,'DefaultValue',server);
rc2=-1;
end;
end;
/* get auth domain */
autrc=metadata_getnasn(connx_uri,"Domain",1,domprop_uri);
arc=metadata_getattr(domprop_uri,"Name",authdomain);
if not missing(authdomain) then authdomain=cats('AUTHDOMAIN=',authdomain);
call symputx('authdomain',authdomain,'l');
/* get SCHEMA */
rc6=metadata_getnasn("&liburi",'UsingPackages',1,up_uri);
rc7=metadata_getattr(up_uri,'SchemaName',schema);
&mD.put rc= connx_uri= rc2= conprop_uri= rc3= value= rc4= server=
rc6= up_uri= rc7= schema=;
/* get database value */
prop='Connection.DBMS.Property.DB.Name.xmlKey.txt';
rc=metadata_getprop("&liburi",prop,database,"");
if database^='' then database='database='!!quote(trim(database));
call symputx('database',database,'l');
call symputx('snow_schema',schema,'l');
call symputx('snow_server',server,'l');
run;
libname &libref SNOW SERVER="&snow_server" SCHEMA=&snow_schema &authdomain
&database;
%if %length(&open_passthrough)>0 %then %do;
proc sql;
connect using &libref as &open_passthrough;
%end;
%end;
%else %if &engine=TERADATA %then %do;
%put NOTE: Obtaining &engine library details;
data _null;
+2 -6
View File
@@ -355,8 +355,7 @@ data _null_;
put ' prxchange(''s/\\/\\\\/'',-1,&&name&i) ';
put ' )))))))))))))!!''"''; ';
put ' end; ';
put ' /* trim (not cats) so leading blanks are retained */ ';
put ' else &&name&i=''"''!!trim(&&name&i)!!''"''; ';
put ' else &&name&i=quote(cats(&&name&i)); ';
put ' %end; ';
put ' %end; ';
put ' run; ';
@@ -475,10 +474,7 @@ data _null_;
put ' data _null_; ';
put ' infile &&_webin_fileref&i termstr=crlf; ';
put ' input; ';
put ' /* a plain $ informat strips leading blanks - use $char instead */ ';
put ' call symputx(''input_statement'' ';
put ' ,prxchange(''s/:\$(?=[0-9 ])/:\$char/i'',-1,_infile_) ';
put ' ); ';
put ' call symputx(''input_statement'',_infile_); ';
put ' putlog "&&_webin_name&i input statement: " _infile_; ';
put ' stop; ';
put ' data &&_webin_name&i; ';
+1 -4
View File
@@ -77,10 +77,7 @@
data _null_;
infile &&_webin_fileref&i termstr=crlf;
input;
/* a plain $ informat strips leading blanks - use $char instead */
call symputx('input_statement'
,prxchange('s/:\$(?=[0-9 ])/:\$char/i',-1,_infile_)
);
call symputx('input_statement',_infile_);
putlog "&&_webin_name&i input statement: " _infile_;
stop;
data &&_webin_name&i;
+2 -5
View File
@@ -31,8 +31,5 @@
"test": "npx @sasjs/cli test -t server",
"lint": "npx @sasjs/cli lint",
"prepare": "git rev-parse --git-dir && git config core.hooksPath ./.git-hooks || true"
},
"maintainers": [
"https://sasapps.io"
]
}
}
}
+5 -6
View File
@@ -21,7 +21,6 @@
<link href="$relpath^tabs.css" rel="stylesheet" type="text/css" />
<script type="text/javascript" src="$relpath^jquery.js"></script>
<script type="text/javascript" src="$relpath^dynsections.js"></script>
<script type="text/javascript" src="$relpath^cookie.js"></script>
$treeview $search $mathjax
<link href="$relpath^$stylesheet" rel="stylesheet" type="text/css" />
<link rel="shortcut icon" href="$relpath^favicon.ico" type="image/x-icon" />
@@ -40,10 +39,6 @@
g.async = true; g.src = u + 'matomo.js'; s.parentNode.insertBefore(g, s);
})();
</script>
<noscript>
<p><img referrerpolicy="no-referrer-when-downgrade" src="https://analytics.4gl.io/matomo.php?idsite=6&amp;rec=1"
style="border:0;" alt="" /></p>
</noscript>
<!-- End Matomo Code -->
</head>
@@ -84,4 +79,8 @@
</table>
</div>
<!--END TITLEAREA-->
<!-- end header part -->
<!-- end header part -->
</div>
</body>
</html>
+8 -4
View File
@@ -5,10 +5,7 @@
"ddl",
"fcmp",
"lua",
"meta",
"metax",
"server",
"viya",
"xplatform",
"tests/base",
"tests/ddlonly",
@@ -25,7 +22,10 @@
},
"testConfig": {
"initProgram": "tests/testinit.sas",
"termProgram": "tests/testterm.sas"
"termProgram": "tests/testterm.sas",
"macroVars": {
"mcTestAppLoc": "/Public/temp/macrocore"
}
},
"defaultTarget": "server",
"targets": [
@@ -42,6 +42,7 @@
"deployScripts": []
},
"macroFolders": [
"viya",
"tests/viyaonly"
],
"contextName": "SAS Job Execution compute context"
@@ -55,6 +56,8 @@
},
"appLoc": "/Shared Data/temp/macrocore",
"macroFolders": [
"meta",
"metax",
"tests/sas9only"
],
"programFolders": [],
@@ -79,6 +82,7 @@
"deployScripts": []
},
"macroFolders": [
"server",
"tests/serveronly"
]
},
+2 -6
View File
@@ -355,8 +355,7 @@ data _null_;
put ' prxchange(''s/\\/\\\\/'',-1,&&name&i) ';
put ' )))))))))))))!!''"''; ';
put ' end; ';
put ' /* trim (not cats) so leading blanks are retained */ ';
put ' else &&name&i=''"''!!trim(&&name&i)!!''"''; ';
put ' else &&name&i=quote(cats(&&name&i)); ';
put ' %end; ';
put ' %end; ';
put ' run; ';
@@ -473,10 +472,7 @@ data _null_;
put ' data _null_; ';
put ' infile &&_webin_fileref&i termstr=crlf lrecl=32767; ';
put ' input; ';
put ' /* a plain $ informat strips leading blanks - use $char instead */ ';
put ' call symputx(''input_statement'' ';
put ' ,prxchange(''s/:\$(?=[0-9 ])/:\$char/i'',-1,_infile_) ';
put ' ); ';
put ' call symputx(''input_statement'',_infile_); ';
put ' putlog "&&_webin_name&i input statement: " _infile_; ';
put ' stop; ';
put ' data &&_webin_name&i; ';
+1 -4
View File
@@ -74,10 +74,7 @@
data _null_;
infile &&_webin_fileref&i termstr=crlf lrecl=32767;
input;
/* a plain $ informat strips leading blanks - use $char instead */
call symputx('input_statement'
,prxchange('s/:\$(?=[0-9 ])/:\$char/i',-1,_infile_)
);
call symputx('input_statement',_infile_);
putlog "&&_webin_name&i input statement: " _infile_;
stop;
data &&_webin_name&i;
+1 -1
View File
@@ -14,7 +14,7 @@ data test;
run;
%mp_assertscope(SNAPSHOT)
%put %mf_getfilesize(libds=work.test);
%put %mf_getfilesize(libds=work.test)
%mp_assertscope(COMPARE)
%mp_assert(
+8 -5
View File
@@ -5,14 +5,9 @@
<h4> SAS Macros </h4>
@li mf_getfmtlist.sas
@li mp_assert.sas
@li mp_assertscope.sas
**/
%mp_assertscope(SNAPSHOT)
%put %mf_getfmtlist(sashelp.prdsale);
%mp_assertscope(COMPARE)
%mp_assert(
iftrue=(
"%mf_getfmtlist(sashelp.prdsale)"="DOLLAR $CHAR W MONNAME"
@@ -28,3 +23,11 @@
desc=Checking basic char,
outds=work.test_results
)
%mp_assert(
iftrue=(
"%mf_getfmtlist(sashelp.demographics)"="BEST Z $CHAR COMMA PERCENTN"
),
desc=Checking longer numeric,
outds=work.test_results
)
-78
View File
@@ -1,78 +0,0 @@
/**
@file
@brief Testing mf_getvalue macro
<h4> SAS Macros </h4>
@li mf_getvalue.sas
@li mp_assert.sas
@li mp_assertscope.sas
**/
data work.test_data;
do i = 1 to 10;
output;
end;
stop;
run;
/* - Test 1 -
Get value from default first observation.
No filter.
*/
%mp_assertscope(SNAPSHOT)
%let test_value=%mf_getvalue(work.test_data,i);
%mp_assertscope(COMPARE,ignorelist=test_value)
%mp_assert(
iftrue=(&test_value=1 and &syscc eq 0),
desc=Basic test fetching value from default first obs,
outds=work.test_results
)
/* - Test 2 -
Get value from 10th observation.
No filter.
*/
%let test_value=%mf_getvalue(work.test_data,i,fetchobs=10);
%mp_assert(
iftrue=(&test_value=10 and &syscc eq 0),
desc=Test fetching value from specifically the 10th row,
outds=work.test_results
)
/* - Test 3 -
Get value from default first observation.
With filter.
*/
%let test_value=%mf_getvalue(work.test_data,i,filter=(i>4));
%mp_assert(
iftrue=(&test_value=5 and &syscc eq 0),
desc=Test fetching value from default row of filtered data,
outds=work.test_results
)
/* - Test 4 -
Get value from specified observation.
With filter.
*/
%let test_value=%mf_getvalue(work.test_data,i,filter=(i>4),fetchobs=5);
%mp_assert(
iftrue=(&test_value=9 and &syscc eq 0),
desc=Test fetching value from 5th row of filtered data,
outds=work.test_results
)
/* - Test 5 -
Get value from default observation.
Filter removes all rows. This simulates providing an empty dataset
or specifying an observation number beyond the set returned by the filter.
*/
%let test_value=%mf_getvalue(work.test_data,i,filter=(i>10));
%mp_assert(
iftrue=(&test_value=%str() and &syscc eq 4),
desc=Test fetching value from 1st row of empty (filtered) data,
outds=work.test_results
)
%let syscc=0;
+1 -1
View File
@@ -15,7 +15,7 @@
%let test5=%mf_getvarlist(sashelp.class,typefilter=C);
data work.test_results;
length test_result $4 test_description $256 test_comments base result $256;
length test_description $256 test_result $4 test_comments base result $256;
test_description="Basic test";
base=symget('test1');
result='Name Sex Age Height Weight';
+1 -4
View File
@@ -8,12 +8,9 @@
**/
%mp_assertscope(SNAPSHOT)
%let test_value=%mf_mimetype(CSV);
%mp_assertscope(COMPARE,ignorelist=test_value)
%mp_assert(
iftrue=("%mf_mimetype(XLS)"="application/vnd.ms-excel"),
iftrue=("%mf_mimetype(XLS)"="application/vnd.ms-excel",
desc=Checking correct value
)
-94
View File
@@ -5,17 +5,12 @@
<h4> SAS Macros </h4>
@li mf_wordsinstr1andstr2.sas
@li mp_assert.sas
@li mp_assertscope.sas
**/
/* basic test, with scope check */
%mp_assertscope(SNAPSHOT)
%let x=%mf_wordsinstr1andstr2(str1=xx DOLLAR x $CHAR xxx W MONNAME
,str2=DOLLAR $CHAR W MONNAME xxxxxx
);
%mp_assertscope(COMPARE,ignorelist=x)
%mp_assert(
iftrue=(
"&x"="DOLLAR $CHAR W MONNAME"
@@ -23,92 +18,3 @@
desc=Checking basic string,
outds=work.test_results
)
/* word boundary - var should not match var1 or var10 */
%mp_assert(
iftrue=(
"%mf_wordsinstr1andstr2(str1=var1 var10,str2=var var1)"="var1"
),
desc=Checking word boundaries,
outds=work.test_results
)
/* case sensitivity - dollar does not match DOLLAR */
%mp_assert(
iftrue=(
"%mf_wordsinstr1andstr2(str1=DOLLAR dollar,str2=DOLLAR)"="DOLLAR"
),
desc=Checking case sensitivity,
outds=work.test_results
)
/* duplicate words in str1 are preserved */
%mp_assert(
iftrue=(
"%mf_wordsinstr1andstr2(str1=a a b,str2=a)"="a a"
),
desc=Checking duplicate words,
outds=work.test_results
)
/* when no words match, nothing is returned */
%mp_assert(
iftrue=(
"%mf_wordsinstr1andstr2(str1=a b,str2=c d)"=""
),
desc=Checking empty result,
outds=work.test_results
)
/* when str1 is empty, nothing is returned */
%mp_assert(
iftrue=(
"%mf_wordsinstr1andstr2(str1=,str2=a b)"=""
),
desc=Checking empty str1,
outds=work.test_results
)
/* when str2 is empty, nothing is returned */
%mp_assert(
iftrue=(
"%mf_wordsinstr1andstr2(str1=a b,str2=)"=""
),
desc=Checking empty str2,
outds=work.test_results
)
/* build strings containing 1000 variables */
/* str2 is kept to 100 words to avoid excessive macro iterations */
data _null_;
length str1 str2 $32767;
do i=1 to 1000;
word=cats('var',i);
str1=catx(' ',str1,word);
if mod(i,10)=0 then str2=catx(' ',str2,word);
end;
call symputx('str1',str1);
call symputx('str2',str2);
run;
%mp_assertscope(SNAPSHOT)
%let result=%mf_wordsinstr1andstr2(str1=&str1,str2=&str2);
%mp_assertscope(COMPARE,ignorelist=result)
%let count=%sysfunc(countw(&result));
%mp_assert(
iftrue=(
"&count"="100"
),
desc=Checking 1000 variable string returns 100 words,
outds=work.test_results
)
%mp_assert(
iftrue=(
"&result"="&str2"
),
desc=Checking 1000 variable string content,
outds=work.test_results
)
@@ -5,17 +5,12 @@
<h4> SAS Macros </h4>
@li mf_wordsinstr1butnotstr2.sas
@li mp_assert.sas
@li mp_assertscope.sas
**/
/* basic test, with scope check */
%mp_assertscope(SNAPSHOT)
%let x=%mf_wordsinstr1butnotstr2(str1=xx DOLLAR x $CHAR xxx W MONNAME
,str2=ff xx x xxx xxxxxx
);
%mp_assertscope(COMPARE,ignorelist=x)
%mp_assert(
iftrue=(
"&x"="DOLLAR $CHAR W MONNAME"
@@ -23,95 +18,3 @@
desc=Checking basic string,
outds=work.test_results
)
/* word boundary - var1 should not match var10 or var100 */
%mp_assert(
iftrue=(
"%mf_wordsinstr1butnotstr2(str1=var1 var10 var100,str2=var1)"
="var10 var100"
),
desc=Checking word boundaries,
outds=work.test_results
)
/* case sensitivity - dollar does not match DOLLAR */
%mp_assert(
iftrue=(
"%mf_wordsinstr1butnotstr2(str1=DOLLAR dollar,str2=DOLLAR)"="dollar"
),
desc=Checking case sensitivity,
outds=work.test_results
)
/* duplicate words in str1 are preserved */
%mp_assert(
iftrue=(
"%mf_wordsinstr1butnotstr2(str1=a a b a,str2=b)"="a a a"
),
desc=Checking duplicate words,
outds=work.test_results
)
/* when all words match, nothing is returned */
%mp_assert(
iftrue=(
"%mf_wordsinstr1butnotstr2(str1=a b,str2=b a)"=""
),
desc=Checking empty result,
outds=work.test_results
)
/* when str1 is empty, nothing is returned */
%mp_assert(
iftrue=(
"%mf_wordsinstr1butnotstr2(str1=,str2=a b)"=""
),
desc=Checking empty str1,
outds=work.test_results
)
/* when str2 is empty, all of str1 is returned */
%mp_assert(
iftrue=(
"%mf_wordsinstr1butnotstr2(str1=a b c,str2=)"="a b c"
),
desc=Checking empty str2 returns all words,
outds=work.test_results
)
/* build strings containing 1000 variables */
/* str2 is kept to 100 words to avoid excessive macro iterations */
data _null_;
length str1 str2 expected $32767;
do i=1 to 1000;
word=cats('var',i);
str1=catx(' ',str1,word);
if mod(i,10)=0 then str2=catx(' ',str2,word);
else expected=catx(' ',expected,word);
end;
call symputx('str1',str1);
call symputx('str2',str2);
call symputx('expected',expected);
run;
%mp_assertscope(SNAPSHOT)
%let result=%mf_wordsinstr1butnotstr2(str1=&str1,str2=&str2);
%mp_assertscope(COMPARE,ignorelist=result)
%let count=%sysfunc(countw(&result));
%mp_assert(
iftrue=(
"&count"="900"
),
desc=Checking 1000 variable string returns 900 words,
outds=work.test_results
)
%mp_assert(
iftrue=(
"&result"="&expected"
),
desc=Checking 1000 variable string content,
outds=work.test_results
)
+6 -86
View File
@@ -85,7 +85,7 @@ data _null_;
run;
%mp_assert(
iftrue=("&test3a"="X:best32. Y:$char7. Z:best32."),
iftrue=("&test3a"="X:best. Y:$char7. Z:best."),
desc=Checking header row Test 3,
outds=work.test_results
)
@@ -96,87 +96,8 @@ run;
)
/* test 4 - sasjs with compare */
data work.baseball ;
attrib
Name length= $18 label="Player's Name"
Team length= $14 label="Team at the End of 1986"
nAtBat length= 8 label="Times at Bat in 1986"
nHits length= 8 label="Hits in 1986"
nHome length= 8 label="Home Runs in 1986"
nRuns length= 8 label="Runs in 1986"
nRBI length= 8 label="RBIs in 1986"
nBB length= 8 label="Walks in 1986"
YrMajor length= 8 label="Years in the Major Leagues"
CrAtBat length= 8 label="Career Times at Bat"
CrHits length= 8 label="Career Hits"
CrHome length= 8 label="Career Home Runs"
CrRuns length= 8 label="Career Runs"
CrRbi length= 8 label="Career RBIs"
CrBB length= 8 label="Career Walks"
League length= $8 label="League at the End of 1986"
Division length= $8 label="Division at the End of 1986"
Position length= $8 label="Position(s) in 1986"
nOuts length= 8 label="Put Outs in 1986"
nAssts length= 8 label="Assists in 1986"
nError length= 8 label="Errors in 1986"
Salary length= 8 label="1987 Salary in $ Thousands"
Div length= $16 label="League and Division"
logSalary length= 8 label="Log Salary"
;
infile cards dsd;
input
Name :$char.
Team :$char.
nAtBat
nHits
nHome
nRuns
nRBI
nBB
YrMajor
CrAtBat
CrHits
CrHome
CrRuns
CrRbi
CrBB
League :$char.
Division :$char.
Position :$char.
nOuts
nAssts
nError
Salary
Div :$char.
logSalary
;
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 _;
/* fix precision issues */
if not missing(logSalary) then logsalary=logSalary*1;
datalines4;
"Allanson, Andy",Cleveland,293,66,1,30,29,14,1,293,66,1,30,29,14,American,East,C,446,33,20,.,AE,.
"Ashby, Alan",Houston,315,81,7,24,38,39,14,3449,835,69,321,414,375,National,West,C,632,43,10,475,NW,6.16331480403464
"Davis, Alan",Seattle,479,130,18,66,72,76,3,1624,457,63,224,266,263,American,West,1B,880,82,14,480,AW,6.17378610390193
"Dawson, Andre",Montreal,496,141,20,65,78,37,11,5628,1575,225,828,838,354,National,East,RF,200,11,3,500,NE,6.21460809842219
"Galarraga, Andres",Montreal,321,87,10,39,42,30,2,396,101,12,48,46,33,National,East,1B,805,40,4,91.5,NE,4.51633897228147
"Griffin, Alfredo",Oakland,594,169,4,74,51,35,11,4408,1133,19,501,336,194,American,West,SS,282,421,25,750,AW,6.62007320653035
"Newman, Al",Montreal,185,37,1,23,8,21,2,214,42,1,30,9,24,National,East,2B,76,127,7,70,NE,4.24849524204936
"Salazar, Argenis",Kansas City,298,73,0,24,24,7,3,509,108,0,41,37,12,American,West,SS,121,283,9,100,AW,4.60517018598809
"Thomas, Andres",Atlanta,323,81,6,26,32,8,2,341,86,6,32,34,8,National,West,SS,143,290,19,75,NW,4.31748811353631
"Thornton, Andre",Cleveland,401,92,17,49,66,65,13,5206,1332,253,784,890,866,American,East,DH,0,0,0,1100,AE,7.00306545878646
"Trammell, Alan",Detroit,574,159,21,107,75,59,10,4631,1300,90,702,504,488,American,East,SS,238,445,22,517.143,AE,6.24831943200756
"Trevino, Alex",Los Angeles,202,53,4,31,26,27,9,1876,467,15,192,186,161,National,West,C,304,45,11,512.5,NW,6.23930071101256
"Van Slyke, Andy",St Louis,418,113,13,48,61,47,4,1512,392,41,205,204,203,National,East,RF,211,11,7,550,NE,6.30991827822651
"Wiggins, Alan",Baltimore,239,60,0,30,11,22,6,1941,510,4,309,103,207,American,East,2B,121,151,6,700,AE,6.5510803350434
"Almon, Bill",Pittsburgh,196,43,7,29,27,30,13,3231,825,36,376,290,238,National,East,UT,80,45,8,240,NE,5.48063892334199
"Beane, Billy",Minneapolis,183,39,3,20,15,11,3,201,42,3,20,16,11,American,West,OF,118,0,0,.,AW,.
"Bell, Buddy",Cincinnati,568,158,20,89,75,73,15,8068,2273,177,1045,993,732,National,West,3B,105,290,10,775,NW,6.65286302935334
"Biancalana, Buddy",Kansas City,190,46,2,24,8,15,5,479,102,5,65,23,39,American,West,SS,102,177,16,175,AW,5.16478597392351
"Bochte, Bruce",Oakland,407,104,6,57,43,65,12,5233,1478,100,643,658,653,American,West,1B,912,88,9,.,AW,.
;;;;
run;
filename example temp lrecl=5000;
%mp_ds2csv(work.baseball,outref=example,headerformat=SASJS)
filename example temp;
%mp_ds2csv(sashelp.air,outref=example,headerformat=SASJS)
data _null_; infile example; input;put _infile_; if _n_>5 then stop;run;
data _null_;
@@ -192,16 +113,15 @@ run;
%mp_assert(
iftrue=(&syscc =0),
desc=Checking syscc prior to compare of work.baseball,
desc=Checking syscc prior to compare of sashelp.air,
outds=work.test_results
)
proc compare base=want compare=work.baseball
method = absolute criterion = 0.0000000001 ;
proc compare base=want compare=sashelp.air;
run;
%mp_assert(
iftrue=(&sysinfo le 41),
desc=Checking compare of work.baseball,
desc=Checking compare of sashelp.air,
outds=work.test_results
)
+1 -1
View File
@@ -32,7 +32,7 @@ data _null_;
run;
%mp_assert(
iftrue=("&test1a"="A:best3. B:best4. C:best32."),
iftrue=("&test1a"="A:best3. B:best4. C:best."),
desc=Checking header row Test 1,
outds=work.test_results
)
+1 -1
View File
@@ -24,7 +24,7 @@ data _null_;
run;
%mp_assert(
iftrue=("&author"="github-actions[bot]"),
iftrue=(&author=sasjsbot),
desc=release info extracted successfully,
outds=work.test_results
)
+1 -23
View File
@@ -11,20 +11,9 @@
filename webref temp;
data demo;
length x $100;
do x='"','0A'x,'0D'x,'09'x,'00'x,'0E'x,'0F'x,'01'x,'02'x,'10'x,'11'x,'\';
output;
end;
/* embedded quote variants */
x='say "hi" there'; output;
x='"fully quoted"'; output;
x='back\slash'; output;
x='quote and back\"slash'; output;
/* leading / trailing blank variants */
x=' leading blanks'; output;
x=' "leading blanks and quotes"'; output;
x='trailing blanks '; output;
x=' both '; output;
run;
%mp_jsonout(OPEN,jref=webref)
%mp_jsonout(OBJ,demo,jref=webref)
@@ -57,19 +46,8 @@ describe table web.demo;
proc compare base=work.demo compare=web.demo(keep=x);
quit;
/* sysinfo is a bitmask - keep only data-related bits, ie:
64 Base data set has observation not in comparison
128 Comparison data set has observation not in base
4096 A value comparison was unequal
32768 Number of observations differ
Attribute diffs (eg 16 - variable length) are ignored, as a JSON
round trip will not preserve lengths/formats/labels.
*/
/* SYSINFO is read only, so store the masked value in a new variable */
%let sysinfo_masked=%sysfunc(band(&sysinfo, 64+128+4096+32768));
%mp_assert(
iftrue=(&sysinfo_masked=0),
iftrue=(&sysinfo=0),
desc=Returned json is identical to input table for all special chars,
outds=work.test_results
)
-51
View File
@@ -5,7 +5,6 @@
<h4> SAS Macros </h4>
@li mp_assert.sas
@li mp_assertcolvals.sas
@li mp_assertscope.sas
@li mp_retainedkey.sas
**/
@@ -115,53 +114,3 @@ run;
desc=Checking append records created,
outds=work.test_results
)
/** Test 3 - large numeric RK values (avoid exponential notation issue) **/
data work.targetds3;
set sashelp.class;
rk_col=_n_+209100000000;
run;
data work.appendtable3;
set sashelp.class;
if mod(_n_,2)=0 then name=cats('New',_n_);
if _n_<7;
run;
%mp_assertscope(SNAPSHOT)
%mp_retainedkey(
base_lib=X
,base_dsn=targetds3
,append_lib=X
,append_dsn=APPENDTABLE3
,retained_key=rk_col
,business_key= name
,check_uniqueness=NO
,maxkeytable=0
,locktable=0
,outds=work.APPEND3
,filter_str=
)
%mp_assertscope(COMPARE,
desc=Checking macro scope leakage in mp_retainedkey (test 3)
)
%mp_assert(
iftrue=(&syscc=0),
desc=Checking errors in test 3 (large numeric RK),
outds=work.test_results
)
data work.check3;
do val=209100000001,209100000003,209100000005,
209100000020,209100000021,209100000022;
output;
end;
run;
%mp_assertcolvals(work.append3.rk_col,
checkvals=work.check3.val,
desc=All large numeric values have a match,
test=ALLVALS
)
-195
View File
@@ -1,195 +0,0 @@
/**
@file
@brief Testing mp_rowhash.sas macro
@details The mp_rowhash macro generates DATA step statements that compute a
deterministic row-level MD5 hash. These tests exercise the versioned seed,
character and numeric hashing rules, variable ordering, sensitivity to
whitespace, and reproducibility.
<h4> SAS Macros </h4>
@li mp_rowhash.sas
@li mp_assert.sas
@li mp_assertdsobs.sas
@li mp_assertscope.sas
**/
%mp_assertscope(SNAPSHOT)
/* test 1 - versioned seed only (no input variables) */
data _null_;
length actual expected $32;
actual='';
%mp_rowhash(md5_col=actual)
expected=put(md5('DC HASH v2'),$hex32.);
call symputx('t1_actual',actual);
call symputx('t1_expected',expected);
run;
%mp_assertscope(COMPARE,ignorelist=T1_ACTUAL T1_EXPECTED)
%mp_assert(
iftrue=("&t1_actual"="&t1_expected"),
desc=Versioned seed is returned when no variables are supplied,
outds=work.test_results
)
/* test 2 - single character variable reference */
data _null_;
length c1 $1 actual expected $32;
length state digest $16 pair $32;
c1='A';
actual='';
%mp_rowhash(md5_col=actual, cvars=c1)
state=md5('DC HASH v2');
digest=md5(trimn(c1));
pair=state||digest;
expected=put(md5(pair),$hex32.);
call symputx('t2_actual',actual);
call symputx('t2_expected',expected);
run;
%mp_assert(
iftrue=("&t2_actual"="&t2_expected"),
desc=Single character variable hash matches reference construction,
outds=work.test_results
)
/* test 3 - single numeric variable reference */
data _null_;
length actual expected $32;
length state digest $16 numtext $64 pair $32;
n1=42;
actual='';
%mp_rowhash(md5_col=actual, nvars=n1)
state=md5('DC HASH v2');
/* multiply-by-one matches the internal normalisation */
normal=n1*1;
numtext=put(normal,binary64.);
digest=md5(trim(numtext));
pair=state||digest;
expected=put(md5(pair),$hex32.);
call symputx('t3_actual',actual);
call symputx('t3_expected',expected);
run;
%mp_assert(
iftrue=("&t3_actual"="&t3_expected"),
desc=Single numeric variable hash matches reference construction,
outds=work.test_results
)
/* test 4 - order of cvars affects the resulting hash */
data _null_;
length c1 c2 $1 h1 h2 $32;
c1='A';
c2='B';
h1='';
%mp_rowhash(md5_col=h1, cvars=c1 c2)
h2='';
%mp_rowhash(md5_col=h2, cvars=c2 c1)
call symputx('t4_h1',h1);
call symputx('t4_h2',h2);
run;
%mp_assert(
iftrue=("&t4_h1" ne "&t4_h2"),
desc=Order of cvars changes the resulting hash,
outds=work.test_results
)
/* test 5 - order of nvars affects the resulting hash */
data _null_;
length h1 h2 $32;
n1=1;
n2=2;
h1='';
%mp_rowhash(md5_col=h1, nvars=n1 n2)
h2='';
%mp_rowhash(md5_col=h2, nvars=n2 n1)
call symputx('t5_h1',h1);
call symputx('t5_h2',h2);
run;
%mp_assert(
iftrue=("&t5_h1" ne "&t5_h2"),
desc=Order of nvars changes the resulting hash,
outds=work.test_results
)
/* test 6 - identical rows produce identical hashes */
data work.test6;
length c $5 n 8;
c='ABC';
n=42;
output;
output;
run;
data work.test6_hashed;
set work.test6;
length hash $32;
%mp_rowhash(md5_col=hash, cvars=c, nvars=n)
run;
proc sql noprint;
select count(distinct hash) into: t6_distinct trimmed
from work.test6_hashed;
quit;
%mp_assert(
iftrue=(&t6_distinct=1),
desc=Identical rows produce identical hashes,
outds=work.test_results
)
/* test 7 - different rows produce different hashes */
data work.test7;
length c $5 n 8;
c='ABC';
n=42;
output;
c='ABD';
n=42;
output;
run;
data work.test7_hashed;
set work.test7;
length hash $32;
%mp_rowhash(md5_col=hash, cvars=c, nvars=n)
run;
proc sql noprint;
select count(distinct hash) into: t7_distinct trimmed
from work.test7_hashed;
quit;
%mp_assert(
iftrue=(&t7_distinct=2),
desc=Different rows produce different hashes,
outds=work.test_results
)
/* test 8 - leading blanks in character variables are retained */
data work.test8;
length c $5;
c=' f';
output;
c='f';
output;
run;
data work.test8_hashed;
set work.test8;
length hash $32;
%mp_rowhash(md5_col=hash, cvars=c)
run;
proc sql noprint;
select count(distinct hash) into: t8_distinct trimmed
from work.test8_hashed;
quit;
%mp_assert(
iftrue=(&t8_distinct=2),
desc=Leading blanks are retained in character hashes,
outds=work.test_results
)
-26
View File
@@ -129,30 +129,4 @@ run;
desc=Test4 - ISFORMAT,
test=EQUALS 6,
outds=work.test_results
)
/**
* Test 5 - ISLIB
*/
data test5;
infile datalines4 dsd;
input;
inf=_infile_;
%mp_validatecol(inf,ISLIB,islib)
if islib=1;
datalines4;
some
!lib
%abort
definite
2fail
nineletrs
.failalso
_valid
;;;;
run;
%mp_assertdsobs(work.test5,
desc=Testing ISLIB,
test=EQUALS 3,
outds=work.test_results
)
@@ -1,30 +0,0 @@
/**
@file
@brief Testing mm_assigndirectlib macro
@details A valid library must first be configured in metadata.
To test success, also define a table for which we can test the existence.
This is a unit test - not part of the full test run, as it would be a
lot of overhead to create an external DB and metadata setup each time.
<h4> SAS Macros </h4>
@li mf_existds.sas
@li mp_assert.sas
@li mp_assertscope.sas
@li mm_assigndirectlib.sas
**/
%let runtest=0;
%let libref=XXX;
%let ds=XXXX;
%mp_assertscope(SNAPSHOT)
%mm_assigndirectlib(&libref)
%mp_assertscope(COMPARE)
%mp_assert(
iftrue=(&runtest=1 and %mf_existds(&libref..&ds)),
desc=Check if &libref..&ds exists
)
@@ -32,7 +32,6 @@ run;
%ms_adduser2group(uid=1,gid=&groupid,mdebug=&sasjs_mdebug,outds=test1)
%mp_assertscope(COMPARE
,ignorelist=MCLIB2_JADP1LEN MCLIB2_JADP2LEN MCLIB2_JADPNUM MCLIB2_JADVLEN
MC2_JADP1LEN MC2_JADP2LEN MC2_JADPNUM MC2_JADVLEN
)
/* check the user is in the output list */
+1 -2
View File
@@ -16,8 +16,7 @@
%mp_assertscope(SNAPSHOT)
%ms_creategroup(&group, desc=The description,mdebug=&sasjs_mdebug,outds=test1)
%mp_assertscope(COMPARE
,ignorelist=MCLIB0_JADP1LEN MCLIB0_JADPNUM MCLIB0_JADVLEN MC0_JADP1LEN
MC0_JADPNUM MC0_JADVLEN
,ignorelist=MCLIB0_JADP1LEN MCLIB0_JADPNUM MCLIB0_JADVLEN
)
%let id=0;
+1 -2
View File
@@ -16,8 +16,7 @@
%mp_assertscope(SNAPSHOT)
%ms_createuser(&user,passwrd,outds=test1,mdebug=&sasjs_mdebug)
%mp_assertscope(COMPARE
,ignorelist=MCLIB0_JADP1LEN MCLIB0_JADPNUM MCLIB0_JADVLEN MC0_JADP1LEN
MC0_JADPNUM MC0_JADVLEN
,ignorelist=MCLIB0_JADP1LEN MCLIB0_JADPNUM MCLIB0_JADVLEN
)
%let id=0;
+1 -2
View File
@@ -21,8 +21,7 @@
%mp_assertscope(SNAPSHOT)
%ms_getgroups(outds=work.test1,mdebug=&sasjs_mdebug)
%mp_assertscope(COMPARE
,ignorelist=MCLIB2_JADP1LEN MCLIB2_JADPNUM MCLIB2_JADVLEN MC2_JADP1LEN
MC2_JADPNUM MC2_JADVLEN
,ignorelist=MCLIB2_JADP1LEN MCLIB2_JADPNUM MCLIB2_JADVLEN
)
/* check the group was created */
+1 -2
View File
@@ -15,8 +15,7 @@
%mp_assertscope(SNAPSHOT)
%ms_getusers(outds=work.test1,mdebug=&sasjs_mdebug)
%mp_assertscope(COMPARE
,ignorelist=MCLIB0_JADP1LEN MCLIB0_JADPNUM MCLIB0_JADVLEN MC0_JADP1LEN
MC0_JADPNUM MC0_JADVLEN
,ignorelist=MCLIB0_JADP1LEN MCLIB0_JADPNUM MCLIB0_JADVLEN
)
%mp_assertdsobs(work.test1,test=ATLEAST 1)
+4 -5
View File
@@ -51,13 +51,12 @@ options mprint;
%ms_triggerstp(/sasjs/tests/&fname2
,outds=work.mySessions
)
%mp_assertscope(COMPARE,ignorelist=MCLIB0_JADP1LEN MCLIB0_JADPNUM
MCLIB0_JADVLEN MC0_JADP1LEN MC0_JADPNUM MC0_JADVLEN
)
%mp_assertscope(COMPARE
,ignorelist=MCLIB0_JADP1LEN MCLIB0_JADPNUM MCLIB0_JADVLEN)
%mp_assert(iftrue=%str(%mf_existds(work.mySessions)=1)
,desc=Testing output exists
,outds=work.test_results
,desc=Testing output exists
,outds=work.test_results
)
%mp_assertdsobs(work.mySessions,
-51
View File
@@ -32,55 +32,4 @@ run;
%mp_assert(
iftrue=(%str(&checkval)=%str(&sysvlong)),
desc=Check if the sysvlong value was created
)
/*
Test that ms_webout(FETCH) retains leading blanks in character values
(simulates the CSV format generated by the sasjs adapter, ie an input
statement in the first row followed by unquoted data)
*/
%let fref2=%mf_getuniquefileref();
data _null_;
file &fref2 lrecl=32767 termstr=crlf;
put 'col1:$char10. col2:best.';
put ' padded,1';
run;
%global _webin_fileref _webin_name;
%let _webin_fileref=&fref2;
%let _webin_name=leadblank;
%let _webin_file_count=1;
%ms_webout(FETCH)
data _null_;
set leadblank;
if col1=' padded' then call symputx('checkblank','PASS');
else call symputx('checkblank',cats('FAIL:',col1));
run;
%mp_assert(
iftrue=(&checkblank=PASS),
desc=ms_webout FETCH retains leading blanks with $char informat
)
/* same test, but with a plain $ informat (as sent by some clients) */
%let fref3=%mf_getuniquefileref();
data _null_;
file &fref3 lrecl=32767 termstr=crlf;
put 'col1:$10. col2:best.';
put ' padded,1';
run;
%let _webin_fileref=&fref3;
%let _webin_name=leadblank2;
%let _webin_file_count=1;
%ms_webout(FETCH)
data _null_;
set leadblank2;
if col1=' padded' then call symputx('checkblank2','PASS');
else call symputx('checkblank2',cats('FAIL:',col1));
run;
%mp_assert(
iftrue=(&checkblank2=PASS),
desc=ms_webout FETCH retains leading blanks with $ informat
)
+1 -4
View File
@@ -10,9 +10,7 @@
**/
/* location in metadata or SAS Drive for temporary files */
%let mcTestAppLoc=/Users/&sysuserid/testresults/sasjs_core/%mf_uid();
%let mcTestContext=SAS Job Execution compute context;
%let mcTestAppLoc=/Public/testresults/sasjs_core/%mf_uid();
/* set defaults */
%mp_init()
@@ -25,7 +23,6 @@ options lrecl=80;
%macro loglevel();
%if "&_debug"="2477" or "&_debug"="fields,log,trace" or "&_debug"="131"
or "&_debug"="128"
%then %do;
%put debug mode activated;
options mprint mprintnest;
-74
View File
@@ -1,74 +0,0 @@
/**
@file
@brief Testing mfv_existsashdat macro function
<h4> SAS Macros </h4>
@li mf_uid.sas
@li mfv_existsashdat.sas
@li mp_assert.sas
@li mp_assertscope.sas
**/
options mprint;
/* ------------------------------------------------------------------------ */
/* Setup: start a CAS session and stage a sashdat file in the Public caslib */
/* ------------------------------------------------------------------------ */
cas mysess;
caslib _all_ assign;
%let testcaslib = Public; /* change this if Public isn't available */
proc cas;
table.caslibInfo result=r / ;
found = 0;
do row over r.CASLibInfo;
if upcase(row.Name) = upcase("&testcaslib") then found = 1;
end;
if found = 0 then do;
print "ERROR: caslib &testcaslib not available";
exit;
end;
quit;
%put NOTE: Using testcaslib=&testcaslib;
%let tab1=T%mf_uid();
proc casutil;
load data=sashelp.baseball outcaslib="&testcaslib" casout="&tab1" replace;
save casdata="&tab1" incaslib="&testcaslib"
casout="&tab1..sashdat" outcaslib="&testcaslib" replace;
droptable casdata="&tab1" incaslib="&testcaslib" quiet;
quit;
/* ------------------------------------------------------------------------ */
%put TEST 1 - returns 1 when the sashdat file exists in the caslib;
/* ------------------------------------------------------------------------ */
%mp_assert(
iftrue=(%mfv_existsashdat(&testcaslib..&tab1)=1),
desc=Test 1 - Check returns 1 for a sashdat that exists
)
/* ------------------------------------------------------------------------ */
%put TEST 2 - returns 0 when the file does not exist in the caslib;
/* ------------------------------------------------------------------------ */
%mp_assertscope(SNAPSHOT)
%mp_assert(
iftrue=(%mfv_existsashdat(&testcaslib..DOESNOTEXIST_%mf_uid())=0),
desc=Check returns 0 for a sashdat that does not exist
)
%mp_assertscope(COMPARE,
desc=Check mfv_existsashdat does not leak macro variables into GLOBAL scope
)
/* ------------------------------------------------------------------------ */
/* Teardown */
/* ------------------------------------------------------------------------ */
proc casutil;
deletesource casdata="&tab1..sashdat" incaslib="&testcaslib" quiet;
quit;
cas mysess terminate;
%let syscc=0;
-69
View File
@@ -1,69 +0,0 @@
/**
@file
@brief Testing mfv_getcaslib macro function
<h4> SAS Macros </h4>
@li mfv_getcaslib.sas
@li mp_assert.sas
@li mp_assertscope.sas
**/
options mprint;
/* ------------------------------------------------------------------------ */
/* Setup: start a CAS session and assign caslibs */
/* ------------------------------------------------------------------------ */
cas mysess;
caslib _all_ assign;
%let testcaslib=Public;
libname castest cas caslib=&testcaslib;
/* ------------------------------------------------------------------------ */
%put TEST 1 - returns the caslib name for a valid CAS libref;
/* ------------------------------------------------------------------------ */
%mp_assert(
iftrue=(%mfv_getcaslib(castest)=%upcase(&testcaslib)),
desc=Check correct caslib name returned for a valid CAS libref
)
/* ------------------------------------------------------------------------ */
%put TEST 2 - returns empty for a non-CAS libref (WORK);
/* ------------------------------------------------------------------------ */
%mp_assert(
iftrue=(%mfv_getcaslib(WORK)=),
desc=Check empty string returned for a non-CAS libref
)
/* ------------------------------------------------------------------------ */
%put TEST 3 - returns empty for a libref that does not exist;
/* ------------------------------------------------------------------------ */
%mp_assert(
iftrue=(%mfv_getcaslib(DOESNOTEXIST)=),
desc=Check empty string returned for a non-existent libref
)
/* ------------------------------------------------------------------------ */
%put TEST 5 - no scope leakage into global macro variables;
/* ------------------------------------------------------------------------ */
%mp_assertscope(SNAPSHOT)
%let _rc=%mfv_getcaslib(castest);
%mp_assertscope(COMPARE,
desc=Check mfv_getcaslib does not leak macro variables into GLOBAL scope,
ignorelist=_RC
)
/* ------------------------------------------------------------------------ */
/* Teardown */
/* ------------------------------------------------------------------------ */
cas mysess terminate;
-31
View File
@@ -1,31 +0,0 @@
/**
@file
@brief Testing mfv_getfolderpath macro function
<h4> SAS Macros </h4>
@li mf_uid.sas
@li mfv_getfolderpath.sas
@li mfv_getpathuri.sas
@li mp_assert.sas
@li mv_createfolder.sas
**/
options mprint sgen;
%let folder=%mf_uid();
/* create a folder */
%mv_createfolder(path=&mcTestAppLoc/&folder)
%mp_assert(
iftrue=(&syscc=0),
desc=no errs on folder creation
)
%let uri=%mfv_getpathuri(&mcTestAppLoc/&folder);
%put %mfv_getfolderpath(&uri);
%mp_assert(
iftrue=("%mfv_getfolderpath(&uri)"="&mcTestAppLoc/&folder"),
desc=Check if correct folder was returned
)
+1 -1
View File
@@ -14,7 +14,7 @@ options mprint sgen;
%let file=%mf_uid();
/* create a file */
/* create a folder */
filename somefile temp;
data _null_;
file somefile;
-152
View File
@@ -1,152 +0,0 @@
/**
@file
@brief Testing mv_castabload macro
<h4> SAS Macros </h4>
@li mf_uid.sas
@li mp_assert.sas
@li mp_assertscope.sas
@li mv_castabload.sas
**/
options mprint;
/* -------------------------------------------------------------------- */
/* Setup: start a CAS session and stage a source file in the caslib */
/* -------------------------------------------------------------------- */
cas mysess;
caslib _all_ assign;
%let testcaslib=Public;
proc cas;
table.caslibInfo result=r / ;
found=0;
do row over r.CASLibInfo;
if upcase(row.Name)=upcase("&testcaslib") then found=1;
end;
if found=0 then do;
print "ERROR: caslib &testcaslib not available";
exit;
end;
quit;
%put NOTE: Using testcaslib=&testcaslib;
%let tab1=T%mf_uid();
/* Save a sashdat source file then drop the in-memory copy so the first
mv_castabload call has something to load */
proc casutil;
load data=sashelp.baseball
outcaslib="&testcaslib" casout="&tab1" replace;
save casdata="&tab1" incaslib="&testcaslib"
casout="&tab1..sashdat" outcaslib="&testcaslib" replace;
droptable casdata="&tab1" incaslib="&testcaslib" quiet;
quit;
libname mylib cas caslib="&testcaslib";
/* -------------------------------------------------------------------- */
%put TEST 1 - load a table that is not in memory;
/* -------------------------------------------------------------------- */
/* Confirm table is absent before the call */
%let _tabexists=0;
proc cas;
table.tableExists result=r /
caslib="&testcaslib" name="&tab1";
if r.exists > 0 then call symputx('_tabexists','1');
quit;
%mp_assert(
iftrue=(&_tabexists=0),
desc=Check table is not in memory before mv_castabload
)
%mv_castabload(lib=mylib, table=&tab1, mdebug=1)
%let _tabexists=0;
proc cas;
table.tableExists result=r /
caslib="&testcaslib" name="&tab1";
if r.exists > 0 then call symputx('_tabexists','1');
quit;
%mp_assert(
iftrue=(&_tabexists=1),
desc=Check table is in memory after mv_castabload
)
/* -------------------------------------------------------------------- */
%put TEST 2 - reload fetches a fresh copy and discards in-memory changes;
/* -------------------------------------------------------------------- */
/* Append a sentinel row to the in-memory table */
data work.extra;
set mylib.&tab1;
name='TESTROW';
output;
stop;
run;
proc casutil;
load data=work.extra casout="&tab1"
outcaslib="&testcaslib" append;
quit;
%let _modified=0;
proc sql noprint;
select count(*) into :_modified
from mylib.&tab1
where name='TESTROW';
quit;
%mp_assert(
iftrue=(&_modified=1),
desc=Check sentinel row is present in memory before reload
)
/* Drop the table and reload - source file does not have the sentinel */
proc casutil;
droptable casdata="&tab1" incaslib="&testcaslib" quiet;
droptable casdata="&tab1" incaslib="&testcaslib" quiet;
quit;
%mp_assertscope(SNAPSHOT)
%mv_castabload(lib=mylib, table=&tab1, mdebug=1)
%mp_assertscope(COMPARE,
desc=Check mv_castabload does not leak macro variables into GLOBAL scope
)
%let _after=0;
proc sql noprint;
select count(*) into :_after
from mylib.&tab1
where name='TESTROW';
quit;
%mp_assert(
iftrue=(&_after=0),
desc=Check sentinel row is absent after reload from source
)
/* -------------------------------------------------------------------- */
/* Teardown */
/* -------------------------------------------------------------------- */
libname mylib clear;
proc casutil;
droptable casdata="&tab1" incaslib="&testcaslib" quiet;
droptable casdata="&tab1" incaslib="&testcaslib" quiet;
deletesource casdata="&tab1..sashdat"
incaslib="&testcaslib" quiet;
quit;
cas mysess terminate;
%let syscc=0;
-158
View File
@@ -1,158 +0,0 @@
/**
@file
@brief Testing mv_castabsave macro
<h4> SAS Macros </h4>
@li mf_uid.sas
@li mp_assert.sas
@li mp_assertscope.sas
@li mv_castabsave.sas
**/
options mprint;
/* -------------------------------------------------------------------- */
/* Setup: start a CAS session and load a table that has a tracked */
/* source file so mv_castabsave can discover it via the REST API */
/* -------------------------------------------------------------------- */
cas mysess;
caslib _all_ assign;
%let testcaslib=Public;
proc cas;
table.caslibInfo result=r / ;
found=0;
do row over r.CASLibInfo;
if upcase(row.Name)=upcase("&testcaslib") then found=1;
end;
if found=0 then do;
print "ERROR: caslib &testcaslib not available";
exit;
end;
quit;
%put NOTE: Using testcaslib=&testcaslib;
%let tab1=T%mf_uid();
/* Load sashelp.class into CAS, save as sashdat, reload from that file
so the table has a tracked source path (needed for REST discovery) */
proc casutil;
load data=sashelp.class
outcaslib="&testcaslib" casout="&tab1" replace;
save casdata="&tab1" incaslib="&testcaslib"
casout="&tab1..sashdat" outcaslib="&testcaslib" replace;
/* Drop any existing global-scope version before promoting */
/* runs twice (with quiet) as first would drop local scope if exists */
droptable casdata="&tab1" incaslib="&testcaslib" quiet;
droptable casdata="&tab1" incaslib="&testcaslib" quiet;
load casdata="&tab1..sashdat" incaslib="&testcaslib"
casout="&tab1" outcaslib="&testcaslib" promote;
quit;
libname mylib cas caslib="&testcaslib";
/* -------------------------------------------------------------------- */
%put TEST 1 - save in-memory table back to disk + no scope leakage;
/* -------------------------------------------------------------------- */
/* Source file is removed so that the reload proves mv_castabsave
created the file from scratch, not that a prior version existed */
proc casutil;
deletesource casdata="&tab1..sashdat"
incaslib="&testcaslib" quiet;
quit;
/* Insert a sentinel row - it must survive the full save/drop/reload */
data work.appendme;
set mylib.&tab1;
name='TESTROW';
output;
stop;
proc casutil;
load data=work.appendme casout="&tab1" outcaslib="&testcaslib" append;
quit;
%mp_assertscope(SNAPSHOT)
%mv_castabsave(lib=mylib, table=&tab1, mdebug=1)
%mp_assertscope(COMPARE,
desc=Check mv_castabsave does not leak macro variables into GLOBAL scope,
ignorelist=MC0_JADP1LEN MC0_JADP2LEN MC0_JADP3LEN MC0_JADPNUM MC0_JADVLEN
)
proc casutil;
droptable casdata="&tab1" incaslib="&testcaslib" quiet;
droptable casdata="&tab1" incaslib="&testcaslib" quiet;
load casdata="&tab1..sashdat" incaslib="&testcaslib"
casout="&tab1" outcaslib="&testcaslib" promote;
quit;
%let _rowcount=0;
proc sql noprint;
select count(*) into :_rowcount
from mylib.&tab1
where name='TESTROW';
quit;
%mp_assert(
iftrue=(&_rowcount=1),
desc=Check inserted row survives mv_castabsave round-trip to disk
)
/* -------------------------------------------------------------------- */
%put TEST 2 - save overwrites an existing source file;
/* -------------------------------------------------------------------- */
/* Source file already exists from the TEST 1 save - append a new row */
data work.appendme;
set mylib.&tab1;
name='TESTROW2';
output;
stop;
proc casutil;
load data=work.appendme casout="&tab1"
outcaslib="&testcaslib" append;
quit;
%mv_castabsave(lib=mylib, table=&tab1, mdebug=1)
proc casutil;
droptable casdata="&tab1" incaslib="&testcaslib" quiet;
droptable casdata="&tab1" incaslib="&testcaslib" quiet;
load casdata="&tab1..sashdat" incaslib="&testcaslib"
casout="&tab1" outcaslib="&testcaslib" promote;
quit;
%let _rowcount=0;
proc sql noprint;
select count(*) into :_rowcount
from mylib.&tab1
where name='TESTROW2';
quit;
%mp_assert(
iftrue=(&_rowcount=1),
desc=Check inserted row survives save over an existing source file
)
/* -------------------------------------------------------------------- */
/* Teardown */
/* -------------------------------------------------------------------- */
libname mylib clear;
proc casutil;
droptable casdata="&tab1" incaslib="&testcaslib" quiet;
deletesource casdata="&tab1..sashdat"
incaslib="&testcaslib" quiet;
quit;
cas mysess terminate;
%let syscc=0;
+10 -35
View File
@@ -23,7 +23,7 @@ data _null_;
put 'hello testings';
run;
%mp_assertscope(SNAPSHOT)
%mv_createfile(path=&mcTestAppLoc, name=&file..txt,inref=somefile,mdebug=1)
%mv_createfile(path=&mcTestAppLoc/temp, name=&file..txt,inref=somefile,mdebug=1)
%mp_assertscope(COMPARE
,ignorelist=MCLIB0_JADP1LEN MCLIB0_JADP2LEN MCLIB0_JADPNUM
MCLIB0_JADVLEN MCLIB2_JADP1LEN
@@ -32,7 +32,7 @@ run;
)
%mp_assert(
iftrue=(%mfv_existfile(&mcTestAppLoc/&file..txt)=1),
iftrue=(%mfv_existfile(&mcTestAppLoc/temp/&file..txt)=1),
desc=Check if created file exists
)
@@ -42,10 +42,10 @@ data _null_;
file f2;
put '<html><body><p>Hello world</p></body></html>';
run;
%mv_createfile(path=&mcTestAppLoc, name=test.html,inref=f2,mdebug=1)
%mv_createfile(path=&mcTestAppLoc/temp, name=test.html,inref=f2,mdebug=1)
%mp_assert(
iftrue=(%mfv_existfile(&mcTestAppLoc/test.html)=1),
iftrue=(%mfv_existfile(&mcTestAppLoc/temp/test.html)=1),
desc=Check if created file exists
)
@@ -55,10 +55,10 @@ x=1;
run;
filename ds "%sysfunc(pathname(work))/temp.sas7bdat";
%mv_createfile(path=&mcTestAppLoc, name=&file..sas7bdat,inref=ds,mdebug=1)
%mv_createfile(path=&mcTestAppLoc/temp, name=&file..sas7bdat,inref=ds,mdebug=1)
%mp_assert(
iftrue=(%mfv_existfile(&mcTestAppLoc/&file..sas7bdat)=1),
iftrue=(%mfv_existfile(&mcTestAppLoc/temp/&file..sas7bdat)=1),
desc=Check if created dataset exists
)
@@ -68,44 +68,19 @@ data _null_;
file f4;
put '%put hello FromSASStudioBailey; ';
run;
%mv_createfile(path=&mcTestAppLoc, name=test4.sas,inref=f4,mdebug=1)
%mv_createfile(path=&mcTestAppLoc/temp, name=test4.sas,inref=f4,mdebug=1)
%mp_assert(
iftrue=(%mfv_existfile(&mcTestAppLoc/test4.sas)=1),
iftrue=(%mfv_existfile(&mcTestAppLoc/temp/test4.sas)=1),
desc=Check if created sas program exists
)
%put TEST 5 - reading from files service and writing back;
filename sendfrom filesrvc folderpath="&mcTestAppLoc" filename='test4.sas';
filename sendfrom filesrvc folderpath="&mcTestAppLoc/temp" filename='test4.sas';
OPTIONS MERROR SYMBOLGEN MLOGIC MPRINT;
%mv_createfile(path=&mcTestAppLoc,name=test5.sas,inref=sendfrom,mdebug=1) ;
%mv_createfile(path=&mcTestAppLoc/temp,name=test5.sas,inref=sendfrom,mdebug=1) ;
%put TEST 6 - try the find and replace;
filename f6 temp;
data _null_;
file f6;
put '//Hello world!';
put 'let var=/some/path/name;';
run;
%let in=/some/path/name;
%let out=/final/destination;
%mv_createfile(path=&mcTestAppLoc, name=test6.js,inref=f6,mdebug=1,swap=in out)
filename getback filesrvc folderpath="&mcTestAppLoc" filename='test6.js';
%let test6=0;
data _null_;
infile getback;
input;
if _infile_="let var=&out;" then call symputx('test6',1);
putlog _infile_;
run;
%mp_assert(
iftrue=(&test6=1),
desc=Check if find & replace worked
)
+3 -30
View File
@@ -5,7 +5,6 @@
<h4> SAS Macros </h4>
@li mf_uid.sas
@li mp_assert.sas
@li mp_assertscope.sas
@li mv_createfolder.sas
@li mv_deleteviyafolder.sas
@li mv_getfoldermembers.sas
@@ -16,11 +15,7 @@
%let folder=%mf_uid();
/* create a folder */
%mp_assertscope(SNAPSHOT)
%mv_createfolder(path=&mcTestAppLoc/temp/&folder/&folder)
%mp_assertscope(COMPARE, ignorelist=MC0_JADP1LEN MC0_JADP2LEN MC0_JADPNUM
MC0_JADVLEN MC2_JADP1LEN MC2_JADP2LEN MC2_JADPNUM MC2_JADVLEN
)
%mv_getfoldermembers(root=&mcTestAppLoc/temp/&folder, outds=work.folders)
@@ -37,39 +32,17 @@ run;
)
/* create a folder without output dataset as part of the original macro */
%mv_createfolder(path=&mcTestAppLoc/temp/&folder/f2
,outds=folders2,mdebug=&sasjs_mdebug
)
%mv_createfolder(path=&mcTestAppLoc/temp/&folder/folder2,outds=folders2)
%let test=0;
data _null_;
set work.folders2;
putlog (_all_)(=);
if not missing(self_uri) then call symputx('test2',1);
if not missing(self_uri) and not missing(parent_uri)
then call symputx('test2',1);
run;
%mp_assert(
iftrue=(&test2=1),
desc=Check if outds param works
)
/* create a folder with full stops */
%let newfolder=%mf_uid().2.1;
%mv_createfolder(path=&mcTestAppLoc/temp/&newfolder
,outds=work.folders3
,mdebug=&sasjs_mdebug
)
%mv_getfoldermembers(root=&mcTestAppLoc/temp, outds=work.folders3)
%let test3=0;
data _null_;
set work.folders3;
putlog (_all_)(=);
if name="&newfolder" then call symputx('test3',1);
run;
%mp_assert(
iftrue=(&test3=1),
desc=Check if folder with full stops can be successfully created
)
+1 -1
View File
@@ -32,7 +32,7 @@ run;
)
%put TEST1: checking web service code;
data work.test_results;
length test_result $4 test_description $256 test_comments $256;
length test_description $256 test_result $4 test_comments $256;
if _n_=1 then call missing (of _all_);
infile compare end=eof;
input;
@@ -1,96 +0,0 @@
/**
@file
@brief Testing mv_getviyafileextparms macro
<h4> SAS Macros </h4>
@li mf_isblank.sas
@li mp_assert.sas
@li mp_assertscope.sas
@li mv_getviyafileextparms.sas
**/
options mprint;
%let mvarIgnoreList =
MC0_JADP1LEN MC0_JADP2LEN MC0_JADP3LEN MC0_JADPNUM MC0_JADVLEN
SASJSPROCESSMODE SASJS_STPSRV_HEADER_LOC;
%put TEST 1 - Test with common extension, requesting only typeDefName parameter;
%mp_assertscope(SNAPSHOT)
%mv_getviyafileextparms(ext=txt, typeDefNameVar=viyaTypeDefName)
%mp_assertscope(COMPARE
,ignorelist=&mvarIgnoreList viyaTypeDefName
)
%mp_assert(
iftrue=(not %mf_isBlank(&viyaTypeDefName)),
desc=Check the requested macro variable viyaTypeDefName is not blank.
)
%put TEST 2 - Test with common extension, requesting only properties parameter;
%mp_assertscope(SNAPSHOT)
%mv_getviyafileextparms(ext=html, propertiesVar=viyaProperties)
%mp_assertscope(COMPARE
,ignorelist=&mvarIgnoreList viyaProperties
)
%mp_assert(
iftrue=(not %mf_isBlank(%superq(viyaProperties))),
desc=Check the requested macro variable viyaProperties is not blank.
)
%put TEST 3 - Test with common extension, requesting only mediaType parameter;
%mp_assertscope(SNAPSHOT)
%mv_getviyafileextparms(ext=mp3, mediaTypeVar=viyaMediaType)
%mp_assertscope(COMPARE
,ignorelist=&mvarIgnoreList viyaMediaType
)
%mp_assert(
iftrue=(not %mf_isBlank(&viyaMediaType)),
desc=Check the requested macro variable viyaMediaType is not blank.
)
%put TEST 4 - Test with common extension, requesting all parameters;
%mp_assertscope(SNAPSHOT)
%mv_getviyafileextparms(
ext=css,
typeDefNameVar=cssViyaTypeDefName,
propertiesVar=cssViyaProperties,
mediaTypeVar=cssViyaMediaType
)
%mp_assertscope(COMPARE
,ignorelist=
&mvarIgnoreList cssViyaTypeDefName cssViyaProperties cssViyaMediaType
)
%mp_assert(
iftrue=(not ( %mf_isBlank(&cssViyaTypeDefName) or
%mf_isBlank(%superq(cssViyaProperties)) or
%mf_isBlank(&cssViyaMediaType) ) ),
desc=Check a full set of requested macro variables are not blank.
)
%put TEST 5 - Test with invalid extension - requested parameters will be blank;
%mp_assertscope(SNAPSHOT)
%mv_getviyafileextparms(
ext=xxxINVALIDxxx,
typeDefNameVar=invalidTypeDefName,
propertiesVar=invalidProperties,
mediaTypeVar=invalidMediaType
)
%mp_assertscope(COMPARE
,ignorelist=
&mvarIgnoreList invalidTypeDefName invalidProperties invalidMediaType
)
%mp_assert(
iftrue=(
%mf_isBlank(&invalidTypeDefName) and
%mf_isBlank(%superq(invalidProperties)) and
%mf_isBlank(&invalidMediaType)
),
desc=Check the requested macro variables are all blank.
)
+5 -5
View File
@@ -33,19 +33,19 @@ data _null_;
;
run;
%mv_createjob(path=&mcTestAppLoc,name=demo1,code=testprog)
%mv_createjob(path=&mcTestAppLoc,name=demo2,code=testprog)
%mv_createjob(path=/Public/temp,name=demo1,code=testprog)
%mv_createjob(path=/Public/temp,name=demo2,code=testprog)
data work.inputjobs;
_contextName="&mcTestContext";
_contextName='SAS Job Execution compute context';
do flow_id=1 to 2;
do i=1 to 4;
_program="&mcTestAppLoc/demo1";
_program='/Public/temp/demo1';
macrovar1=10*i;
macrovar2=4*i;
output;
i+1;
_program="&mcTestAppLoc/demo2";
_program='/Public/temp/demo2';
macrovar1=40*i;
macrovar2=44*i;
output;
+5 -5
View File
@@ -29,19 +29,19 @@ data _null_;
;
run;
%mv_createjob(path=&mcTestAppLoc,name=demo1,code=testprog)
%mv_createjob(path=&mcTestAppLoc,name=demo2,code=testprog)
%mv_createjob(path=/Public/temp,name=demo1,code=testprog)
%mv_createjob(path=/Public/temp,name=demo2,code=testprog)
data work.inputjobs;
_contextName="&mcTestContext";
_contextName='SAS Job Execution compute context';
do flow_id=1 to 2;
do i=1 to 4;
_program="&mcTestAppLoc/demo1";
_program='/Public/temp/demo1';
macrovar1=10*i;
macrovar2=4*i;
output;
i+1;
_program="&mcTestAppLoc/demo2";
_program='/Public/temp/demo2';
macrovar1=40*i;
macrovar2=44*i;
output;
-192
View File
@@ -1,192 +0,0 @@
/**
@file
@brief Testing mx_append2pgm.sas macro
Be sure to run <code>%let mcTestAppLoc=/Public/temp/macrocore;</code> when
running in Studio
<h4> SAS Macros </h4>
@li mf_getplatform.sas
@li mf_uid.sas
@li mp_assert.sas
@li mp_assertscope.sas
@li ms_createfile.sas
@li mv_createfile.sas
@li mm_createstp.sas
@li mx_append2pgm.sas
@li mx_getcode.sas
**/
/**
* Test 1 - Append content to an existing program and verify combined output
* Also checking for scope leakage
*/
/* create a unique name for the program */
%let item=test_%mf_uid();
/* create the initial program with some code */
filename initpgm temp;
data _null_;
file initpgm;
put '%put ORIGINAL LINE;';
run;
%macro setup_pgm();
%let platform=%mf_getplatform();
%if &platform=SASJS %then %do;
%ms_createfile(&mcTestAppLoc/temp/&item..sas, inref=initpgm)
%end;
%else %if &platform=SASVIYA %then %do;
%mv_createfile(path=&mcTestAppLoc/temp, name=&item..sas, inref=initpgm)
%end;
%else %do;
%let work=%sysfunc(pathname(work));
data _null_;
file "&work/&item..sas";
infile initpgm;
input;
put _infile_;
run;
%mm_createstp(stpname=&item
,filename=&item..sas
,directory=&work
,tree=&mcTestAppLoc/temp
,stptype=2
,minify=NO
)
%end;
%mend setup_pgm;
%setup_pgm()
/* create the content to append */
filename toappnd temp;
data _null_;
file toappnd;
put '%put APPENDED LINE;';
run;
/* run the macro under test with scope checks */
%mp_assertscope(SNAPSHOT)
%mx_append2pgm(&mcTestAppLoc/temp/&item, inref=toappnd)
%mp_assertscope(COMPARE,
desc=Test 1: mx_append2pgm does not leak scope,
outds=work.test_results,
ignorelist=MC2_JADP1LEN MC2_JADP2LEN MC2_JADPNUM MC2_JADVLEN MC2_JADP3LEN
)
%mp_assert(
iftrue=(&syscc=0),
desc=Test 1: No errors after appending content to program,
outds=work.test_results
)
/**
* Test 2 - Verify the appended content is present
* Fetch the modified program and check both original and appended lines exist
*/
%let test2_orig=0;
%let test2_appd=0;
%macro verify_test2();
%let platform=%mf_getplatform();
%if &platform=SASVIYA %then %do;
filename verifrf filesrvc folderpath="&mcTestAppLoc/temp";
data _null_;
infile verifrf("&item..sas") lrecl=32000;
input;
if index(_infile_,'ORIGINAL LINE') then call symputx('test2_orig','1');
if index(_infile_,'APPENDED LINE') then call symputx('test2_appd','1');
run;
filename verifrf clear;
%end;
%else %do;
%mx_getcode(&mcTestAppLoc/temp/&item, outref=verifrf)
data _null_;
infile verifrf lrecl=32000;
input;
if index(_infile_,'ORIGINAL LINE') then call symputx('test2_orig','1');
if index(_infile_,'APPENDED LINE') then call symputx('test2_appd','1');
run;
%end;
%mend verify_test2;
%verify_test2()
%mp_assert(
iftrue=(&test2_orig=1),
desc=Test 2a: Original content is preserved after append,
outds=work.test_results
)
%mp_assert(
iftrue=(&test2_appd=1),
desc=Test 2b: Appended content is present in modified program,
outds=work.test_results
)
/**
* Test 3 - Append multiple times to ensure repeated appends work
*/
filename toappd2 temp;
data _null_;
file toappd2;
put '%put SECOND APPEND;';
run;
%mp_assertscope(SNAPSHOT)
%mx_append2pgm(&mcTestAppLoc/temp/&item, inref=toappd2)
%mp_assertscope(COMPARE,
desc=Test 3: mx_append2pgm does not leak scope on second call,
outds=work.test_results
)
/* verify all three pieces of content exist */
%let test3_orig=0;
%let test3_appd=0;
%let test3_app2=0;
%macro verify_test3();
%let platform=%mf_getplatform();
%if &platform=SASVIYA %then %do;
filename verifr2 filesrvc folderpath="&mcTestAppLoc/temp";
data _null_;
infile verifr2("&item..sas") lrecl=32000;
input;
if index(_infile_,'ORIGINAL LINE') then call symputx('test3_orig','1');
if index(_infile_,'APPENDED LINE') then call symputx('test3_appd','1');
if index(_infile_,'SECOND APPEND') then call symputx('test3_app2','1');
run;
filename verifr2 clear;
%end;
%else %do;
%mx_getcode(&mcTestAppLoc/temp/&item, outref=verifr2)
data _null_;
infile verifr2 lrecl=32000;
input;
if index(_infile_,'ORIGINAL LINE') then call symputx('test3_orig','1');
if index(_infile_,'APPENDED LINE') then call symputx('test3_appd','1');
if index(_infile_,'SECOND APPEND') then call symputx('test3_app2','1');
run;
%end;
%mend verify_test3;
%verify_test3()
%mp_assert(
iftrue=(&test3_orig=1),
desc=Test 3a: Original content still present after second append,
outds=work.test_results
)
%mp_assert(
iftrue=(&test3_appd=1),
desc=Test 3b: First appended content still present after second append,
outds=work.test_results
)
%mp_assert(
iftrue=(&test3_app2=1),
desc=Test 3c: Second appended content is present,
outds=work.test_results
)
-307
View File
@@ -1,307 +0,0 @@
/**
@file
@brief Testing mx_createjob.sas macro
Be sure to run <code>%let mcTestAppLoc=/Public/temp/macrocore;</code> when
running in Studio
<h4> SAS Macros </h4>
@li mx_createjob.sas
@li mp_assert.sas
@li mf_getuniquefileref.sas
@li mp_assertscope.sas
**/
/**
* Test 1 - Basic job creation with default parameters
* Also checking for scope leakage
*/
filename ft15f001 temp;
parmcards4;
data example1;
set sashelp.class;
run;
%put Job executed successfully;
;;;;
%mp_assertscope(SNAPSHOT)
%mx_createjob(path=&mcTestAppLoc/jobs,name=testjob1,replace=YES)
%mp_assertscope(COMPARE,ignorelist=MC0_JADP1LEN MC0_JADP2LEN MC0_JADPNUM
MC0_JADVLEN MC2_JADP1LEN MC2_JADP2LEN MC2_JADPNUM MC2_JADVLEN MC4_JADP1LEN
MC4_JADP2LEN MC4_JADPNUM MC4_JADVLEN
)
%mp_assert(
iftrue=(&syscc=0),
desc=Test 1: No errors after basic job creation,
outds=work.test_results
)
/**
* Test 2 - Job creation with custom description
*/
filename ft15f001 temp;
parmcards4;
data example2;
set sashelp.cars;
run;
;;;;
%mx_createjob(
path=&mcTestAppLoc/jobs,
name=testjob2,
desc=Custom job description for testing,
replace=YES
)
%mp_assert(
iftrue=(&syscc=0),
desc=Test 2: Job created with custom description,
outds=work.test_results
)
/**
* Test 3 - Job creation with precode
*/
filename precode1 temp;
data _null_;
file precode1;
put '%let testvar=PreCodeValue;';
put '%put &=testvar;';
run;
filename ft15f001 temp;
parmcards4;
data example3;
set sashelp.class;
precode_var="&testvar";
run;
;;;;
%mx_createjob(
path=&mcTestAppLoc/jobs,
name=testjob3,
precode=precode1,
replace=YES
)
%mp_assert(
iftrue=(&syscc=0),
desc=Test 3: Job created with precode parameter,
outds=work.test_results
)
filename precode1 clear;
/**
* Test 4 - Job creation with multiple code filerefs
*/
%let code1=%mf_getuniquefileref();
%let code2=%mf_getuniquefileref();
filename &code1 temp;
data _null_;
file &code1;
put 'data work.part1;';
put ' set sashelp.class(obs=5);';
put 'run;';
run;
filename &code2 temp;
data _null_;
file &code2;
put 'data work.part2;';
put ' set sashelp.class(firstobs=6);';
put 'run;';
run;
%mx_createjob(
path=&mcTestAppLoc/jobs,
name=testjob4,
code=&code1 &code2,
replace=YES
)
%mp_assert(
iftrue=(&syscc=0),
desc=Test 4: Job created with multiple code filerefs,
outds=work.test_results
)
filename &code1 clear;
filename &code2 clear;
/**
* Test 5 - Job creation with both precode and multiple code files
*/
%let pre1=%mf_getuniquefileref();
%let pre2=%mf_getuniquefileref();
%let main1=%mf_getuniquefileref();
filename &pre1 temp;
data _null_;
file &pre1;
put '%let globalvar1=Value1;';
run;
filename &pre2 temp;
data _null_;
file &pre2;
put '%let globalvar2=Value2;';
run;
filename &main1 temp;
data _null_;
file &main1;
put 'data work.combined;';
put ' var1="&globalvar1";';
put ' var2="&globalvar2";';
put ' output;';
put 'run;';
run;
%mx_createjob(
path=&mcTestAppLoc/jobs,
name=testjob5,
precode=&pre1 &pre2,
code=&main1,
desc=Job with multiple precode and code files,
replace=YES
)
%mp_assert(
iftrue=(&syscc=0),
desc=Test 5: Job created with multiple precode and code files,
outds=work.test_results
)
filename &pre1 clear;
filename &pre2 clear;
filename &main1 clear;
/**
* Test 6 - Job creation with special characters in code
*/
filename ft15f001 temp;
parmcards4;
data example6;
length text $200;
text='Special chars: & % $ # @ !';
output;
text="Quotes: 'single' and ""double""";
output;
run;
%put Test with special characters;
;;;;
%mx_createjob(
path=&mcTestAppLoc/jobs,
name=testjob6,
desc=Job with special characters in code,
replace=YES
)
%mp_assert(
iftrue=(&syscc=0),
desc=Test 6: Job created with special characters in code,
outds=work.test_results
)
/**
* Test 7 - Job creation with macro code
*/
filename ft15f001 temp;
parmcards4;
%macro testmacro();
data example7;
set sashelp.class;
where age > 12;
run;
%mend testmacro;
%testmacro()
;;;;
%mx_createjob(
path=&mcTestAppLoc/jobs,
name=testjob7,
desc=Job containing macro definitions,
replace=YES
)
%mp_assert(
iftrue=(&syscc=0),
desc=Test 7: Job created with macro code,
outds=work.test_results
)
/**
* Test 8 - Job creation with empty code (edge case)
*/
filename ft15f001 temp;
data _null_;
file ft15f001;
put '/* Empty job for testing */';
run;
%mx_createjob(
path=&mcTestAppLoc/jobs,
name=testjob8,
desc=Job with minimal code,
replace=YES
)
%mp_assert(
iftrue=(&syscc=0),
desc=Test 8: Job created with minimal code,
outds=work.test_results
)
/**
* Test 9 - Job creation with long code block
*/
filename ft15f001 temp;
data _null_;
file ft15f001;
put 'data work.longtest;';
do i=1 to 50;
put ' var' i +(-1) '=' i ';';
end;
put ' output;';
put 'run;';
run;
%mx_createjob(
path=&mcTestAppLoc/jobs,
name=testjob9,
desc=Job with many variables,
replace=YES
)
%mp_assert(
iftrue=(&syscc=0),
desc=Test 9: Job created with long code block,
outds=work.test_results
)
/**
* Test 10 - Replace existing job (replace=YES)
*/
filename ft15f001 temp;
parmcards4;
data example10_v1;
set sashelp.class;
run;
;;;;
%mx_createjob(path=&mcTestAppLoc/jobs,name=testjob10,replace=YES)
/* Now replace it */
filename ft15f001 temp;
parmcards4;
data example10_v2;
set sashelp.cars;
run;
;;;;
%mx_createjob(path=&mcTestAppLoc/jobs,name=testjob10,replace=YES)
%mp_assert(
iftrue=(&syscc=0),
desc=Test 10: Job replaced successfully with replace=YES,
outds=work.test_results
)
+4 -5
View File
@@ -3,10 +3,10 @@
@brief Testing mx_testservice.sas macro
Be sure to run <code>%let mcTestAppLoc=/Public/temp/macrocore;</code> when
running in Studio
runnin in Studio
<h4> SAS Macros </h4>
@li mx_createwebservice.sas
@li mp_createwebservice.sas
@li mx_testservice.sas
@li mp_assert.sas
@@ -44,7 +44,6 @@ data work.somedata1 work.somedata2;
x=1;
y=' t"w"o';
z=.z;
y2=' two';
label x='x factor';
output;
run;
@@ -59,14 +58,14 @@ run;
%let test1=FAIL;
data _null_;
set testlib1.somedata1;
if x=1 and y=' t"w"o' and z="Z" and y2=' two' then call symputx('test1','PASS');
if x=1 and y=' t"w"o' and z="Z" then call symputx('test1','PASS');
putlog (_all_)(=);
run;
%let test2=FAIL;
data _null_;
set testlib1.somedata2;
if x=1 and y=' t"w"o' and z="Z" and y2=' two' then call symputx('test2','PASS');
if x=1 and y=' t"w"o' and z="Z" then call symputx('test2','PASS');
putlog (_all_)(=);
run;
+1 -8
View File
@@ -33,23 +33,16 @@
msg=Cannot enter mfv_existfolder.sas with syscc=&syscc
)
%local fref rc var;
%local fref rc;
%let fref=%mf_getuniquefileref();
%if %sysfunc(filename(fref,,filesrvc,folderPath="&path"))=0 %then %do;
1
%let var=_FILESRVC_&fref._URI;
%let rc=%sysfunc(filename(fref));
%symdel &var;
%end;
%else %do;
0
%let syscc=0;
%end;
%mf_abort(
iftrue=(&syscc ne 0),
msg=Cannot leave mfv_existfolder.sas with syscc=&syscc
)
%mend mfv_existfolder;
+9 -8
View File
@@ -6,12 +6,12 @@
%if %mfv_existsashdat(libds=casuser.sometable) %then %put yes it does!;
The function uses `dosubl()` to run the `table.fileinfo` action, for the
specified library, filtering for `*.sashdat` tables.
specified library, filtering for `*.sashdat` tables. The results are stored
in a WORK table (&outprefix._&lib). If that table already exists, it is
queried instead, to avoid the dosubl() performance hit.
Results are cached in a WORK table (&outprefix._&lib). If that table
already exists it is queried directly to avoid the dosubl() overhead.
To force a rescan, use a new `&outprefix` value or delete the cache
table before calling.
To force a rescan, just use a new `&outprefix` value, or delete the table(s)
before running the function.
@param [in] libds library.dataset
@param [out] outprefix= (work.mfv_existsashdat)
@@ -24,12 +24,13 @@
@author Mathieu Blauw
**/
%macro mfv_existsashdat(libds,outprefix=work.mfv_existsashdat);
%macro mfv_existsashdat(libds,outprefix=work.mfv_existsashdat
);
%local rc dsid name lib ds;
%let lib=%upcase(%scan(&libds,1,'.'));
%let ds=%upcase(%scan(&libds,-1,'.'));
/* if cache table does not exist, build it */
/* if table does not exist, create it */
%if %sysfunc(exist(&outprefix._&lib)) ne 1 %then %do;
%let rc=%sysfunc(dosubl(%nrstr(
/* Read in table list (once per &lib per session) */
@@ -40,7 +41,7 @@
quit;
/* Only keep name, without file extension */
data &outprefix._&lib;
set &outprefix._&lib(where=(upcase(Name) like '%.SASHDAT') keep=Name);
set &outprefix._&lib(where=(Name like '%.sashdat') keep=Name);
Name=upcase(scan(Name,1,'.'));
run;
)));
-37
View File
@@ -1,37 +0,0 @@
/**
@file mfv_getcaslib.sas
@brief Returns the CAS caslib name for a given SAS libref
@details Pure macro function. Reads sashelp.vlibnam and returns
the sysvalue where sysname='Caslib' for the given libref. This
is useful when the caslib name and libref name may differ.
Usage:
%put %mfv_getcaslib(lib=PUBLIC);
@param [in] lib SAS libref for which to return the CAS caslib name
@return Returns the CAS caslib name, or empty string if not found
**/
%macro mfv_getcaslib(lib);
%local dsid rc result;
%let dsid=%sysfunc(open(sashelp.vlibnam(
where=(libname="%upcase(&lib)" and sysname="Caslib")
)));
%if &dsid %then %do;
%let rc=%sysfunc(fetch(&dsid));
%if &rc=0 %then
%let result=%sysfunc(
getvarc(&dsid,%sysfunc(varnum(&dsid,SYSVALUE)))
);
%let rc=%sysfunc(close(&dsid));
%end;
&result
%mend mfv_getcaslib;
-53
View File
@@ -1,53 +0,0 @@
/**
@file
@brief Returns the path of a folder from the URI
@details Makes use of the SYSMSG() ER8OR response, which resolves the uri,
seemingly without entering an er8or state.
Usage:
%mv_createfolder(path=/public/demo)
%let uri=%mfv_getpathuri(/public/demo);
%put %mfv_getfolderpath(&uri);
Notice above the new path has an uppercase P - the correct path.
@param [in] uri The uri of the folder -eg /folders/folders/xxxx)
<h4> SAS Macros </h4>
@li mf_getuniquefileref.sas
<h4> Related Macros </h4>
@li mfv_getpathuri.sas
@version 4
@author [Allan Bowe](https://www.linkedin.com/in/allanbowe/)
**/
%macro mfv_getfolderpath(uri
)/*/STORE SOURCE*/;
%local fref rc path msg var /* var used to avoid delete timing issue */;
%let fref=%mf_getuniquefileref();
%if %quote(%substr(%str(&uri),1,17)) ne %quote(/folders/folders/)
%then %do;
%put &sysmacroname: Invalid URI: &uri;
%end;
%else %if %sysfunc(filename(fref,,filesrvc,folderuri="&uri" ))=0
%then %do;
%let var=_FILESRVC_&fref._URI;
%local fid ;
%let fid= %sysfunc(fopen(&fref,I));
%let msg=%quote(%sysfunc(sysmsg()));
%unquote(%scan(&msg,2,%str(,.)))
%let rc=%sysfunc(fclose(&fid));
%let rc=%sysfunc(filename(fref));
%symdel &var;
%end;
%else %do;
%put &sysmacroname: Not Found: &uri;
%let syscc=0;
%end;
%mend mfv_getfolderpath ;
-4
View File
@@ -49,8 +49,4 @@
%let syscc=0;
%end;
%mf_abort(
iftrue=(&syscc ne 0),
msg=Cannot leave &sysmacroname with syscc=&syscc
)
%mend mfv_getpathuri;
-207
View File
@@ -1,207 +0,0 @@
/**
@file mv_castabload.sas
@brief Checks if a CAS table is loaded; if not, loads and promotes it
@details Runs in SPRE against an active CAS session. Accepts a
SAS libref, derives the CAS caslib and session UUID from
sashelp.vlibnam, then checks whether the table is already
in-memory. If not, locates the owning CAS server via the
casManagement REST API, queries the table endpoint to discover
the source file and caslib, then loads and promotes the table.
A CAS session must already be established by the caller, eg:
cas mysess;
libname mylib cas caslib=Public;
%mv_castabload(lib=mylib, table=BASEBALL)
@param [in] lib= SAS libref for the CAS caslib
@param [in] table= Name of the CAS table to load
@param [in] mdebug= (0) Set to 1 to enable verbose logging:
- echoes resolved parameters
- prints tableExists result
- enables mprint/notes during PROC calls
<h4> SAS Macros </h4>
@li mf_getplatform.sas
@li mf_getuniquefileref.sas
@li mf_getuniquelibref.sas
@li mp_abort.sas
**/
%macro mv_castabload(
lib=
,table=
,mdebug=0
);
%local _sysopts base_uri caslib uuid server
srcfile srccaslib fname1 libref1 ftmp i _svcount _exists;
%let _sysopts=%sysfunc(getoption(mprint)) %sysfunc(getoption(notes));
/* ---- input validation -------------------------------------------------- */
%mp_abort(
iftrue=("&lib"="" or "&table"=""),
msg=%str(lib= and table= are required)
)
%if &mdebug=1 %then %do;
%put &=lib;
%put &=table;
options mprint notes;
%end;
/* ---- derive caslib and session UUID from sashelp.vlibnam --------------- */
data _null_;
set sashelp.vlibnam(
where=(libname="%upcase(&lib)"
and sysname in ("Caslib","Session UUID"))
);
if sysname="Caslib" then call symputx('caslib',sysvalue,'L');
else call symputx('uuid',sysvalue,'L');
%if &mdebug=1 %then %do;
putlog sysname sysvalue;
%end;
run;
%mp_abort(
iftrue=("&caslib"=""),
msg=%str(&lib is not an assigned CAS libref)
)
%mp_abort(
iftrue=("&uuid"=""),
msg=%str(No session UUID found for libref &lib)
)
/* ---- existence check --------------------------------------------------- */
proc cas;
table.tableExists result=r /
caslib="&caslib"
name="&table";
%if &mdebug=1 %then %do;
print r;
%end;
if r.exists > 0 then call symputx('_exists', '1', 'L');
else call symputx('_exists', '0', 'L');
quit;
/* ---- already loaded: skip ---------------------------------------------- */
%if &_exists=1 %then %do;
%put NOTE: Table &caslib..&table already loaded - skipping;
%return;
%end;
/* ---- get list of CAS servers ----------------------------------------- */
%let base_uri=%mf_getplatform(VIYARESTAPI);
%let fname1=%mf_getuniquefileref();
%let libref1=%mf_getuniquelibref();
proc http method='GET' out=&fname1 oauth_bearer=sas_services
url="&base_uri/casManagement/servers";
run;
%mp_abort(
iftrue=(&SYS_PROCHTTP_STATUS_CODE ne 200),
msg=%str(&SYS_PROCHTTP_STATUS_CODE &SYS_PROCHTTP_STATUS_PHRASE)
)
libname &libref1 JSON fileref=&fname1;
data _null_;
set &libref1..items;
call symputx(cats('_sv_', _n_), name, 'L');
call symputx('_svcount', _n_, 'L');
run;
libname &libref1 clear;
filename &fname1 clear;
/* ---- find which server owns this session ------------------------------ */
%do i=1 %to &_svcount;
%if "&server"="" %then %do;
%if &mdebug=1 %then %put checking server: &&_sv_&i;
%let ftmp=%mf_getuniquefileref();
proc http method='GET' out=&ftmp oauth_bearer=sas_services
url="&base_uri/casManagement/servers/&&_sv_&i/sessions/&uuid";
run;
%if &SYS_PROCHTTP_STATUS_CODE=200
%then %let server=&&_sv_&i;
filename &ftmp clear;
%end;
%end;
%mp_abort(
iftrue=("&server"=""),
msg=%str(Could not find owning server for CAS session &uuid)
)
%if &mdebug=1 %then %put &=server;
/* ---- discover source file from REST endpoint -------------------------- */
%let fname1=%mf_getuniquefileref();
%let libref1=%mf_getuniquelibref();
proc http method='GET' out=&fname1 oauth_bearer=sas_services
url="&base_uri/casManagement/servers/&server/caslibs/&caslib/tables/&table";
run;
%if &mdebug=1 %then %do;
%put &=SYS_PROCHTTP_STATUS_CODE &=SYS_PROCHTTP_STATUS_PHRASE;
data _null_;
infile &fname1;
input;
putlog _infile_;
run;
%end;
%mp_abort(
iftrue=(&SYS_PROCHTTP_STATUS_CODE=404),
msg=%str(&caslib..&table not found - is a source file registered?)
)
%mp_abort(
iftrue=(&SYS_PROCHTTP_STATUS_CODE ne 200),
msg=%str(&SYS_PROCHTTP_STATUS_CODE &SYS_PROCHTTP_STATUS_PHRASE)
)
libname &libref1 JSON fileref=&fname1;
data _null_;
set &libref1..tablereference;
call symputx('srcfile', sourceTableName, 'L');
call symputx('srccaslib', sourceCaslibName, 'L');
stop;
run;
libname &libref1 clear;
filename &fname1 clear;
%mp_abort(
iftrue=("&srcfile"="" or "&srccaslib"=""),
msg=%str(No sourceTableName/sourceCaslibName for &caslib..&table)
)
%if &mdebug=1 %then %put &=srcfile &=srccaslib;
/* ---- load from discovered source -------------------------------------- */
proc casutil;
load casdata="&srcfile"
incaslib="&srccaslib"
casout="&table"
outcaslib="&caslib"
promote;
quit;
%mp_abort(
iftrue=(&syscc ne 0),
msg=%str(Load failed for &caslib..&table)
)
%put NOTE: Table &caslib..&table loaded and promoted from &srcfile;
/* ---- restore options --------------------------------------------------- */
%if &mdebug=1 %then %do;
options &_sysopts;
%end;
%mend mv_castabload;
-192
View File
@@ -1,192 +0,0 @@
/**
@file mv_castabsave.sas
@brief Saves an in-memory CAS table back to persistent storage
@details Runs in SPRE against an active CAS session. Accepts a
SAS libref, derives the CAS caslib and session UUID from
sashelp.vlibnam, locates the owning CAS server via the
casManagement REST API, then queries the table endpoint to
discover the original source file and saves back to that path.
CASUTIL infers the file type from the output file extension.
A CAS session must already be established by the caller, eg:
cas mysess;
libname mylib cas caslib=Public;
%mv_castabsave(lib=mylib, table=BASEBALL)
@param [in] lib= SAS libref for the CAS caslib
@param [in] table= Name of the in-memory CAS table to save
@param [in] mdebug= (0) Set to 1 to enable verbose logging:
- echoes resolved parameters
- prints HTTP response body
- enables mprint/notes during PROC calls
<h4> SAS Macros </h4>
@li mf_getplatform.sas
@li mf_getuniquefileref.sas
@li mf_getuniquelibref.sas
@li mp_abort.sas
**/
%macro mv_castabsave(
lib=
,table=
,mdebug=0
);
%local _sysopts base_uri caslib uuid server
srcfile srccaslib fname1 libref1 ftmp i _svcount;
%let _sysopts=%sysfunc(getoption(mprint)) %sysfunc(getoption(notes));
/* ---- input validation -------------------------------------------------- */
%mp_abort(
iftrue=("&lib"="" or "&table"=""),
msg=%str(lib= and table= are required)
)
%if &mdebug=1 %then %do;
%put &=lib;
%put &=table;
options mprint notes;
%end;
/* ---- derive caslib and session UUID from sashelp.vlibnam --------------- */
data _null_;
set sashelp.vlibnam(
where=(libname="%upcase(&lib)"
and sysname in ("Caslib","Session UUID"))
);
if sysname="Caslib" then call symputx('caslib',sysvalue,'L');
else call symputx('uuid',sysvalue,'L');
run;
%mp_abort(
iftrue=("&caslib"=""),
msg=%str(&lib is not an assigned CAS libref)
)
%mp_abort(
iftrue=("&uuid"=""),
msg=%str(No session UUID found for libref &lib)
)
%if &mdebug=1 %then %do;
%put &=caslib;
%put &=uuid;
%end;
%let base_uri=%mf_getplatform(VIYARESTAPI);
/* ---- get list of CAS servers ------------------------------------------- */
%let fname1=%mf_getuniquefileref();
%let libref1=%mf_getuniquelibref();
proc http method='GET' out=&fname1 oauth_bearer=sas_services
url="&base_uri/casManagement/servers";
run;
%mp_abort(
iftrue=(&SYS_PROCHTTP_STATUS_CODE ne 200),
msg=%str(&SYS_PROCHTTP_STATUS_CODE &SYS_PROCHTTP_STATUS_PHRASE)
)
libname &libref1 JSON fileref=&fname1;
data _null_;
set &libref1..items;
call symputx(cats('_sv_', _n_), name, 'L');
call symputx('_svcount', _n_, 'L');
run;
libname &libref1 clear;
filename &fname1 clear;
/* ---- find which server owns this session ------------------------------- */
%do i=1 %to &_svcount;
%if "&server"="" %then %do;
%if &mdebug=1 %then %put checking server: &&_sv_&i;
%let ftmp=%mf_getuniquefileref();
proc http method='GET' out=&ftmp oauth_bearer=sas_services
url="&base_uri/casManagement/servers/&&_sv_&i/sessions/&uuid";
run;
%if &SYS_PROCHTTP_STATUS_CODE=200
%then %let server=&&_sv_&i;
filename &ftmp clear;
%end;
%end;
%mp_abort(
iftrue=("&server"=""),
msg=%str(Could not find owning server for CAS session &uuid)
)
%if &mdebug=1 %then %put &=server;
/* ---- discover srcfile from REST endpoint ------------------------------- */
%let fname1=%mf_getuniquefileref();
%let libref1=%mf_getuniquelibref();
proc http method='GET' out=&fname1 oauth_bearer=sas_services
url="&base_uri/casManagement/servers/&server/caslibs/&caslib/tables/&table";
run;
%if &mdebug=1 %then %do;
%put &=SYS_PROCHTTP_STATUS_CODE &=SYS_PROCHTTP_STATUS_PHRASE;
data _null_;
infile &fname1;
input;
putlog _infile_;
run;
%end;
%mp_abort(
iftrue=(&SYS_PROCHTTP_STATUS_CODE=404),
msg=%str(&caslib..&table not found - is it loaded in memory?)
)
%mp_abort(
iftrue=(&SYS_PROCHTTP_STATUS_CODE ne 200),
msg=%str(&SYS_PROCHTTP_STATUS_CODE &SYS_PROCHTTP_STATUS_PHRASE)
)
libname &libref1 JSON fileref=&fname1;
data _null_;
set &libref1..tablereference;
call symputx('srcfile', sourceTableName, 'L');
call symputx('srccaslib', sourceCaslibName, 'L');
stop;
run;
libname &libref1 clear;
filename &fname1 clear;
%mp_abort(
iftrue=("&srcfile"="" or "&srccaslib"=""),
msg=%str(No sourceTableName/sourceCaslibName for &caslib..&table)
)
%if &mdebug=1 %then %put &=srcfile;
/* ---- save to disk ------------------------------------------------------- */
proc casutil;
save casdata="&table"
incaslib="&caslib"
casout="&srcfile"
outcaslib="&srccaslib"
replace;
quit;
%mp_abort(
iftrue=(&syscc ne 0),
msg=%str(Save failed for &caslib..&table)
)
%put NOTE: Table &caslib..&table saved to &srcfile;
/* ---- restore options --------------------------------------------------- */
%if &mdebug=1 %then %do;
options &_sysopts;
%end;
%mend mv_castabsave;
+18 -140
View File
@@ -17,20 +17,6 @@
run;
%mv_createfile(path=/Public/temp,name=newfile.txt,inref=myfile)
The macro also supports find & replace (used by the SASjs Streaming App
build program). This allows one string to be replaced by another at the
point at which the file is created. This is done by passing in the NAMES of
the macro variables containing the values to be swapped, eg:
filename fref temp;
data _null_;
file fref;
put 'whenever life gets you down, Mrs Brown..';
run;
%let f=Mrs Brown;
%let r=just remember that you're standing on a planet that's evolving;
%mv_createfile(path=/Public,name=life.md,inref=fref,fin,swap=f r)
@param [in] path= The parent (SAS Drive) folder in which to create the file
@param [in] name= The name of the file to be created
@@ -52,8 +38,6 @@
@li sas_services
@param [in] force= (YES) Will overwrite (delete / recreate) files by default.
Set to NO to abort if a file already exists in that location.
@param pin] swap= (0) Provide two macro variable NAMES that contain the values
to be swapped, eg swap=find replace (see also the example above)
@param [out] outds= (_null_) Output dataset with the uri of the new file
@param [in] mdebug= (0) Set to 1 to enable DEBUG messages
@@ -67,9 +51,7 @@
@li mfv_getpathuri.sas
@li mp_abort.sas
@li mp_base64copy.sas
@li mp_replace.sas
@li mv_createfolder.sas
@li mv_getviyafileextparms.sas
<h4> Related Macros</h4>
@li mv_createfile.sas
@@ -87,7 +69,6 @@
,mdebug=0
,outds=_null_
,force=YES
,swap=0
);
%local dbg;
%if &mdebug=1 %then %do;
@@ -96,11 +77,6 @@
%end;
%else %let dbg=*;
%mp_abort(
iftrue=(&syscc ne 0),
msg=Cannot enter &sysmacroname with syscc=&syscc
)
%local oauth_bearer;
%if &grant_type=detect %then %do;
%if %symexist(&access_token_var) %then %let grant_type=authorization_code;
@@ -137,12 +113,6 @@
%end;
%else %put %str(ERR)OR: invalid value for intype: &intype;
%if "&swap" ne "0" %then %do;
%mp_replace("%sysfunc(pathname(&fref))"
,findvar=%scan(&swap,1,%str( ))
,replacevar=%scan(&swap,2,%str( ))
)
%end;
%if &mdebug=1 %then %do;
data _null_;
@@ -154,7 +124,7 @@
options noquotelenmax;
%local base_uri; /* location of rest apis */
%let base_uri=%trim(%mf_getplatform(VIYARESTAPI));
%let base_uri=%mf_getplatform(VIYARESTAPI);
/* create folder if it does not already exist */
%local folderds self_uri;
@@ -173,16 +143,11 @@ run;
/* abort or delete if file already exists */
%let force=%upcase(&force);
%local fileuri ;
%let fileuri=%trim(%mfv_getpathuri(&path/&name));
%let fileuri=%mfv_getpathuri(&path/&name);
%mp_abort(iftrue=(%mf_isblank(&fileuri)=0 and &force ne YES)
,mac=MV_CREATEFILE
,msg=%str(File &path/&name already exists and force=&force)
)
%mp_abort(
iftrue=(&syscc ne 0),
mac=MV_CREATEFILE182
msg=syscc=&syscc after mfv_getpathuri
)
%if %mf_isblank(&fileuri)=0 and &force=YES %then %do;
proc http method="DELETE" url="&base_uri&fileuri" &oauth_bearer;
@@ -192,146 +157,59 @@ run;
%end;
"Accept"="*/*";
run;
%put &sysmacroname DELETE &base_uri&fileuri;
%if &SYS_PROCHTTP_STATUS_CODE ne 204 %then %do;
%put &=SYS_PROCHTTP_STATUS_CODE &=SYS_PROCHTTP_STATUS_PHRASE;
%end;
%put &sysmacroname DELETE &base_uri&fileuri
&=SYS_PROCHTTP_STATUS_CODE &=SYS_PROCHTTP_STATUS_PHRASE;
%end;
%local url mimetype ext;
%local url mimetype;
%let url=&base_uri/files/files?parentFolderUri=&self_uri;
%let ext=%upcase(%trim(%scan(&name,-1,.)));
/* Get Viya file-extension details into some macro variables */
%mv_getViyaFileExtParms(&ext
,propertiesVar=viyaProperties
,typeDefNameVar=viyaTypeDefName
,mdebug=&mdebug
)
/* fetch job info */
%local fname1;
%let fname1=%mf_getuniquefileref();
proc http method='POST' out=&fname1 &oauth_bearer in=&fref
%if "&ctype" = "0" %then %do;
%let mimetype=%mf_mimetype(&ext);
%let mimetype=%mf_mimetype(%scan(&name,-1,.));
ct="&mimetype"
%end;
%else %do;
ct="&ctype"
%end;
/* typeDefName */
%if not %mf_isBlank(&viyaTypeDefName) %then %do;
url="&url%str(&)typeDefName=&viyaTypeDefName";
%if "&mimetype"="text/html" or "&mimetype"="text/css"
or "&mimetype"="text/javascript" %then %do;
url="&url%str(&)typeDefName=file";
%end;
%else %do;
%if "&ext"="HTM" or "&ext"="HTML" or "&ext"="XHTML" %then %do;
url="&url%str(&)typeDefName=file_html";
%end;
%else %do;
%if "&ext"="CSS" or "&ext"="JS" or "&ext"="PNG" or "&ext"="SVG" %then %do;
url="&url%str(&)typeDefName=file_%lowcase(&ext)";
%end;
%else %do;
url="&url";
%end;
%end;
url="&url";
%end;
headers "Accept"="application/json"
%if &grant_type=authorization_code %then %do;
"Authorization"="Bearer &&&access_token_var"
%end;
"Content-Disposition"=
%if "&ext"="SVG" or "&ext"="HTML" %then %do;
"filename=""&name"";"
%end;
%else %do;
"&contentdisp filename=""&name""; name=""&name"";"
%end;
;
"Content-Disposition"= "&contentdisp filename=""&name""; name=""&name"";";
run;
%if &mdebug=1 %then %put &sysmacroname POST &=url
%put &sysmacroname POST &=url
&=SYS_PROCHTTP_STATUS_CODE &=SYS_PROCHTTP_STATUS_PHRASE;
%mp_abort(iftrue=(&SYS_PROCHTTP_STATUS_CODE ne 201)
,mac=MV_CREATEFILE
,msg=%str(&SYS_PROCHTTP_STATUS_CODE &SYS_PROCHTTP_STATUS_PHRASE)
)
%local libref2;
%let libref2=%mf_getuniquelibref();
libname &libref2 JSON fileref=&fname1;
/* Grab the follow on link */
%put Grabbing the follow on link ;
data &outds;
set &libref2..links end=last;
if rel='createChild' then do;
call symputx('href',quote(cats("&base_uri",href)),'l');
&dbg put (_all_)(=);
end;
run;
/* URI of the created file */
%let fileuri=%trim(%mfv_getpathuri(&path/&name));
/* If properties were found then patch the file to include them */
%if not %mf_isBlank(%superq(viyaProperties)) %then %do;
/* Wrap the properties object in a root object also containing the filename */
%local viyapatch;
%let viyapatch=%sysfunc(pathname(work))/%mf_getuniquename(prefix=patch_json_);
data _null_;
length line $32767;
file "&viyapatch" lrecl=32767;
put '{ "name": "' "&name" '",';
line = cat('"properties": ',symget("viyaProperties"));
put line;
put '}';
stop;
run;
%if &mdebug=1 %then %do;
data _null_;
if (_n_ eq 1) then put 'DEBUG: ** PATCH JSON **';
infile "&viyapatch" end=last;
input;
put _infile_;
run;
%end;
/* Apply the properties to the newly created file, using the PATCH method */
%let fref=%mf_getuniquefileref();
filename &fref "&viyapatch";
%let url=&base_uri&fileuri;
proc http method='PATCH' oauth_bearer=sas_services in=&fref
url="&url";
headers "Accept"="application/json"
"Content-Type"="application/json"
"If-Match"="*";
%if &mdebug=1 %then %do;
debug level=2;
%end;
run;
%if &mdebug=1 %then %put &sysmacroname PATCH &=url
&=SYS_PROCHTTP_STATUS_CODE &=SYS_PROCHTTP_STATUS_PHRASE;
%mp_abort(iftrue=(&SYS_PROCHTTP_STATUS_CODE ne 200)
,mac=MV_CREATEFILE
,msg=%str(&SYS_PROCHTTP_STATUS_CODE &SYS_PROCHTTP_STATUS_PHRASE)
)
%end;
%put &sysmacroname: &base_uri&fileuri;
%put /SASJobExecution?_file=&path/&name;%put;
%if &mdebug=0 %then %do;
/* clear refs */
filename &fname1 clear;
filename &fref clear;
libname &libref2 clear;
%end;
%mp_abort(
iftrue=(&syscc ne 0),
msg=Cannot leave &sysmacroname with syscc=&syscc
)
%put &sysmacroname: File &name successfully created:;%put;
%put &base_uri%mfv_getpathuri(&path/&name);%put;
%put &base_uri/SASJobExecution?_file=&path/&name;%put;
%put &sysmacroname:;
%mend mv_createfile;
+5 -26
View File
@@ -46,13 +46,8 @@
%end;
%else %let dbg=*;
%mp_abort(
iftrue=(&syscc ne 0),
msg=Cannot enter &sysmacroname with syscc=&syscc
)
%if %mfv_existfolder(&path)=1 %then %do;
%&dbg.put &sysmacroname: &path already exists;
%put &sysmacroname: &path already exists;
data &outds;
self_uri="%mfv_getpathuri(&path)";
output;
@@ -60,7 +55,6 @@
run;
%return;
%end;
%mp_abort(iftrue=(&syscc ne 0),msg=syscc=&syscc when folder checking)
%local oauth_bearer;
%if &grant_type=detect %then %do;
@@ -114,17 +108,6 @@ options noquotelenmax;
headers "Authorization"="Bearer &&&access_token_var";
%end;
run;
%if &SYS_PROCHTTP_STATUS_CODE=401 %then %do;
/* relates to: https://github.com/sasjs/core/issues/400 */
%put 401 thrown in &sysmacroname;
%put sleeping: %sysfunc(sleep(12,1)) secs - will try again;
proc http method='GET' out=&fname1 &oauth_bearer
url="&base_uri/folders/folders/@item?path=&newpath";
%if &grant_type=authorization_code %then %do;
headers "Authorization"="Bearer &&&access_token_var";
%end;
run;
%end;
%local libref1;
%let libref1=%mf_getuniquelibref();
libname &libref1 JSON fileref=&fname1;
@@ -132,7 +115,7 @@ options noquotelenmax;
iftrue=(
&SYS_PROCHTTP_STATUS_CODE ne 200 and &SYS_PROCHTTP_STATUS_CODE ne 404
)
,mac=mv_createfolder124
,mac=&sysmacroname
,msg=%str(&SYS_PROCHTTP_STATUS_CODE &SYS_PROCHTTP_STATUS_PHRASE)
)
%if &mdebug=1 %then %do;
@@ -145,6 +128,7 @@ options noquotelenmax;
run;
%end;
%if &SYS_PROCHTTP_STATUS_CODE=200 %then %do;
%*put &sysmacroname &newpath exists so grab the follow on link ;
data _null_;
set &libref1..links;
if rel='createChild' then
@@ -180,9 +164,8 @@ options noquotelenmax;
'Content-Type'='application/vnd.sas.content.folder+json'
'Accept'='application/vnd.sas.content.folder+json';
run;
%if &SYS_PROCHTTP_STATUS_CODE ne 201 %then %do;
%put &=SYS_PROCHTTP_STATUS_CODE &=SYS_PROCHTTP_STATUS_PHRASE;
%end;
%put &=SYS_PROCHTTP_STATUS_CODE;
%put &=SYS_PROCHTTP_STATUS_PHRASE;
%mp_abort(iftrue=(&SYS_PROCHTTP_STATUS_CODE ne 201)
,mac=&sysmacroname
,msg=%str(&SYS_PROCHTTP_STATUS_CODE &SYS_PROCHTTP_STATUS_PHRASE)
@@ -210,8 +193,4 @@ options noquotelenmax;
filename &fname1 clear;
libname &libref1 clear;
%end;
%mp_abort(
iftrue=(&syscc ne 0),
msg=Cannot leave &sysmacroname with syscc=&syscc
)
%mend mv_createfolder;
+18 -34
View File
@@ -122,7 +122,7 @@ options noquotelenmax;
%let path=%substr(&path,1,%length(&path)-1);
/* ensure folder exists */
%&dbg.put &sysmacroname: Path &path being checked / created;
%put &sysmacroname: Path &path being checked / created;
%mv_createfolder(path=&path)
%local base_uri; /* location of rest apis */
@@ -497,8 +497,7 @@ data _null_;
put ' prxchange(''s/\\/\\\\/'',-1,&&name&i) ';
put ' )))))))))))))!!''"''; ';
put ' end; ';
put ' /* trim (not cats) so leading blanks are retained */ ';
put ' else &&name&i=''"''!!trim(&&name&i)!!''"''; ';
put ' else &&name&i=quote(cats(&&name&i)); ';
put ' %end; ';
put ' %end; ';
put ' run; ';
@@ -595,14 +594,14 @@ data _null_;
put ' ,showmeta=N,maxobs=MAX,workobs=0 ';
put '); ';
put '%global _webin_file_count _webin_fileuri _debug _omittextlog _webin_name ';
put ' sasjs_tables SYS_JES_JOB_URI _EXECUTIONTASKS; ';
put '%if %index("&_debug",log) %then %let _debug=128; ';
put ' sasjs_tables SYS_JES_JOB_URI; ';
put '%if %index("&_debug",log) %then %let _debug=131; ';
put ' ';
put '%local i tempds table; ';
put '%let action=%upcase(&action); ';
put ' ';
put '%if &action=FETCH %then %do; ';
put ' %if %upcase(&_omittextlog)=FALSE or %str(&_debug) ge 128 %then %do; ';
put ' %if %upcase(&_omittextlog)=FALSE or %str(&_debug) ge 131 %then %do; ';
put ' options mprint notes mprintnest; ';
put ' %end; ';
put ' ';
@@ -610,11 +609,6 @@ data _null_;
put ' %let _webin_file_count=%eval(&_webin_file_count+0); ';
put ' %let _webin_fileuri1=&_webin_fileuri; ';
put ' %let _webin_name1=&_webin_name; ';
put ' %if &_EXECUTIONTASKS=true %then %do; ';
put ' /* TODO - remove this once SAS Track CS0409737 is resolved */ ';
put ' /* links: https://github.com/sasjs/adapter/issues/884 */ ';
put ' %if %upcase(&_webin_name)=_SASJS_NOOP %then %let _webin_file_count=0; ';
put ' %end; ';
put ' %end; ';
put ' ';
put ' /* if the sasjs_tables param is passed, we expect param based upload */ ';
@@ -644,10 +638,7 @@ data _null_;
put ' data _null_; ';
put ' infile "%sysfunc(pathname(work))/&table..csv" termstr=crlf ; ';
put ' input; ';
put ' /* a plain $ informat strips leading blanks - use $char instead */ ';
put ' if _n_=1 then call symputx(''input_statement'' ';
put ' ,prxchange(''s/:\$(?=[0-9 ])/:\$char/i'',-1,_infile_) ';
put ' ); ';
put ' if _n_=1 then call symputx(''input_statement'',_infile_); ';
put ' list; ';
put ' data work.&table; ';
put ' infile "%sysfunc(pathname(work))/&table..csv" firstobs=2 dsd ';
@@ -658,20 +649,13 @@ data _null_;
put ' %end; ';
put ' %else %do i=1 %to &_webin_file_count; ';
put ' /* read in any files that are sent */ ';
put ' %if &_EXECUTIONTASKS=true %then %do; ';
put ' filename indata "%sysfunc(pathname(&&_webin_fileref&i))" lrecl=999999; ';
put ' %end; ';
put ' %else %do; ';
put ' filename indata filesrvc "&&_webin_fileuri&i" lrecl=999999; ';
put ' %end; ';
put ' /* this part needs refactoring for wide files */ ';
put ' filename indata filesrvc "&&_webin_fileuri&i" lrecl=999999; ';
put ' data _null_; ';
put ' infile indata termstr=crlf lrecl=32767; ';
put ' input; ';
put ' /* a plain $ informat strips leading blanks - use $char instead */ ';
put ' if _n_=1 then call symputx(''input_statement'' ';
put ' ,prxchange(''s/:\$(?=[0-9 ])/:\$char/i'',-1,_infile_) ';
put ' ); ';
put ' %if %str(&_debug) ge 128 %then %do; ';
put ' if _n_=1 then call symputx(''input_statement'',_infile_); ';
put ' %if %str(&_debug) ge 131 %then %do; ';
put ' if _n_<20 then putlog _infile_; ';
put ' else stop; ';
put ' %end; ';
@@ -704,9 +688,6 @@ data _null_;
put ' ';
put ' /* setup json */ ';
put ' data _null_;file &fref; ';
put ' %if %str(&_debug) ge 128 and &_EXECUTIONTASKS=true %then %do; ';
put ' put ''>>weboutBEGIN<<''; ';
put ' %end; ';
put ' put ''{"SYSDATE" : "'' "&SYSDATE" ''"''; ';
put ' put '',"SYSTIME" : "'' "&SYSTIME" ''"''; ';
put ' run; ';
@@ -819,9 +800,7 @@ data _null_;
put ' memsize=quote(cats(memsize)); ';
put ' put '',"MEMSIZE" : '' memsize; ';
put ' put "}"; ';
put ' %if %str(&_debug) ge 128 and &_EXECUTIONTASKS=true %then %do; ';
put ' put ''>>weboutEND<<''; ';
put ' %end; ';
put ' ';
put ' %if %upcase(&fref) ne _WEBOUT and &stream=Y %then %do; ';
put ' data _null_; rc=fcopy("&fref","_webout");run; ';
put ' %end; ';
@@ -976,7 +955,12 @@ run;
libname &libref1 clear;
%end;
%put &sysmacroname: Job &name created! Check it out:;
%put &url/SASJobExecution?_PROGRAM=&path/&name;
%put &sysmacroname: Job &name successfully created in &path;
%put &sysmacroname:;
%put &sysmacroname: Check it out here:;
%put &sysmacroname:;%put;
%put &url/SASJobExecution?_PROGRAM=&path/&name;%put;
%put &sysmacroname:;
%put &sysmacroname:;
%mend mv_createwebservice;
+2 -4
View File
@@ -111,15 +111,13 @@ proc http method='GET' out=&fname1a &oauth_bearer
headers "Authorization"="Bearer &&&access_token_var";
%end;
run;
%if &SYS_PROCHTTP_STATUS_CODE ne 200 %then %do;
%put &=sysmacroname &=SYS_PROCHTTP_STATUS_CODE &=SYS_PROCHTTP_STATUS_PHRASE;
%end;
%put &=SYS_PROCHTTP_STATUS_CODE;
%local libref1a;
%let libref1a=%mf_getuniquelibref();
libname &libref1a JSON fileref=&fname1a;
%local uri found;
%let found=0;
/* %put Getting object uri from &libref1a..items; */
%put Getting object uri from &libref1a..items;
data _null_;
length contenttype name $1000;
set &libref1a..items;
+7 -9
View File
@@ -69,7 +69,8 @@
options noquotelenmax;
%local base_uri; /* location of rest apis */
%let base_uri=%mf_getplatform(VIYARESTAPI);
/* fetch the members of the folder to get the uri */
%put &sysmacroname: fetching details for &path ;
%local fname1;
%let fname1=%mf_getuniquefileref();
proc http method='GET' out=&fname1 &oauth_bearer
@@ -89,7 +90,7 @@ run;
)
%end;
/* grab the follow on link */
%put &sysmacroname: grab the follow on link ;
%local libref1;
%let libref1=%mf_getuniquelibref();
libname &libref1 JSON fileref=&fname1;
@@ -107,18 +108,15 @@ proc http method='GET' out=&fname1a &oauth_bearer
headers "Authorization"="Bearer &&&access_token_var";
%end;
run;
%if &SYS_PROCHTTP_STATUS_CODE ne 200 %then %do;
%put &=sysmacroname &=SYS_PROCHTTP_STATUS_CODE &=SYS_PROCHTTP_STATUS_PHRASE;
%end;
%put &=SYS_PROCHTTP_STATUS_CODE;
%local libref1a;
%let libref1a=%mf_getuniquelibref();
libname &libref1a JSON fileref=&fname1a;
%local uri found;
%let found=0;
/* %put Getting object uri from &libref1a..items; */
%put Getting object uri from &libref1a..items;
data _null_;
length contenttype name uri $1000;
call missing(of _all_);
length contenttype name $1000;
set &libref1a..items;
if contenttype='jobDefinition' and upcase(name)="%upcase(&name)" then do;
call symputx('uri',cats("&base_uri",uri),'l');
@@ -142,7 +140,7 @@ run;
,msg=%str(&SYS_PROCHTTP_STATUS_CODE &SYS_PROCHTTP_STATUS_PHRASE)
)
%end;
%else %put &sysmacroname: &path/&name deleted;
%else %put &sysmacroname: &path/&name successfully deleted;
/* clear refs */
filename &fname1 clear;
+2 -2
View File
@@ -126,7 +126,7 @@ data _null_;
uri=symget('uri');
if length(uri)<12 then do;
call symputx('errflg',1);
call symputx('errmsg',"URI is too short - "!!uri,'l');
call symputx('errmsg',"URI is invalid (too short) - '&uri'",'l');
end;
if scan(uri,-1)='state' or scan(uri,1) ne 'jobExecution' then do;
call symputx('errflg',1);
@@ -191,7 +191,7 @@ data _null_;
uri=symget('loglocation');
if length(uri)<12 then do;
call symputx('errflg',1);
call symputx('errmsg',"URI is too short - "!!uri,'l');
call symputx('errmsg',"URI is invalid (too short) - '&uri'",'l');
end;
else if (scan(uri,1,'/') ne 'compute' or scan(uri,2,'/') ne 'sessions')
and (scan(uri,1,'/') ne 'files' or scan(uri,2,'/') ne 'files')
-255
View File
@@ -1,255 +0,0 @@
/**
@file mv_getviyafileextparms.sas
@brief Reads the VIYA file-extension type definition and returns selected
values in SAS macro variables
@details Content is derived from the following endpoint:
"https://${serverUrl}/types/types?limit=999999"
@param [in] ext File extension to retrieve property info for.
@param [out] propertiesVar= SAS macro variable name that will contain
the 'properties' object json, if found, else blank.
@param [out] typeDefNameVar= SAS macro variable name that will contain
the 'typeDefName' property value, if found, else blank.
@param [out] mediaTypeVar= SAS macro variable name that will contain
the 'mediaType' property value, if found, else blank.
@param [out] viyaFileExtRespLibDs (work.mv_getViyaFileExtParmsResponse)
Library.name of the dataset to receive the local working copy of the initial
response that requests all file extension details. Created once per session
to avoid multiple api calls.
@param [in] mdebug= (0) Set to 1 to enable DEBUG messages
<h4> SAS Macros </h4>
@li mf_existds.sas
@li mf_getplatform.sas
@li mf_getuniquefileref.sas
@li mf_getuniquelibref.sas
@li mf_getuniquename.sas
@li mf_getvalue.sas
@li mf_getvarlist.sas
@li mf_getvartype.sas
@li mf_isblank.sas
@li mf_nobs.sas
@li mp_abort.sas
**/
%macro mv_getViyaFileExtParms(
ext,
typeDefNameVar=,
propertiesVar=,
mediaTypeVar=,
viyaFileExtRespLibDs=work.mv_getViyaFileExtParmsResponse,
mdebug=0
);
%local base_uri; /* location of rest apis */
%local url; /* File extension info end-point */
%mp_abort(
iftrue=(%mf_isBlank(&ext))
,msg=%str(No file extension provided.)
,mac=MV_GETVIYAFILEEXTPARMS
)
%mp_abort(
iftrue=(%mf_isBlank(&typeDefNameVar) and
%mf_isBlank(&propertiesVar) and
%mf_isBlank(&mediaTypeVar))
,msg=%str(MV_GETVIYAFILEEXTPARMS - No parameter was requested.)
,mac=MV_GETVIYAFILEEXTPARMS
)
%mp_abort(
iftrue=(%mf_isBlank(&viyaFileExtRespLibDs))
,msg=%str(No <libname.>dataset name provided to cache inital response.)
,mac=MV_GETVIYAFILEEXTPARMS
)
/* Declare requested parameters as global macro vars and initialize blank */
%if not %mf_isBlank(&typeDefNameVar) %then %do;
%global &typeDefNameVar;
%let &typeDefNameVar = %str();
%end;
%if not %mf_isBlank(&propertiesVar) %then %do;
%global &propertiesVar;
%let &propertiesVar = %str();
%end;
%if not %mf_isBlank(&mediaTypeVar) %then %do;
%global &mediaTypeVar;
%let &mediaTypeVar = %str();
%end;
%let base_uri=%mf_getplatform(VIYARESTAPI);
%if &mdebug=1 %then %put DEBUG: &=base_uri;
%let ext=%lowcase(&ext);
/* Create a local copy of the Viya response containing all file type info, if
it does not already exist. */
%if not %mf_existds(&viyaFileExtRespLibDs) %then %do;
/* Create a temp file and fill with JSON that declares */
/* VIYA file-type details for the given file extension */
%local viyatypedefs;
%let viyatypedefs=%mf_getuniquefileref();
filename &viyatypedefs temp;
%let url = &base_uri/types/types?limit=999999;
proc http oauth_bearer=sas_services out=&viyatypedefs
url="&url";
run;
%if &mdebug=1 %then %put DEBUG: &sysmacroname &=url
&=SYS_PROCHTTP_STATUS_CODE &=SYS_PROCHTTP_STATUS_PHRASE;
%if (&SYS_PROCHTTP_STATUS_CODE ne 200) %then %do;
/* To avoid a breaking change, exit early if the request failed.
The calling process will proceed with empty macro variables. */
%put INFO: &sysmacroname File extension details were not retrieved.;
filename &viyatypedefs clear;
%return;
%end;
%if &mdebug=1 %then %do;
/* Dump the response to the log */
data _null_;
length line $120;
null=byte(0);
infile &viyatypedefs dlm=null lrecl=120 recfm=n;
input line $120.;
if _n_ = 1 then put "DEBUG:";
put line;
run;
%end;
/* Convert the content of that JSON into SAS datasets */
/* First prepare a new WORK-based folder to receive the datasets */
%local jsonworkfolder jsonlib opt_dlcreatedir;
%let jsonworkfolder=%sysfunc(pathname(work))/%mf_getuniquename(prefix=jsn_);
%let jsonlib=%mf_getuniquelibref(prefix=json);
/* And point a libname at it */
%let opt_dlcreatedir = %sysfunc(getoption(dlcreatedir));
options dlcreatedir; libname &jsonlib "&jsonworkfolder";
options &opt_dlcreatedir;
/* Read the json output once and copy datasets to its work folder */
%local libref1;
%let libref1=%mf_getuniquelibref();
libname &libref1 JSON fileref=&viyatypedefs automap=create;
proc copy in=&libref1 out=&jsonlib; run;
libname &libref1 clear;
/* Now give all rows belonging to the same items array a grouping value */
data &viyaFileExtRespLibDs;
length _viyaItemIdx 8;
set &jsonlib..alldata;
retain _viyaItemIdx 0;
/* Increment the row group index when a new 'items' group is observed */
if P=1 and P1='items' then _viyaItemIdx + 1;
run;
%if &mdebug=0 %then %do;
/* Tidy up, unless debug=1 */
proc datasets library=&jsonlib nolist kill; quit;
libname &jsonlib clear;
%end;
filename &viyatypedefs clear;
%end; /* If initial filetype query response didn't exist */
%if &mdebug %then %put DEBUG: Find the row-group for extension &ext;
%local itemRowGroup;
data _null_;
set &viyaFileExtRespLibDs;
where p1='items' and p2='extensions' and value="&ext";
call symputx('itemRowGroup',_viyaItemIdx,'l');
%if &mdebug %then %do;
putlog (_all_)(=);
%end;
run;
%if &mdebug %then %put DEBUG: &=itemRowGroup;
%if %mf_isBlank(&itemRowGroup) %then %do;
/* extension was not found */
%if &mdebug %then %put DEBUG: No type details found for extension "&ext";
%return;
%end;
/* Filter the cached response data down to the required file extension */
%local dsItems;
%let dsItems = %mf_getuniquename(prefix=dsItems_);
data work.&dsItems;
set &viyaFileExtRespLibDs;
where _viyaItemIdx = &itemRowGroup;
run;
/* Populate typeDefName, if requested */
%if (not %mf_isBlank(&typeDefNameVar)) %then %do;
%let &typeDefNameVar = %mf_getvalue(
&dsItems,value,filter=%quote(p1="items" and p2="name"));
%if &mdebug %then
%put DEBUG: &=typeDefNameVar &typeDefNameVar=&&&typeDefNameVar;
%end;
/* Populate mediaType, if requested */
%if (not %mf_isBlank(&mediaTypeVar)) %then %do;
%let &mediaTypeVar = %mf_getvalue(
&dsItems,value,filter=%quote(p1="items" and p2="mediaType"));
%if &mdebug %then %put DEBUG: &=mediaTypeVar &mediaTypeVar=&&&mediaTypeVar;
%end;
/* Populate properties macro variable, if requested */
%if not %mf_isBlank(&propertiesVar) %then %do;
/* Filter dsItems down to the properties */
%local dsProperties;
%let dsProperties = %mf_getuniquename(prefix=dsProperties_);
data work.&dsProperties ( rename=(p3 = propertyName) );
set work.&dsItems;
where p2="properties" and v=1;
run;
/* Check for 1+ properties */
%if ( %mf_nobs(&dsProperties) = 0 ) %then %do;
%let &propertiesVar = %str();
%if &mdebug %then %put DEBUG: &SYSMACRONAME - No Viya properties %trim(
)found for file suffix %str(%')&ext%str(%');
%end;
%else %do;
/* Properties potentially span multiple rows in the input table */
data _null_;
length
line $32767
properties $32767
;
retain properties;
set &dsProperties end=last;
if _n_ = 1 then properties = '{';
line = cats(quote(trim(propertyName)),':');
/* Only strings and bools appear in properties */
if value not in ("true","false") then value = quote(trim(value));
line = catx(' ',line,value);
/* Add a comma separator to all except the last line */
if not last then line = cats(line,',');
/* Add this line to the output value */
properties = catx(' ',properties,line);
if last then do;
/* Close off the properties object and output to the macro variable */
properties=catx(' ',properties,'}');
call symputx("&propertiesVar",properties);
end;
run;
%if &mdebug %then
%put DEBUG: &=propertiesVar &propertiesVar=&&&propertiesVar;
%end;
%end;
%mend mv_getViyaFileExtParms;
+19 -36
View File
@@ -121,7 +121,7 @@
@li mf_nobs.sas
@li mp_abort.sas
@li mf_getplatform.sas
@li mf_getvarlist.sas
@li mf_getuniquefileref.sas
@li mf_existvarlist.sas
@li mv_jobwaitfor.sas
@li mv_jobexecute.sas
@@ -188,8 +188,6 @@
%if %mf_existvarList(&inds,FLOW_ID)=0 %then %do;
retain FLOW_ID 0;
%end;
/* https://github.com/sasjs/adapter/pull/845#issuecomment-2956589644 */
retain _omitSessionResults "false";
set &inds;
&dbg. putlog (_all_)(=);
run;
@@ -234,12 +232,14 @@ data _null_;
if last then call symputx('flowcnt',cnt,'l');
run;
/* prepare temporary datasets */
%local fid jid jds jdsapp jdsrunning jdswaitfor;
/* prepare temporary datasets and frefs */
%local fid jid jds jjson jdsapp jdsrunning jdswaitfor jfref;
data;run;%let jds=&syslast;
data;run;%let jjson=&syslast;
data;run;%let jdsapp=&syslast;
data;run;%let jdsrunning=&syslast;
data;run;%let jdswaitfor=&syslast;
%let jfref=%mf_getuniquefileref();
/* start loop */
%do fid=1 %to &flowcnt;
@@ -257,39 +257,23 @@ data;run;%let jdswaitfor=&syslast;
&dbg. if _n_= 1 then putlog "Loop &fid";
&dbg. putlog (_all_)(=);
run;
/* build list of char and num vars in json format */
/* Viya 2026 expects all values to be strings */
%local nvars cvars ii _vnm;
%let cvars=%mf_getvarlist(&jds,typefilter=C);
%let nvars=%mf_getvarlist(&jds,typefilter=N);
%put exporting job variables in json format;
%do jid=1 %to &jcnt;
data _null_;
data &jjson;
set &jds;
if _n_=&jid;
length _param $32767;
_param='';
%if %length(&cvars)>0 %then %do ii=1 %to %sysfunc(countw(&cvars,%str( )));
%let _vnm=%scan(&cvars,&ii,%str( ));
if _param ne '' then _param=cats(_param,',');
_param=cats(_param,'"'
,"%lowcase(&_vnm)"
,'":'
,quote(trim(&_vnm))
);
%end;
%if %length(&nvars)>0 %then %do ii=1 %to %sysfunc(countw(&nvars,%str( )));
%let _vnm=%scan(&nvars,&ii,%str( ));
if _param ne '' then _param=cats(_param,',');
_param=cats(_param,'"'
,"%lowcase(&_vnm)"
,'":"'
,strip(put(&_vnm,best32.))
,'"'
);
%end;
call symputx(cats('jparams',&jid),_param,'l');
stop;
if _n_=&jid then do;
output;
stop;
end;
run;
proc json out=&jfref;
export &jjson / nosastags fmtnumeric;
run;
data _null_;
infile &jfref lrecl=32767;
input;
jparams=cats('jparams',symget('jid'));
call symputx(jparams,substr(_infile_,3,length(_infile_)-4));
run;
%local jobuid&jid;
%let jobuid&jid=0; /* used in next loop */
@@ -334,7 +318,6 @@ data;run;%let jdswaitfor=&syslast;
,paramstring=%superq(jparams&jid)
,outds=&jdsapp
,contextname=&&context&jid
,mdebug=&mdebug
)
data &jdsapp;
format jobparams $32767.;
+9 -29
View File
@@ -55,14 +55,14 @@
,showmeta=N,maxobs=MAX,workobs=0
);
%global _webin_file_count _webin_fileuri _debug _omittextlog _webin_name
sasjs_tables SYS_JES_JOB_URI _EXECUTIONTASKS;
%if %index("&_debug",log) %then %let _debug=128;
sasjs_tables SYS_JES_JOB_URI;
%if %index("&_debug",log) %then %let _debug=131;
%local i tempds table;
%let action=%upcase(&action);
%if &action=FETCH %then %do;
%if %upcase(&_omittextlog)=FALSE or %str(&_debug) ge 128 %then %do;
%if %upcase(&_omittextlog)=FALSE or %str(&_debug) ge 131 %then %do;
options mprint notes mprintnest;
%end;
@@ -70,11 +70,6 @@
%let _webin_file_count=%eval(&_webin_file_count+0);
%let _webin_fileuri1=&_webin_fileuri;
%let _webin_name1=&_webin_name;
%if &_EXECUTIONTASKS=true %then %do;
/* TODO - remove this once SAS Track CS0409737 is resolved */
/* links: https://github.com/sasjs/adapter/issues/884 */
%if %upcase(&_webin_name)=_SASJS_NOOP %then %let _webin_file_count=0;
%end;
%end;
/* if the sasjs_tables param is passed, we expect param based upload */
@@ -104,10 +99,7 @@
data _null_;
infile "%sysfunc(pathname(work))/&table..csv" termstr=crlf ;
input;
/* a plain $ informat strips leading blanks - use $char instead */
if _n_=1 then call symputx('input_statement'
,prxchange('s/:\$(?=[0-9 ])/:\$char/i',-1,_infile_)
);
if _n_=1 then call symputx('input_statement',_infile_);
list;
data work.&table;
infile "%sysfunc(pathname(work))/&table..csv" firstobs=2 dsd
@@ -118,20 +110,13 @@
%end;
%else %do i=1 %to &_webin_file_count;
/* read in any files that are sent */
%if &_EXECUTIONTASKS=true %then %do;
filename indata "%sysfunc(pathname(&&_webin_fileref&i))" lrecl=999999;
%end;
%else %do;
filename indata filesrvc "&&_webin_fileuri&i" lrecl=999999;
%end;
/* this part needs refactoring for wide files */
filename indata filesrvc "&&_webin_fileuri&i" lrecl=999999;
data _null_;
infile indata termstr=crlf lrecl=32767;
input;
/* a plain $ informat strips leading blanks - use $char instead */
if _n_=1 then call symputx('input_statement'
,prxchange('s/:\$(?=[0-9 ])/:\$char/i',-1,_infile_)
);
%if %str(&_debug) ge 128 %then %do;
if _n_=1 then call symputx('input_statement',_infile_);
%if %str(&_debug) ge 131 %then %do;
if _n_<20 then putlog _infile_;
else stop;
%end;
@@ -164,9 +149,6 @@
/* setup json */
data _null_;file &fref;
%if %str(&_debug) ge 128 and &_EXECUTIONTASKS=true %then %do;
put '>>weboutBEGIN<<';
%end;
put '{"SYSDATE" : "' "&SYSDATE" '"';
put ',"SYSTIME" : "' "&SYSTIME" '"';
run;
@@ -279,9 +261,7 @@
memsize=quote(cats(memsize));
put ',"MEMSIZE" : ' memsize;
put "}";
%if %str(&_debug) ge 128 and &_EXECUTIONTASKS=true %then %do;
put '>>weboutEND<<';
%end;
%if %upcase(&fref) ne _WEBOUT and &stream=Y %then %do;
data _null_; rc=fcopy("&fref","_webout");run;
%end;
-149
View File
@@ -1,149 +0,0 @@
/**
@file
@brief Appends a text file to a SASjs Stored Program, Viya SAS program, or
SAS 9 Stored Process
@details Extracts the source code from a SASjs Stored Program, Viya SAS
program (file in SAS Drive), or SAS 9 Stored Process, appends the contents
of a provided text file, then deletes and recreates the target item with the
combined content.
This is useful for dynamically modifying deployed programs, for example to
add test-specific configuration or runtime settings.
Usage:
%* compile macros ;
filename mc url
"https://raw.githubusercontent.com/sasjs/core/main/all.sas";
%inc mc;
%* write some content to append;
filename append temp;
data _null_;
file append;
put "libname mylib '/some/path';";
run;
%* append to existing program;
%mx_append2pgm(/Public/app/common/settings, inref=append)
@param [in] loc The full path to the Viya SAS program, SAS 9 Stored Process,
or SASjs Stored Program in Drive or Metadata, WITHOUT the .sas extension
(SASjs only)
@param [in] inref= (0) Fileref pointing to the content to be appended to the
target program.
@param [in] mdebug= (0) Set to 1 to show debug messages in the log
<h4> SAS Macros </h4>
@li mf_getplatform.sas
@li mf_getuniquefileref.sas
@li mm_createstp.sas
@li mm_deletestp.sas
@li mm_getstpcode.sas
@li ms_createfile.sas
@li ms_deletefile.sas
@li mv_createfile.sas
@li mv_deletefoldermember.sas
@li mx_getcode.sas
<h4> Related Macros </h4>
@li mx_append2pgm.test.sas
@li mx_getcode.sas
@li mx_createjob.sas
@author Allan Bowe
**/
%macro mx_append2pgm(loc
,inref=0
,mdebug=0
)/*/STORE SOURCE*/;
%local platform name shortloc coderef combref work tmpfile viyaref;
%let platform=%mf_getplatform();
%if &mdebug=1 %then %do;
%put &sysmacroname entry vars:;
%put _local_;
%end;
%if &syscc ne 0 %then %do;
%put syscc=&syscc - &sysmacroname will not execute in this state;
%return;
%end;
/* extract name & path from loc */
data _null_;
length name shortloc $500;
loc=symget('loc');
name=scan(loc,-1,'/');
shortloc=substr(loc,1,length(loc)-length(name)-1);
call symputx('name',name,'l');
call symputx('shortloc',shortloc,'l');
run;
/* create a combined fileref with original + appended content */
%let combref=%mf_getuniquefileref();
%let work=%sysfunc(pathname(work));
%let tmpfile=&combref..sas;
filename &combref "&work/&tmpfile" lrecl=32000;
%if &platform=SASVIYA %then %do;
/* On Viya, read the SAS program file from SAS Drive using filesrvc */
%let viyaref=%mf_getuniquefileref();
filename &viyaref filesrvc folderpath="&shortloc";
data _null_;
file &combref lrecl=32000 termstr=crlf;
infile &viyaref("&name..sas") lrecl=32000 end=last;
input;
put _infile_;
run;
filename &viyaref clear;
%symdel _FILESRVC_&viyaref._URI;
%end;
%else %do;
/* For SAS9 and SASJS, use mx_getcode */
%let coderef=%mf_getuniquefileref();
%mx_getcode(&loc, outref=&coderef)
data _null_;
file &combref lrecl=32000 termstr=crlf;
infile &coderef lrecl=32000 end=last;
input;
put _infile_;
run;
filename &coderef clear;
%end;
/* append the new content */
data _null_;
file &combref lrecl=32000 termstr=crlf mod;
infile &inref lrecl=32000;
input;
put _infile_;
run;
/* delete and recreate the target item */
%if &platform=SASJS %then %do;
%ms_deletefile(&loc..sas)
%ms_createfile(&loc..sas, inref=&combref, mdebug=&mdebug)
%end;
%else %if &platform=SASVIYA %then %do;
%mv_deletefoldermember(path=&shortloc, name=&name..sas, contenttype=file)
%mv_createfile(path=&shortloc, name=&name..sas, inref=&combref)
%end;
%else %do;
/* SAS 9 */
%mm_deletestp(target=&loc)
%mm_createstp(stpname=&name
,filename=&tmpfile
,directory=&work
,tree=&shortloc
,stptype=2
,mDebug=&mdebug
,minify=NO
)
%end;
filename &combref clear;
%mend mx_append2pgm;
-111
View File
@@ -1,111 +0,0 @@
/**
@file mx_createjob.sas
@brief Create a job in SAS 9, Viya or SASjs
@details Creates a Stored Process in SAS 9, a Job Execution Service in SAS
Viya, or a Stored Program on SASjs Server - depending on the executing
environment.
Usage:
%* compile macros ;
filename mc url "https://raw.githubusercontent.com/sasjs/core/main/all.sas";
%inc mc;
%* write some code;
filename ft15f001 temp;
parmcards4;
data example1;
set sashelp.class;
run;
;;;;
%* create the job;
%mx_createjob(path=/Public/app/jobs,name=myjob,replace=YES)
<h4> SAS Macros </h4>
@li mf_getplatform.sas
@li mm_createstp.sas
@li ms_createfile.sas
@li mv_createjob.sas
@param [in,out] path= The full folder path where the job will be created
@param [in,out] name= Job name. Avoid spaces.
@param [in] desc= The description of the job (optional)
@param [in] precode= Space separated list of filerefs, pointing to the code
that needs to be attached to the beginning of the job (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 job in that
location
@param [in] mDebug= (0) set to 1 to show debug messages in the log
@author Allan Bowe
<h4> Related Macros </h4>
@li mx_createjob.test.sas
@li mx_createwebservice.sas
**/
%macro mx_createjob(path=HOME
,name=initJob
,precode=
,code=ft15f001
,desc=This job was created by the mx_createjob macro
,replace=YES
,mdebug=0
)/*/STORE SOURCE*/;
%if &syscc ge 4 %then %do;
%put syscc=&syscc - &sysmacroname will not execute in this state;
%return;
%end;
/* combine precode and code into a single file */
%local tempref x fref freflist;
%let tempref=%mf_getuniquefileref();
%local work tmpfile;
%let work=%sysfunc(pathname(work));
%let tmpfile=&tempref..sas;
filename &tempref "&work/&tmpfile";
%let freflist=&precode &code ;
%do x=1 %to %sysfunc(countw(&freflist));
%let fref=%scan(&freflist,&x);
%put &sysmacroname: adding &fref;
data _null_;
file &tempref lrecl=3000 termstr=crlf mod;
infile &fref lrecl=3000;
input;
put _infile_;
run;
%end;
%local platform; %let platform=%mf_getplatform();
%if &platform=SASVIYA %then %do;
%if "&path"="HOME" %then %let path=/Users/&sysuserid/My Folder;
%mv_createjob(path=&path
,name=&name
,code=&tempref
,desc=&desc
,replace=&replace
)
%end;
%else %if &platform=SASJS %then %do;
%if "&path"="HOME" %then %let path=/Users/&_sasjs_username/My Folder;
%ms_createfile(&path/&name..sas
,inref=&tempref
,mdebug=&mdebug
)
%end;
%else %do;
%if "&path"="HOME" %then %let path=/User Folders/&_METAPERSON/My Folder;
%mm_createstp(stpname=&name
,filename=&tmpfile
,directory=&work
,tree=&path
,stpdesc=&desc
,mDebug=&mdebug
)
%end;
filename &tempref clear;
%mend mx_createjob;
-3
View File
@@ -48,9 +48,6 @@ Usage:
@author Allan Bowe
<h4> Related Macros </h4>
@li mx_createjob.sas
**/
%macro mx_createwebservice(path=HOME