1
0
mirror of https://github.com/sasjs/core.git synced 2026-03-22 10:33:22 +00:00

Compare commits

..

39 Commits

Author SHA1 Message Date
github-actions
2b74caede4 chore: updating all.sas 2026-03-19 00:52:45 +00:00
Allan Bowe
b5a76600d6 fix: bumping action packages 2026-03-19 00:52:09 +00:00
Allan Bowe
13113cacaf Merge pull request #418 from sasjs/improve_mf_getvalue
feat(mf_getvalue)!: Breaking Change - Improve mf_getvalue()
2026-03-19 00:20:53 +00:00
github-actions
ae7f93aa4e chore: updating all.sas 2026-03-18 14:10:05 +00:00
Trevor Moody
e3b8ee69a9 feat(mf_getvalue)!: specify row and raise SYSCC value upon issue 2026-03-18 14:09:18 +00:00
Allan Bowe
78287ed5d3 Merge pull request #416 from sasjs/snow
Snow
2026-03-10 20:11:44 +00:00
github-actions
5944619488 chore: updating all.sas 2026-03-10 20:04:12 +00:00
allan
df0c9899cf feat: snowflake support in mm_assigndirectlib 2026-03-10 20:01:38 +00:00
Allan Bowe
737eb65251 Merge pull request #415 from sasjs/sf
feat: snowflake support in mf_getschema
2026-02-18 19:01:37 +00:00
github-actions
c50555a6e2 chore: updating all.sas 2026-02-18 17:23:52 +00:00
allan
c69639a228 feat: snowflake support in mf_getschema 2026-02-18 17:23:25 +00:00
Allan Bowe
9930b84785 Merge pull request #413 from sasjs/issue412
fix: adding uri to length statement in mv_deletejes

Closes #412
2026-02-09 23:07:11 +00:00
github-actions
7686b7fb99 chore: updating all.sas 2026-02-09 23:06:39 +00:00
allan
def0514731 fix: adding uri to length statement in mv_deletejes 2026-02-09 23:06:15 +00:00
Allan Bowe
502fafa53d Merge pull request #411 from sasjs/mx_createjob
feat: new mx_createjob macro and associated test
2026-02-09 17:59:18 +00:00
github-actions
6721e73ecd chore: updating all.sas 2026-02-09 17:55:09 +00:00
allan
9e36e82ff2 fix: uninitialied warnings in strict mode for mv_deletejes 2026-02-09 17:54:18 +00:00
github-actions
87ce565321 chore: updating all.sas 2026-02-09 01:36:38 +00:00
allan
3bb902b74e chore: adding scope test 2026-02-09 01:36:11 +00:00
github-actions
dd5e4edc80 chore: updating all.sas 2026-02-09 01:29:50 +00:00
allan
835369381c chore: more tests 2026-02-09 01:29:22 +00:00
github-actions
c32819df9f chore: updating all.sas 2026-02-09 00:52:07 +00:00
allan
c1f1fcdebf feat: new mx_createjob macro and associated test 2026-02-09 00:51:30 +00:00
Trevor Moody
641966eed8 Merge pull request #410 from sasjs/bumpNodeVersionForTest
fix(test): bumped Node to lts/iron (v20.x.x) for tests
2025-12-16 15:03:41 +00:00
github-actions
16922c525c chore: updating all.sas 2025-12-16 13:27:30 +00:00
Trevor Moody
f315f803db Merge branch 'bumpNodeVersionForTest' of github.com:sasjs/core into bumpNodeVersionForTest 2025-12-16 13:26:33 +00:00
Trevor Moody
bae5431d24 chore(build): ensure test branch of @sasjs/core is sourced by @sasjs/cli 2025-12-16 13:26:07 +00:00
Trevor Moody
76728cbc6c fix: corrected dependent macro name 2025-12-16 13:23:23 +00:00
github-actions
a221a706b4 chore: updating all.sas 2025-12-10 17:22:58 +00:00
Trevor Moody
f3b712ecee fix(test): bumped Node to lts/iron (v20.x.x) for tests 2025-12-10 17:22:22 +00:00
Allan Bowe
db15c66e68 Merge pull request #409 from sasjs/build/disable-npm-scripts
Disable npm scripts
2025-12-09 16:10:45 +00:00
github-actions
62796ab6e6 chore: updating all.sas 2025-12-09 15:30:06 +00:00
mulahasanovic
7eca3b5e07 build(security): disable npm install scripts 2025-12-09 16:26:15 +01:00
Allan Bowe
66ceb738c8 Merge pull request #408 from sasjs/mp_stripdiff_fix
Bug fixes - a header dependency and the sasjsconfig global macroFolders includes all platforms
2025-11-27 15:28:19 +00:00
github-actions
9d37856fc2 chore: updating all.sas 2025-11-27 15:22:43 +00:00
Trevor Moody
14987e3914 Merge branch 'main' into mp_stripdiff_fix 2025-11-27 15:22:24 +00:00
github-actions
10857b2153 chore: updating all.sas 2025-11-27 15:08:51 +00:00
Trevor Moody
ac2a054c84 fix: global macroFolders includes all platforms 2025-11-27 15:08:18 +00:00
Trevor Moody
f60b298bbb fix: moved dependency to 'SAS Macros' section 2025-11-27 14:43:57 +00:00
20 changed files with 834 additions and 69 deletions

View File

@@ -13,7 +13,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v6
- name: Install dependencies
run: |
@@ -54,7 +54,7 @@ jobs:
echo "REFRESH_TOKEN=${{secrets.SAS9_4GL_IO_REFRESH_TOKEN}}" >> .env.server
- name: Semantic Release
uses: cycjimmy/semantic-release-action@v4
uses: cycjimmy/semantic-release-action@v6
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}

View File

@@ -13,7 +13,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v6
- name: Install dependencies
run: |

View File

@@ -12,10 +12,10 @@ jobs:
strategy:
matrix:
node-version: [lts/hydrogen]
node-version: [lts/iron]
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v6
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v2
with:
@@ -73,6 +73,8 @@ 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
@@ -84,3 +86,4 @@ jobs:
SAS_PASSWORD: ${{secrets.SAS_PASSWORD}}
ACCESS_TOKEN: ${{secrets.ACCESS_TOKEN}}
REFRESH_TOKEN: ${{secrets.REFRESH_TOKEN}}

View File

@@ -1,6 +0,0 @@
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/*

View File

@@ -1,27 +0,0 @@
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

View File

@@ -1,6 +1,5 @@
all.sas
build.py
.gitpod*
tests/
sasjs/
.github/

1
.npmrc Normal file
View File

@@ -0,0 +1 @@
ignore-scripts=true

210
all.sas
View File

@@ -1018,8 +1018,9 @@ or %index(&pgm,/tests/testteardown)
%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/Owner'
libname="%upcase(&libref)" and sysname=:'Schema'
)),i));
%if (&dsid ^= 0) %then %do;
%let vnum=%sysfunc(varnum(&dsid,SYSVALUE));
@@ -1214,8 +1215,9 @@ or %index(&pgm,/tests/testteardown)
%mend mf_getuser;
/**
@file
@brief Retrieves a value from a dataset. If no filter supplied, then first
record is used.
@brief Retrieves a value from a dataset. Returned value is fetched from the
'fetchobs=' record (row 1 by default), after applying the optional filter.
@details Be sure to <code>%quote()</code> your where clause. Example usage:
%put %mf_getvalue(sashelp.class,name,filter=%quote(age=15));
@@ -1230,24 +1232,43 @@ or %index(&pgm,/tests/testteardown)
@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
%macro mf_getvalue(libds,variable,filter=1,fetchobs=1
)/*/STORE SOURCE*/;
%if %mf_getattrn(&libds,NLOBS)>0 %then %do;
%local dsid rc &variable;
%let dsid=%sysfunc(open(&libds(where=(&filter))));
%local dsid;
%let dsid=%sysfunc(open(&libds(where=(&filter))));
%if (&dsid) %then %do;
%local rc &variable;
%syscall set(dsid);
%let rc = %sysfunc(fetch(&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(close(&dsid));
%trim(&&&variable)
%end;
%mend mf_getvalue;/**
%else %do;
%put %sysfunc(sysmsg());
%let syscc = %sysfunc(max(&syscc,%sysfunc(sysrc())));
%end;
%mend mf_getvalue;
/**
@file
@brief Returns number of variables in a dataset
@details Useful to identify those renagade datasets that have no columns!
@@ -13859,10 +13880,10 @@ run;
@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
@li mp_ds2squeeze.sas
@li mp_stackdiffs.sas
@li mp_storediffs.sas
@li mp_stripdiffs.test.sas
@@ -15372,7 +15393,8 @@ 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);
@@ -15572,6 +15594,55 @@ 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;
@@ -26427,7 +26498,8 @@ libname &libref1a JSON fileref=&fname1a;
%let found=0;
/* %put Getting object uri from &libref1a..items; */
data _null_;
length contenttype name $1000;
length contenttype name uri $1000;
call missing(of _all_);
set &libref1a..items;
if contenttype='jobDefinition' and upcase(name)="%upcase(&name)" then do;
call symputx('uri',cats("&base_uri",uri),'l');
@@ -30910,6 +30982,117 @@ endsub;
%end;
%mend mcf_string2file;/**
@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;
/**
@file mx_createwebservice.sas
@brief Create a web service in SAS 9, Viya or SASjs
@details Creates a SASJS ready Stored Process in SAS 9, a Job Execution
@@ -30959,6 +31142,9 @@ Usage:
@author Allan Bowe
<h4> Related Macros </h4>
@li mx_createjob.sas
**/
%macro mx_createwebservice(path=HOME

View File

@@ -25,8 +25,9 @@
%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/Owner'
libname="%upcase(&libref)" and sysname=:'Schema'
)),i));
%if (&dsid ^= 0) %then %do;
%let vnum=%sysfunc(varnum(&dsid,SYSVALUE));

View File

@@ -1,7 +1,8 @@
/**
@file
@brief Retrieves a value from a dataset. If no filter supplied, then first
record is used.
@brief Retrieves a value from a dataset. Returned value is fetched from the
'fetchobs=' record (row 1 by default), after applying the optional filter.
@details Be sure to <code>%quote()</code> your where clause. Example usage:
%put %mf_getvalue(sashelp.class,name,filter=%quote(age=15));
@@ -16,21 +17,39 @@
@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
%macro mf_getvalue(libds,variable,filter=1,fetchobs=1
)/*/STORE SOURCE*/;
%if %mf_getattrn(&libds,NLOBS)>0 %then %do;
%local dsid rc &variable;
%let dsid=%sysfunc(open(&libds(where=(&filter))));
%local dsid;
%let dsid=%sysfunc(open(&libds(where=(&filter))));
%if (&dsid) %then %do;
%local rc &variable;
%syscall set(dsid);
%let rc = %sysfunc(fetch(&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(close(&dsid));
%trim(&&&variable)
%end;
%mend mf_getvalue;
%else %do;
%put %sysfunc(sysmsg());
%let syscc = %sysfunc(max(&syscc,%sysfunc(sysrc())));
%end;
%mend mf_getvalue;

View File

@@ -36,10 +36,10 @@
@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
@li mp_ds2squeeze.sas
@li mp_stackdiffs.sas
@li mp_storediffs.sas
@li mp_stripdiffs.test.sas

52
meta/mm_assigndirectlib.sas Executable file → Normal file
View File

@@ -213,7 +213,8 @@ 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);
@@ -413,6 +414,55 @@ 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;

View File

@@ -5,7 +5,10 @@
"ddl",
"fcmp",
"lua",
"meta",
"metax",
"server",
"viya",
"xplatform",
"tests/base",
"tests/ddlonly",
@@ -42,7 +45,6 @@
"deployScripts": []
},
"macroFolders": [
"viya",
"tests/viyaonly"
],
"contextName": "SAS Job Execution compute context"
@@ -56,8 +58,6 @@
},
"appLoc": "/Shared Data/temp/macrocore",
"macroFolders": [
"meta",
"metax",
"tests/sas9only"
],
"programFolders": [],
@@ -82,7 +82,6 @@
"deployScripts": []
},
"macroFolders": [
"server",
"tests/serveronly"
]
},

View File

@@ -0,0 +1,91 @@
/**
@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; /* Reset w@rning To ensure confidence in next test */
/* - Test 6 -
Get value from default observation.
Dataset does not exist.
*/
%let test_value=%mf_getvalue(work.test_data_x,i);
%mp_assert(
iftrue=(&test_value=%str() and &syscc gt 0),
desc=Test fetching value from 1st row of non-existent data,
outds=work.test_results
)
%let syscc = 0; /* To reset expected error and allow test job to exit clean. */

View File

@@ -0,0 +1,30 @@
/**
@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
)

View File

@@ -0,0 +1,304 @@
/**
@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)
%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
)

View File

@@ -3,10 +3,10 @@
@brief Testing mx_testservice.sas macro
Be sure to run <code>%let mcTestAppLoc=/Public/temp/macrocore;</code> when
runnin in Studio
running in Studio
<h4> SAS Macros </h4>
@li mp_createwebservice.sas
@li mx_createwebservice.sas
@li mx_testservice.sas
@li mp_assert.sas

View File

@@ -117,7 +117,8 @@ libname &libref1a JSON fileref=&fname1a;
%let found=0;
/* %put Getting object uri from &libref1a..items; */
data _null_;
length contenttype name $1000;
length contenttype name uri $1000;
call missing(of _all_);
set &libref1a..items;
if contenttype='jobDefinition' and upcase(name)="%upcase(&name)" then do;
call symputx('uri',cats("&base_uri",uri),'l');

111
xplatform/mx_createjob.sas Normal file
View File

@@ -0,0 +1,111 @@
/**
@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;

View File

@@ -48,6 +48,9 @@ Usage:
@author Allan Bowe
<h4> Related Macros </h4>
@li mx_createjob.sas
**/
%macro mx_createwebservice(path=HOME