1
0
mirror of https://github.com/sasjs/core.git synced 2026-01-03 23:50:06 +00:00

Merge pull request #63 from sasjs/issue62

feat: adding FCMP capability
This commit is contained in:
Allan Bowe
2021-08-19 00:00:15 +03:00
committed by GitHub
44 changed files with 651 additions and 143 deletions

View File

@@ -41,6 +41,13 @@ Documentation: https://core.sasjs.io
- No X command
- Prefixes: _mf_, _mp_
**fcmp** library (SAS9/Viya)
- Function and macro names are identical, except for special cases
- Prefixes: _mcf_
The fcmp macros are used to generate fcmp functions, and can be used with or
without the `proc fcmp` wrapper.
**meta** library (SAS9 only)
- OS independent

206
all.sas
View File

@@ -147,6 +147,40 @@ options noquotelenmax;
%end;
%mend mf_existfileref;/**
@file
@brief Checks if a function exists
@details Returns 1 if the function exists, else 0. Note that this function
can be slow as it needs to open the sashelp.vfuncs table.
Usage:
%put %mf_existfunction(CAT);
%put %mf_existfunction(DOG);
Full credit to [Bart](https://sasensei.com/user/305) for the vfunc pointer
and the tidy approach for pure macro data set filtering.
Check out his [SAS Packages](https://github.com/yabwon/SAS_PACKAGES)
framework!
@param [in] name (positional) - function name
@author Allan Bowe
**/
/** @cond */
%macro mf_existfunction(name
)/*/STORE SOURCE*/;
%local dsid rc exist;
%let dsid=%sysfunc(open(sashelp.vfunc(where=(fncname="%upcase(&name)"))));
%let exist = %sysfunc(fetch(&dsid, NOSET));
%let rc = %sysfunc(close(&dsid));
%sysevalf(0 = &exist)
%mend mf_existfunction;
/** @endcond *//**
@file
@brief Checks if a variable exists in a data set.
@details Returns 0 if the variable does NOT exist, and return the position of
@@ -18804,3 +18838,175 @@ run;
%inc "%sysfunc(pathname(work))/ml_json.lua";
%mend ml_json;
/**
@file
@brief Provides a replacement for the stpsrv_header function
@details The stpsrv_header is normally a built-in function, used to set the
headers for SAS 9 Stored Processes as documented here:
https://go.documentation.sas.com/doc/en/itechcdc/9.4/stpug/srvhead.htm
The purpose of this custom function is to provide a replacement when running
similar code as a web service against
[sasjs/server](https://github.com/sasjs/server). It operates by creating a
text file with the headers. The location of this text file is determined by
a macro variable (`sasjs_stpsrv_header_loc`) which needs to be injected into
each service by the calling process, eg:
%let sasjs_stpsrv_header_loc = C:/temp/some_uuid/stpsrv_header.txt;
Note - the function works by appending headers to the file. If multiple same-
named headers are provided, they will all be appended - the calling process
needs to pick up the last one. This will mean removing the attribute if the
final record has an empty value.
The function takes the following (positional) parameters:
| PARAMETER | DESCRIPTION |
|------------|-------------|
| name $ | name of the header attribute to create|
| value $ | value of the header attribute|
It returns 0 if successful, or -1 if an error occured.
Usage:
%let sasjs_stpsrv_header_loc=%sysfunc(pathname(work))/stpsrv_header.txt;
%mcf_stpsrv_header(wrap=YES, insert_cmplib=YES)
data _null_;
rc=stpsrv_header('Content-type','application/text');
rc=stpsrv_header('Content-disposition',"attachment; filename=file.txt");
run;
data _null_;
infile "&sasjs_stpsrv_header_loc";
input;
putlog _infile_;
run;
@param [out] wrap= (NO) Choose YES to add the proc fcmp wrapper.
@param [out] insert_cmplib= (NO) Choose YES to insert the package into the
CMPLIB reference.
@param [out] lib= (work) The output library in which to create the catalog.
@param [out] cat= (sasjs) The output catalog in which to create the package.
@param [out] pkg= (utils) The output package in which to create the function.
Uses a 3 part format: libref.catalog.package
**/
%macro mcf_stpsrv_header(wrap=NO
,insert_cmplib=NO
,lib=WORK
,cat=SASJS
,pkg=UTILS
)/*/STORE SOURCE*/;
%if &wrap=YES %then %do;
proc fcmp outcat=&lib..&cat..&pkg;
%end;
function stpsrv_header(name $, value $);
length loc $128 val $512;
loc=symget('sasjs_stpsrv_header_loc');
val=trim(name)!!': '!!value;
length fref $8;
rc=filename(fref,loc);
if (rc ne 0) then return( -1 );
fid = fopen(fref,'a');
if (fid = 0) then return( -1 );
rc=fput(fid, val);
rc=fwrite(fid);
rc=fclose(fid);
rc=filename(fref);
return(0);
endsub;
%if &wrap=YES %then %do;
quit;
%end;
%if &insert_cmplib=YES %then %do;
options insert=(CMPLIB=(&lib..&cat));
%end;
%mend mcf_stpsrv_header;/**
@file
@brief Adds a string to a file
@details Creates an fcmp function for appending a string to an external file.
If the file does not exist, it is created.
The function itself takes the following (positional) parameters:
| PARAMETER | DESCRIPTION |
|------------|-------------|
| filepath $ | full path to the file|
| string $ | string to add to the file |
| mode $ | mode of the output - either APPEND (default) or CREATE |
It returns 0 if successful, or -1 if an error occured.
Usage:
%mcf_string2file(wrap=YES, insert_cmplib=YES)
data _null_;
rc=mcf_string2file(
"%sysfunc(pathname(work))/newfile.txt"
, "This is a test"
, "CREATE");
run;
data _null_;
infile "%sysfunc(pathname(work))/newfile.txt";
input;
putlog _infile_;
run;
@param [out] wrap= (NO) Choose YES to add the proc fcmp wrapper.
@param [out] insert_cmplib= (NO) Choose YES to insert the package into the
CMPLIB reference.
@param [out] lib= (work) The output library in which to create the catalog.
@param [out] cat= (sasjs) The output catalog in which to create the package.
@param [out] pkg= (utils) The output package in which to create the function.
Uses a 3 part format: libref.catalog.package
**/
%macro mcf_string2file(wrap=NO
,insert_cmplib=NO
,lib=WORK
,cat=SASJS
,pkg=UTILS
)/*/STORE SOURCE*/;
%if &wrap=YES %then %do;
proc fcmp outcat=&lib..&cat..&pkg;
%end;
function mcf_string2file(filepath $, string $, mode $);
if mode='APPEND' then fmode='a';
else fmode='o';
length fref $8;
rc=filename(fref,filepath);
if (rc ne 0) then return( -1 );
fid = fopen(fref,fmode);
if (fid = 0) then return( -1 );
rc=fput(fid, string);
rc=fwrite(fid);
rc=fclose(fid);
rc=filename(fref);
return(0);
endsub;
%if &wrap=YES %then %do;
quit;
%end;
%if &insert_cmplib=YES %then %do;
options insert=(CMPLIB=(&lib..&cat));
%end;
%mend mcf_string2file;

35
base/mf_existfunction.sas Normal file
View File

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

View File

@@ -84,7 +84,7 @@ options noquotelenmax;
"""
f = open('all.sas', "w") # r / r+ / rb / rb+ / w / wb
f.write(header)
folders=['base','meta','metax','viya','lua']
folders=['base','meta','metax','viya','lua','fcmp']
for folder in folders:
filenames = [fn for fn in Path('./' + folder).iterdir() if fn.match("*.sas")]
filenames.sort()

View File

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

79
fcmp/mcf_string2file.sas Normal file
View File

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

View File

@@ -20,6 +20,19 @@
*/
/*! \dir fcmp
* \brief Macros for generating FCMP functions
* \details These macros have the following attributes:
* Macro name matches compiled function / subroutine name
* Prefixes: _mcf_, _mcs_
The macro part is just a wrapper for the underlying function / subroutine,
and has switches for including the proc fcmp / quit statements and whether
to insert the package into the CMPLIB option.
*/
/*! \dir meta
* \brief Metadata Aware Macros
* \details These macros have the following attributes:

226
package-lock.json generated
View File

@@ -7,44 +7,43 @@
"name": "@sasjs/core",
"license": "MIT",
"devDependencies": {
"@sasjs/cli": "2.33.3"
"@sasjs/cli": "^2.37.2"
}
},
"node_modules/@sasjs/adapter": {
"version": "2.8.9",
"resolved": "https://registry.npmjs.org/@sasjs/adapter/-/adapter-2.8.9.tgz",
"integrity": "sha512-8UChcZlqqlmaMMaKOCr2Bc1h+i2KVDY0FINlPXQN5PdAgEMd8dbxI2p9bsiI1yjYjOBO9LuMl7B79/mwYCtyEw==",
"version": "2.10.0",
"resolved": "https://registry.npmjs.org/@sasjs/adapter/-/adapter-2.10.0.tgz",
"integrity": "sha512-GbvyIgbODnAJaBaz/Tz8/IwcujNOsZcwzbIuKQtG5y13BzgVPtx/e7b2TAhdoBqd+8uVh0CdrSDfOV/SuvPurg==",
"dev": true,
"dependencies": {
"@sasjs/utils": "^2.21.0",
"@sasjs/utils": "^2.27.1",
"axios": "^0.21.1",
"axios-cookiejar-support": "^1.0.1",
"form-data": "^4.0.0",
"https": "^1.0.0",
"jwt-decode": "^3.1.2",
"tough-cookie": "^4.0.0",
"url": "^0.11.0"
"tough-cookie": "^4.0.0"
},
"engines": {
"node": ">=15"
}
},
"node_modules/@sasjs/cli": {
"version": "2.33.3",
"resolved": "https://registry.npmjs.org/@sasjs/cli/-/cli-2.33.3.tgz",
"integrity": "sha512-y1uFM5MEE6eoKLrPUJbweDt4njSy9Y3CS5w5/U2xwNbiAyhiPXgkwCHjCQ8Qg+0rQnMvyNEn6qJuJZ3udc6T7w==",
"version": "2.37.2",
"resolved": "https://registry.npmjs.org/@sasjs/cli/-/cli-2.37.2.tgz",
"integrity": "sha512-Bcm8UFN9Y/ZON4T31Gu9mf1REn1pZoStHFVrix/yp7mchFt5rrrY5RbIqO/AI1jzhShSVg9P7oU4VPJrqki+SA==",
"dev": true,
"dependencies": {
"@sasjs/adapter": "2.8.9",
"@sasjs/core": "2.35.3",
"@sasjs/adapter": "2.10.0",
"@sasjs/core": "2.35.4",
"@sasjs/lint": "1.11.2",
"@sasjs/utils": "2.23.3",
"@types/url-parse": "1.4.3",
"btoa": "1.2.1",
"@sasjs/utils": "2.27.1",
"chalk": "4.1.1",
"csv-stringify": "5.6.2",
"dotenv": "10.0.0",
"esm": "3.2.25",
"find": "0.3.0",
"fs-extra": "10.0.0",
"get-installed-path": "4.0.8",
"js-base64": "3.6.1",
"jsdom": "16.6.0",
"jwt-decode": "3.1.2",
"lodash.groupby": "4.6.0",
@@ -60,9 +59,9 @@
}
},
"node_modules/@sasjs/core": {
"version": "2.35.3",
"resolved": "https://registry.npmjs.org/@sasjs/core/-/core-2.35.3.tgz",
"integrity": "sha512-3o5PU6DkihpA+Aibt1lRy4USqJI0VFa+wNsKCD+bUD2DLZICU3JablZQxwAPH70VWJGXAUJtDFj0T/iRo5Devg==",
"version": "2.35.4",
"resolved": "https://registry.npmjs.org/@sasjs/core/-/core-2.35.4.tgz",
"integrity": "sha512-Exr3+yRdvacKvbXQoi1RfGHi5NtZUkc7RwFNkemHTFXLYaIzPI8CGSCaQmKkwM1UteOJly2e2pw2YT6kHNY1NA==",
"dev": true
},
"node_modules/@sasjs/lint": {
@@ -75,11 +74,12 @@
}
},
"node_modules/@sasjs/utils": {
"version": "2.23.3",
"resolved": "https://registry.npmjs.org/@sasjs/utils/-/utils-2.23.3.tgz",
"integrity": "sha512-tEh4mGG80eUxSLpbPivA0vl4akMdauL+yZrLn1uUM8EyiXPvlcWPkQTeN6oHbyyAH108D9cfEBidTePZh1p5VQ==",
"version": "2.27.1",
"resolved": "https://registry.npmjs.org/@sasjs/utils/-/utils-2.27.1.tgz",
"integrity": "sha512-CYTQwEj89cc7H3tGiQQcyDkZYaWRc1HZJpOF8o2RHYS37fIAOy0SyyJdq6mcQ74Nb1u5AmFXPFIvnRCMEcTYeQ==",
"dev": true,
"dependencies": {
"@types/fs-extra": "^9.0.11",
"@types/prompts": "^2.0.13",
"chalk": "^4.1.1",
"cli-table": "^0.3.6",
@@ -87,7 +87,11 @@
"fs-extra": "^10.0.0",
"jwt-decode": "^3.1.2",
"prompts": "^2.4.1",
"rimraf": "^3.0.2",
"valid-url": "^1.0.9"
},
"engines": {
"node": ">=15"
}
},
"node_modules/@tootallnate/once": {
@@ -99,6 +103,15 @@
"node": ">= 6"
}
},
"node_modules/@types/fs-extra": {
"version": "9.0.12",
"resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-9.0.12.tgz",
"integrity": "sha512-I+bsBr67CurCGnSenZZ7v94gd3tc3+Aj2taxMT4yu4ABLuOgOjeFxX3dokG24ztSRg5tnT00sL8BszO7gSMoIw==",
"dev": true,
"dependencies": {
"@types/node": "*"
}
},
"node_modules/@types/node": {
"version": "16.3.1",
"resolved": "https://registry.npmjs.org/@types/node/-/node-16.3.1.tgz",
@@ -121,12 +134,6 @@
"dev": true,
"peer": true
},
"node_modules/@types/url-parse": {
"version": "1.4.3",
"resolved": "https://registry.npmjs.org/@types/url-parse/-/url-parse-1.4.3.tgz",
"integrity": "sha512-4kHAkbV/OfW2kb5BLVUuUMoumB3CP8rHqlw48aHvFy5tf9ER0AfOonBlX29l/DD68G70DmyhRlSYfQPSYpC5Vw==",
"dev": true
},
"node_modules/abab": {
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/abab/-/abab-2.0.5.tgz",
@@ -298,18 +305,6 @@
"integrity": "sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow==",
"dev": true
},
"node_modules/btoa": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/btoa/-/btoa-1.2.1.tgz",
"integrity": "sha512-SB4/MIGlsiVkMcHmT+pSmIPoNDoHg+7cMzmt3Uxt628MTz2487DKSqK/fuhFBrkuqrYv5UCEnACpF4dTFNKc/g==",
"dev": true,
"bin": {
"btoa": "bin/btoa.js"
},
"engines": {
"node": ">= 0.4.0"
}
},
"node_modules/buffer": {
"version": "5.7.1",
"resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz",
@@ -657,9 +652,9 @@
}
},
"node_modules/follow-redirects": {
"version": "1.14.1",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.1.tgz",
"integrity": "sha512-HWqDgT7ZEkqRzBvc2s64vSZ/hfOceEol3ac/7tKwzuvEyWx3/4UegXh5oBOIotkGsObyk3xznnSRVADBgWSQVg==",
"version": "1.14.2",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.2.tgz",
"integrity": "sha512-yLR6WaE2lbF0x4K2qE2p9PEXKLDjUjnR/xmjS3wHAYxtlsI9MLLBJUZirAHKzUZDGLxje7w/cXR49WOUo4rbsA==",
"dev": true,
"funding": [
{
@@ -990,6 +985,12 @@
"integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=",
"dev": true
},
"node_modules/js-base64": {
"version": "3.6.1",
"resolved": "https://registry.npmjs.org/js-base64/-/js-base64-3.6.1.tgz",
"integrity": "sha512-Frdq2+tRRGLQUIQOgsIGSCd1VePCS2fsddTG5dTCqR0JHgltXWfsxnY0gIXPoMeRmdom6Oyq+UMOFg5suduOjQ==",
"dev": true
},
"node_modules/jsdom": {
"version": "16.6.0",
"resolved": "https://registry.npmjs.org/jsdom/-/jsdom-16.6.0.tgz",
@@ -1273,9 +1274,9 @@
}
},
"node_modules/path-parse": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz",
"integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==",
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
"integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
"dev": true
},
"node_modules/pify": {
@@ -1327,16 +1328,6 @@
"node": ">=6"
}
},
"node_modules/querystring": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz",
"integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=",
"deprecated": "The",
"dev": true,
"engines": {
"node": ">=0.4.x"
}
},
"node_modules/readable-stream": {
"version": "3.6.0",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
@@ -1595,22 +1586,6 @@
"node": ">= 10.0.0"
}
},
"node_modules/url": {
"version": "0.11.0",
"resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz",
"integrity": "sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=",
"dev": true,
"dependencies": {
"punycode": "1.3.2",
"querystring": "0.2.0"
}
},
"node_modules/url/node_modules/punycode": {
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz",
"integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=",
"dev": true
},
"node_modules/util-deprecate": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
@@ -1760,40 +1735,36 @@
},
"dependencies": {
"@sasjs/adapter": {
"version": "2.8.9",
"resolved": "https://registry.npmjs.org/@sasjs/adapter/-/adapter-2.8.9.tgz",
"integrity": "sha512-8UChcZlqqlmaMMaKOCr2Bc1h+i2KVDY0FINlPXQN5PdAgEMd8dbxI2p9bsiI1yjYjOBO9LuMl7B79/mwYCtyEw==",
"version": "2.10.0",
"resolved": "https://registry.npmjs.org/@sasjs/adapter/-/adapter-2.10.0.tgz",
"integrity": "sha512-GbvyIgbODnAJaBaz/Tz8/IwcujNOsZcwzbIuKQtG5y13BzgVPtx/e7b2TAhdoBqd+8uVh0CdrSDfOV/SuvPurg==",
"dev": true,
"requires": {
"@sasjs/utils": "^2.21.0",
"@sasjs/utils": "^2.27.1",
"axios": "^0.21.1",
"axios-cookiejar-support": "^1.0.1",
"form-data": "^4.0.0",
"https": "^1.0.0",
"jwt-decode": "^3.1.2",
"tough-cookie": "^4.0.0",
"url": "^0.11.0"
"tough-cookie": "^4.0.0"
}
},
"@sasjs/cli": {
"version": "2.33.3",
"resolved": "https://registry.npmjs.org/@sasjs/cli/-/cli-2.33.3.tgz",
"integrity": "sha512-y1uFM5MEE6eoKLrPUJbweDt4njSy9Y3CS5w5/U2xwNbiAyhiPXgkwCHjCQ8Qg+0rQnMvyNEn6qJuJZ3udc6T7w==",
"version": "2.37.2",
"resolved": "https://registry.npmjs.org/@sasjs/cli/-/cli-2.37.2.tgz",
"integrity": "sha512-Bcm8UFN9Y/ZON4T31Gu9mf1REn1pZoStHFVrix/yp7mchFt5rrrY5RbIqO/AI1jzhShSVg9P7oU4VPJrqki+SA==",
"dev": true,
"requires": {
"@sasjs/adapter": "2.8.9",
"@sasjs/core": "2.35.3",
"@sasjs/adapter": "2.10.0",
"@sasjs/core": "2.35.4",
"@sasjs/lint": "1.11.2",
"@sasjs/utils": "2.23.3",
"@types/url-parse": "1.4.3",
"btoa": "1.2.1",
"@sasjs/utils": "2.27.1",
"chalk": "4.1.1",
"csv-stringify": "5.6.2",
"dotenv": "10.0.0",
"esm": "3.2.25",
"find": "0.3.0",
"fs-extra": "10.0.0",
"get-installed-path": "4.0.8",
"js-base64": "3.6.1",
"jsdom": "16.6.0",
"jwt-decode": "3.1.2",
"lodash.groupby": "4.6.0",
@@ -1806,9 +1777,9 @@
}
},
"@sasjs/core": {
"version": "2.35.3",
"resolved": "https://registry.npmjs.org/@sasjs/core/-/core-2.35.3.tgz",
"integrity": "sha512-3o5PU6DkihpA+Aibt1lRy4USqJI0VFa+wNsKCD+bUD2DLZICU3JablZQxwAPH70VWJGXAUJtDFj0T/iRo5Devg==",
"version": "2.35.4",
"resolved": "https://registry.npmjs.org/@sasjs/core/-/core-2.35.4.tgz",
"integrity": "sha512-Exr3+yRdvacKvbXQoi1RfGHi5NtZUkc7RwFNkemHTFXLYaIzPI8CGSCaQmKkwM1UteOJly2e2pw2YT6kHNY1NA==",
"dev": true
},
"@sasjs/lint": {
@@ -1821,11 +1792,12 @@
}
},
"@sasjs/utils": {
"version": "2.23.3",
"resolved": "https://registry.npmjs.org/@sasjs/utils/-/utils-2.23.3.tgz",
"integrity": "sha512-tEh4mGG80eUxSLpbPivA0vl4akMdauL+yZrLn1uUM8EyiXPvlcWPkQTeN6oHbyyAH108D9cfEBidTePZh1p5VQ==",
"version": "2.27.1",
"resolved": "https://registry.npmjs.org/@sasjs/utils/-/utils-2.27.1.tgz",
"integrity": "sha512-CYTQwEj89cc7H3tGiQQcyDkZYaWRc1HZJpOF8o2RHYS37fIAOy0SyyJdq6mcQ74Nb1u5AmFXPFIvnRCMEcTYeQ==",
"dev": true,
"requires": {
"@types/fs-extra": "^9.0.11",
"@types/prompts": "^2.0.13",
"chalk": "^4.1.1",
"cli-table": "^0.3.6",
@@ -1833,6 +1805,7 @@
"fs-extra": "^10.0.0",
"jwt-decode": "^3.1.2",
"prompts": "^2.4.1",
"rimraf": "^3.0.2",
"valid-url": "^1.0.9"
}
},
@@ -1842,6 +1815,15 @@
"integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==",
"dev": true
},
"@types/fs-extra": {
"version": "9.0.12",
"resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-9.0.12.tgz",
"integrity": "sha512-I+bsBr67CurCGnSenZZ7v94gd3tc3+Aj2taxMT4yu4ABLuOgOjeFxX3dokG24ztSRg5tnT00sL8BszO7gSMoIw==",
"dev": true,
"requires": {
"@types/node": "*"
}
},
"@types/node": {
"version": "16.3.1",
"resolved": "https://registry.npmjs.org/@types/node/-/node-16.3.1.tgz",
@@ -1864,12 +1846,6 @@
"dev": true,
"peer": true
},
"@types/url-parse": {
"version": "1.4.3",
"resolved": "https://registry.npmjs.org/@types/url-parse/-/url-parse-1.4.3.tgz",
"integrity": "sha512-4kHAkbV/OfW2kb5BLVUuUMoumB3CP8rHqlw48aHvFy5tf9ER0AfOonBlX29l/DD68G70DmyhRlSYfQPSYpC5Vw==",
"dev": true
},
"abab": {
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/abab/-/abab-2.0.5.tgz",
@@ -1994,12 +1970,6 @@
"integrity": "sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow==",
"dev": true
},
"btoa": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/btoa/-/btoa-1.2.1.tgz",
"integrity": "sha512-SB4/MIGlsiVkMcHmT+pSmIPoNDoHg+7cMzmt3Uxt628MTz2487DKSqK/fuhFBrkuqrYv5UCEnACpF4dTFNKc/g==",
"dev": true
},
"buffer": {
"version": "5.7.1",
"resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz",
@@ -2253,9 +2223,9 @@
}
},
"follow-redirects": {
"version": "1.14.1",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.1.tgz",
"integrity": "sha512-HWqDgT7ZEkqRzBvc2s64vSZ/hfOceEol3ac/7tKwzuvEyWx3/4UegXh5oBOIotkGsObyk3xznnSRVADBgWSQVg==",
"version": "1.14.2",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.2.tgz",
"integrity": "sha512-yLR6WaE2lbF0x4K2qE2p9PEXKLDjUjnR/xmjS3wHAYxtlsI9MLLBJUZirAHKzUZDGLxje7w/cXR49WOUo4rbsA==",
"dev": true
},
"form-data": {
@@ -2493,6 +2463,12 @@
"integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=",
"dev": true
},
"js-base64": {
"version": "3.6.1",
"resolved": "https://registry.npmjs.org/js-base64/-/js-base64-3.6.1.tgz",
"integrity": "sha512-Frdq2+tRRGLQUIQOgsIGSCd1VePCS2fsddTG5dTCqR0JHgltXWfsxnY0gIXPoMeRmdom6Oyq+UMOFg5suduOjQ==",
"dev": true
},
"jsdom": {
"version": "16.6.0",
"resolved": "https://registry.npmjs.org/jsdom/-/jsdom-16.6.0.tgz",
@@ -2717,9 +2693,9 @@
"dev": true
},
"path-parse": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz",
"integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==",
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
"integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
"dev": true
},
"pify": {
@@ -2756,12 +2732,6 @@
"integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==",
"dev": true
},
"querystring": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz",
"integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=",
"dev": true
},
"readable-stream": {
"version": "3.6.0",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
@@ -2954,24 +2924,6 @@
"integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==",
"dev": true
},
"url": {
"version": "0.11.0",
"resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz",
"integrity": "sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=",
"dev": true,
"requires": {
"punycode": "1.3.2",
"querystring": "0.2.0"
},
"dependencies": {
"punycode": {
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz",
"integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=",
"dev": true
}
}
},
"util-deprecate": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",

View File

@@ -27,12 +27,12 @@
"main": "index.js",
"scripts": {
"build": "sasjs cbd -t viya",
"docs": "sasjs doc && ./sasjs/utils/build.sh",
"docs": "sasjs doc -t docsonly && ./sasjs/utils/build.sh",
"test": "sasjs test -t viya",
"lint": "sasjs lint",
"prepare": "git rev-parse --git-dir && git config core.hooksPath ./.git-hooks || true"
},
"devDependencies": {
"@sasjs/cli": "2.33.3"
"@sasjs/cli": "^2.37.2"
}
}

View File

@@ -2,11 +2,12 @@
"$schema": "https://cli.sasjs.io/sasjsconfig-schema.json",
"macroFolders": [
"base",
"fcmp",
"meta",
"metax",
"viya",
"lua",
"tests/base"
"tests/crossplatform"
],
"docConfig": {
"displayMacroCore": false,
@@ -33,7 +34,7 @@
"allowInsecureRequests": false,
"appLoc": "/Public/temp/macrocore",
"macroFolders": [
"tests/viya"
"tests/viyaonly"
],
"programFolders": [],
"deployConfig": {
@@ -48,7 +49,15 @@
"serverType": "SAS9",
"appLoc": "/Shared Data/temp/macrocore",
"macroFolders": [
"tests/meta"
"tests/sas9only"
]
},
{
"name": "docsonly",
"serverType": "SAS9",
"macroFolders": [
"tests/sas9only",
"tests/viyaonly"
]
}
]

View File

@@ -0,0 +1,39 @@
/**
@file
@brief Testing mcf_stpsrv_header macro
<h4> SAS Macros </h4>
@li mcf_stpsrv_header.sas
@li mp_assert.sas
**/
%let sasjs_stpsrv_header_loc=%sysfunc(pathname(work))/stpsrv_header.txt;
%mcf_stpsrv_header(wrap=YES, insert_cmplib=YES)
data _null_;
rc=stpsrv_header('Content-type','application/text');
rc=stpsrv_header('Content-disposition',"attachment; filename=file.txt");
run;
%let test1=FAIL;
%let test2=FAIL;
data _null_;
infile "&sasjs_stpsrv_header_loc";
input;
if _n_=1 and _infile_='Content-type: application/text'
then call symputx('test1','PASS');
else if _n_=2 & _infile_='Content-disposition: attachment; filename=file.txt'
then call symputx('test2','PASS');
run;
%mp_assert(
iftrue=(%str(&test1)=%str(PASS)),
desc=Check first header line
)
%mp_assert(
iftrue=(%str(&test2)=%str(PASS)),
desc=Check second header line
)

View File

@@ -0,0 +1,52 @@
/**
@file
@brief Testing mcf_string2file macro
<h4> SAS Macros </h4>
@li mcf_string2file.sas
@li mp_assert.sas
**/
%mcf_string2file(wrap=YES, insert_cmplib=YES)
data _null_;
rc=mcf_string2file(
"%sysfunc(pathname(work))/newfile.txt"
, "line1"
, "APPEND");
rc=mcf_string2file(
"%sysfunc(pathname(work))/newfile.txt"
, "line2"
, "APPEND");
run;
data _null_;
infile "%sysfunc(pathname(work))/newfile.txt";
input;
if _n_=2 then call symputx('val',_infile_);
run;
%mp_assert(
iftrue=(%str(&val)=%str(line2)),
desc=Check if APPEND works
)
data _null_;
rc=mcf_string2file(
"%sysfunc(pathname(work))/newfile.txt"
, "creating"
, "CREATE");
run;
data _null_;
infile "%sysfunc(pathname(work))/newfile.txt";
input;
if _n_=1 then call symputx('val2',_infile_);
run;
%mp_assert(
iftrue=(%str(&val2)=%str(creating)),
desc=Check if CREATE works
)

View File

@@ -0,0 +1,22 @@
/**
@file
@brief Testing mf_existfunction macro
<h4> SAS Macros </h4>
@li mf_existfunction.sas
@li mp_assert.sas
**/
%mp_assert(
iftrue=(%mf_existfunction(CAT)=1),
desc=Checking if CAT function exists,
outds=work.test_results
)
%mp_assert(
iftrue=(%mf_existfunction(DOG)=0),
desc=Checking DOG function does not exist,
outds=work.test_results
)