mirror of
https://github.com/sasjs/core.git
synced 2025-12-11 14:34:35 +00:00
Compare commits
13 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ffdfc57aa6 | ||
|
|
6fc8408988 | ||
|
|
eeb25fa5bc | ||
|
|
521d128afe | ||
|
|
0135dd6c8f | ||
|
|
1b66c59dc0 | ||
| 96be5c65dc | |||
| 8f6ef569e1 | |||
| ff45c5a8b8 | |||
| fb5f1c820a | |||
| c0e33175cf | |||
| 2bfa72f48f | |||
| fdc2e8ac8a |
10
.gitpod.dockerfile
Normal file
10
.gitpod.dockerfile
Normal file
@@ -0,0 +1,10 @@
|
||||
|
||||
FROM gitpod/workspace-full
|
||||
|
||||
RUN sudo apt-get update \
|
||||
&& sudo apt-get install -y \
|
||||
doxygen \
|
||||
&& npm i -g npm@latest \
|
||||
&& npm i -g @sasjs/cli \
|
||||
&& npm i \
|
||||
&& sudo rm -rf /var/lib/apt/lists/*
|
||||
8
.gitpod.yml
Normal file
8
.gitpod.yml
Normal file
@@ -0,0 +1,8 @@
|
||||
tasks:
|
||||
- init: npm i && clear
|
||||
|
||||
image:
|
||||
file: .gitpod.dockerfile
|
||||
vscode:
|
||||
extensions:
|
||||
- sasjs.sasjs-for-vscode@1.4.3:LY5VLf6H5R3u7nqVRnCQRw==
|
||||
492
all.sas
492
all.sas
@@ -4027,6 +4027,15 @@ create table &outds (rename=(
|
||||
The output will be one cards file in the `outloc` directory per dataset in the
|
||||
input `lib` library. If the `outloc` directory does not exist, it is created.
|
||||
|
||||
To create a single SAS file with the first 1000 records of each table in a
|
||||
library you could use this syntax:
|
||||
|
||||
%mp_lib2cards(lib=sashelp
|
||||
, outloc= /tmp
|
||||
, outfile= myfile.sas
|
||||
, maxobs= 1000
|
||||
)
|
||||
|
||||
<h4> SAS Macros </h4>
|
||||
@li mf_mkdir.sas
|
||||
@li mf_trimstr.sas
|
||||
@@ -8096,20 +8105,117 @@ filename __outdoc clear;
|
||||
|
||||
%mend;
|
||||
/**
|
||||
@file mm_getfoldertree.sas
|
||||
@file
|
||||
@brief Returns all direct child members of a particular folder
|
||||
@details Displays the children for a particular folder, in a similar fashion
|
||||
to the viya counterpart (mv_getfoldermembers.sas)
|
||||
|
||||
Usage:
|
||||
|
||||
%mm_getfoldermembers(root=/, outds=rootfolders)
|
||||
|
||||
%mm_getfoldermembers(root=/User Folders/&sysuserid, outds=usercontent)
|
||||
|
||||
@param [in] root= the parent folder under which to return all contents
|
||||
@param [out] outds= the dataset to create that contains the list of directories
|
||||
@param [in] mDebug= set to 1 to show debug messages in the log
|
||||
|
||||
<h4> Data Outputs </h4>
|
||||
|
||||
Example for `root=/`:
|
||||
|
||||
|metauri $17|metaname $256|metatype $32|
|
||||
|---|---|---|
|
||||
|A5XLSNXI.AA000001|Products |Folder|
|
||||
|A5XLSNXI.AA000002|Shared Data |Folder|
|
||||
|A5XLSNXI.AA000003|User Folders |Folder|
|
||||
|A5XLSNXI.AA000004|System |Folder|
|
||||
|A5XLSNXI.AA00003K|30.SASApps |Folder|
|
||||
|A5XLSNXI.AA00006A|Public|Folder|
|
||||
|
||||
<h4> SAS Macros </h4>
|
||||
@li mm_getfoldertree.sas
|
||||
@li mf_getuniquefileref.sas
|
||||
@li mf_getuniquelibref.sas
|
||||
|
||||
@version 9.4
|
||||
@author Allan Bowe
|
||||
|
||||
**/
|
||||
%macro mm_getfoldermembers(
|
||||
root=
|
||||
,outds=work.mm_getfoldertree
|
||||
)/*/STORE SOURCE*/;
|
||||
|
||||
%if "&root" = "/" %then %do;
|
||||
%local fname1 fname2 fname3;
|
||||
%let fname1=%mf_getuniquefileref();
|
||||
%let fname2=%mf_getuniquefileref();
|
||||
%let fname3=%mf_getuniquefileref();
|
||||
data _null_ ;
|
||||
file &fname1 ;
|
||||
put '<GetMetadataObjects>' ;
|
||||
put '<Reposid>$METAREPOSITORY</Reposid>' ;
|
||||
put '<Type>Tree</Type>' ;
|
||||
put '<NS>SAS</NS>' ;
|
||||
put '<Flags>388</Flags>' ;
|
||||
put '<Options>' ;
|
||||
put '<XMLSelect search="Tree[SoftwareComponents/SoftwareComponent'@;
|
||||
put '[@Name=''BIP Service'']]"/>';
|
||||
put '</Options>' ;
|
||||
put '</GetMetadataObjects>' ;
|
||||
run ;
|
||||
proc metadata in=&fname1 out=&fname2 verbose;run;
|
||||
|
||||
/* create an XML map to read the response */
|
||||
data _null_;
|
||||
file &fname3;
|
||||
put '<SXLEMAP version="1.2" name="SASFolders">';
|
||||
put '<TABLE name="SASFolders">';
|
||||
put '<TABLE-PATH syntax="XPath">//Objects/Tree</TABLE-PATH>';
|
||||
put '<COLUMN name="metauri">><LENGTH>17</LENGTH>';
|
||||
put '<PATH syntax="XPath">//Objects/Tree/@Id</PATH></COLUMN>';
|
||||
put '<COLUMN name="metaname"><LENGTH>256</LENGTH>>';
|
||||
put '<PATH syntax="XPath">//Objects/Tree/@Name</PATH></COLUMN>';
|
||||
put '</TABLE></SXLEMAP>';
|
||||
run;
|
||||
%local libref1;
|
||||
%let libref1=%mf_getuniquelibref();
|
||||
libname &libref1 xml xmlfileref=&fname2 xmlmap=&fname3;
|
||||
|
||||
data &outds;
|
||||
length metatype $32;
|
||||
retain metatype 'Folder';
|
||||
set &libref1..sasfolders;
|
||||
run;
|
||||
|
||||
%end;
|
||||
%else %do;
|
||||
%mm_getfoldertree(root=&root, outds=&outds,depth=1)
|
||||
data &outds;
|
||||
set &outds(rename=(name=metaname publictype=metatype));
|
||||
keep metaname metauri metatype;
|
||||
run;
|
||||
%end;
|
||||
|
||||
%mend;
|
||||
/**
|
||||
@file
|
||||
@brief Returns all folders / subfolder content for a particular root
|
||||
@details Shows all members and SubTrees recursively for a particular root.
|
||||
Note - for big sites, this returns a lot of data! So you may wish to reduce
|
||||
the logging to speed up the process (see example below)
|
||||
the logging to speed up the process (see example below), OR - use mm_tree.sas
|
||||
which uses proc metadata and is far more efficient.
|
||||
|
||||
Usage:
|
||||
|
||||
options ps=max nonotes nosource;
|
||||
%mm_getfoldertree(root=/My/Meta/Path, outds=iwantthisdataset)
|
||||
options notes source;
|
||||
|
||||
@param root= the parent folder under which to return all contents
|
||||
@param outds= the dataset to create that contains the list of directories
|
||||
@param mDebug= set to 1 to show debug messages in the log
|
||||
|
||||
@param [in] root= the parent folder under which to return all contents
|
||||
@param [out] outds= the dataset to create that contains the list of directories
|
||||
@param [in] mDebug= set to 1 to show debug messages in the log
|
||||
|
||||
<h4> SAS Macros </h4>
|
||||
|
||||
@@ -8157,7 +8263,7 @@ data &outds.TMP/view=&outds.TMP;
|
||||
__n1+1;
|
||||
/* Walk through all possible associations of this object. */
|
||||
__n2=1;
|
||||
if assoctype in ('Members','SubTrees') then
|
||||
if assoctype in ('Members','SubTrees') then
|
||||
do while(metadata_getnasn(pathuri,assoctype,__n2,metauri)>0);
|
||||
__n2+1;
|
||||
call missing(name,publictype,MetadataUpdated,MetadataCreated);
|
||||
@@ -10195,16 +10301,15 @@ run;
|
||||
%end;
|
||||
|
||||
%mend;/**
|
||||
@file mm_updatestpservertype.sas
|
||||
@file
|
||||
@brief Updates a type 2 stored process to run on STP or WKS context
|
||||
@details Only works on Type 2 (9.3 compatible) STPs
|
||||
|
||||
Usage:
|
||||
|
||||
%mm_updatestpservertype(target=/some/meta/path/myStoredProcess
|
||||
,type=WKS)
|
||||
%mm_updatestpservertype(target=/some/meta/path/myStoredProcess
|
||||
,type=WKS)
|
||||
|
||||
<h4> SAS Macros </h4>
|
||||
|
||||
@param target= full path to the STP being deleted
|
||||
@param type= Either WKS or STP depending on whether Workspace or Stored Process
|
||||
@@ -10269,31 +10374,40 @@ run;
|
||||
|
||||
Usage:
|
||||
|
||||
%mm_updatestpsourcecode(stp=/my/metadata/path/mystpname
|
||||
,stpcode="/file/system/source.sas")
|
||||
%mm_updatestpsourcecode(stp=/my/metadata/path/mystpname
|
||||
,stpcode="/file/system/source.sas")
|
||||
|
||||
|
||||
@param stp= the BIP Tree folder path plus Stored Process Name
|
||||
@param stpcode= the source file (or fileref) containing the SAS code to load
|
||||
@param [in] stp= the BIP Tree folder path plus Stored Process Name
|
||||
@param [in] stpcode= the source file (or fileref) containing the SAS code to load
|
||||
into the stp. For multiple files, they should simply be concatenated first.
|
||||
@param minify= set to YES in order to strip comments, blank lines, and CRLFs.
|
||||
@param [in] minify= set to YES in order to strip comments, blank lines, and CRLFs.
|
||||
|
||||
@param frefin= change default inref if it clashes with an existing one
|
||||
@param frefout= change default outref if it clashes with an existing one
|
||||
@param frefin= deprecated - a unique fileref is now always used
|
||||
@param frefout= deprecated - a unique fileref is now always used
|
||||
@param mDebug= set to 1 to show debug messages in the log
|
||||
|
||||
@version 9.3
|
||||
@author Allan Bowe
|
||||
|
||||
<h4> SAS Macros </h4>
|
||||
@li mf_getuniquefileref.sas
|
||||
|
||||
**/
|
||||
|
||||
%macro mm_updatestpsourcecode(stp=
|
||||
,stpcode=
|
||||
,minify=NO
|
||||
,mdebug=0
|
||||
/* deprecated */
|
||||
,frefin=inmeta
|
||||
,frefout=outmeta
|
||||
,mdebug=0
|
||||
);
|
||||
|
||||
%if &frefin ne inmeta or &frefout ne outmeta %then %do;
|
||||
%put %str(WARN)ING: the frefin and frefout parameters will be deprecated in
|
||||
an upcoming release.;
|
||||
%end;
|
||||
|
||||
/* first, check if STP exists */
|
||||
%local tsuri;
|
||||
%let tsuri=stopifempty ;
|
||||
@@ -10331,7 +10445,9 @@ run;
|
||||
%return;
|
||||
%end;
|
||||
|
||||
filename &frefin temp lrecl=32767;
|
||||
%local frefin frefout;
|
||||
%let frefin=%mf_getuniquefileref();
|
||||
%let frefout=%mf_getuniquefileref();
|
||||
|
||||
/* write header XML */
|
||||
data _null_;
|
||||
@@ -10344,7 +10460,7 @@ run;
|
||||
/* write contents */
|
||||
%if %length(&stpcode)>2 %then %do;
|
||||
data _null_;
|
||||
file &frefin mod;
|
||||
file &frefin lrecl=32767 mod;
|
||||
infile &stpcode lrecl=32767;
|
||||
length outstr $32767;
|
||||
input outstr ;
|
||||
@@ -10373,9 +10489,6 @@ data _null_;
|
||||
</UpdateMetadata>";
|
||||
run;
|
||||
|
||||
|
||||
filename &frefout temp;
|
||||
|
||||
proc metadata in= &frefin out=&frefout;
|
||||
run;
|
||||
|
||||
@@ -10387,6 +10500,10 @@ run;
|
||||
put _infile_;
|
||||
run;
|
||||
%end;
|
||||
%else %do;
|
||||
filename &frefin clear;
|
||||
filename &frefout clear;
|
||||
%end;
|
||||
|
||||
%mend;/**
|
||||
@file mm_webout.sas
|
||||
@@ -10829,7 +10946,314 @@ options noquotelenmax;
|
||||
libname &libref1 clear;
|
||||
%end;
|
||||
%mend;/**
|
||||
@file mv_createwebservice.sas
|
||||
@file
|
||||
@brief Creates a Viya Job
|
||||
@details
|
||||
Code is passed in as one or more filerefs.
|
||||
|
||||
%* Step 1 - compile macros ;
|
||||
filename mc url "https://raw.githubusercontent.com/sasjs/core/main/all.sas";
|
||||
%inc mc;
|
||||
|
||||
%* Step 2 - Create some SAS code and add it to a job;
|
||||
filename ft15f001 temp;
|
||||
parmcards4;
|
||||
data some_code;
|
||||
set sashelp.class;
|
||||
run;
|
||||
;;;;
|
||||
%mv_createjob(path=/Public/app/sasjstemp/jobs/myjobs,name=myjob)
|
||||
|
||||
The path to the job will then be shown in the log, eg as follows:
|
||||
|
||||

|
||||
|
||||
|
||||
<h4> SAS Macros </h4>
|
||||
@li mp_abort.sas
|
||||
@li mv_createfolder.sas
|
||||
@li mf_getuniquelibref.sas
|
||||
@li mf_getuniquefileref.sas
|
||||
@li mf_getplatform.sas
|
||||
@li mf_isblank.sas
|
||||
@li mv_deletejes.sas
|
||||
|
||||
@param path= The full path (on SAS Drive) where the job will be created
|
||||
@param name= The name of the job
|
||||
@param desc= The description of the job
|
||||
@param precode= Space separated list of filerefs, pointing to the code that
|
||||
needs to be attached to the beginning of the job
|
||||
@param code= Fileref(s) of the actual code to be added
|
||||
@param access_token_var= The global macro variable to contain the access token
|
||||
@param grant_type= valid values are "password" or "authorization_code" (unquoted).
|
||||
The default is authorization_code.
|
||||
@param replace= select NO to avoid replacing any existing job in that location
|
||||
@param contextname= Choose a specific context on which to run the Job. Leave
|
||||
blank to use the default context. From Viya 3.5 it is possible to configure
|
||||
a shared context - see
|
||||
https://go.documentation.sas.com/?docsetId=calcontexts&docsetTarget=n1hjn8eobk5pyhn1wg3ja0drdl6h.htm&docsetVersion=3.5&locale=en
|
||||
|
||||
@version VIYA V.03.04
|
||||
@author [Allan Bowe](https://www.linkedin.com/in/allanbowe)
|
||||
|
||||
**/
|
||||
|
||||
%macro mv_createjob(path=
|
||||
,name=
|
||||
,desc=Created by the mv_createjob.sas macro
|
||||
,precode=
|
||||
,code=ft15f001
|
||||
,access_token_var=ACCESS_TOKEN
|
||||
,grant_type=sas_services
|
||||
,replace=YES
|
||||
,debug=0
|
||||
,contextname=
|
||||
);
|
||||
%local oauth_bearer;
|
||||
%if &grant_type=detect %then %do;
|
||||
%if %symexist(&access_token_var) %then %let grant_type=authorization_code;
|
||||
%else %let grant_type=sas_services;
|
||||
%end;
|
||||
%if &grant_type=sas_services %then %do;
|
||||
%let oauth_bearer=oauth_bearer=sas_services;
|
||||
%let &access_token_var=;
|
||||
%end;
|
||||
%put &sysmacroname: grant_type=&grant_type;
|
||||
|
||||
/* initial validation checking */
|
||||
%mp_abort(iftrue=(&grant_type ne authorization_code and &grant_type ne password
|
||||
and &grant_type ne sas_services
|
||||
)
|
||||
,mac=&sysmacroname
|
||||
,msg=%str(Invalid value for grant_type: &grant_type)
|
||||
)
|
||||
%mp_abort(iftrue=(%mf_isblank(&path)=1)
|
||||
,mac=&sysmacroname
|
||||
,msg=%str(path value must be provided)
|
||||
)
|
||||
%mp_abort(iftrue=(%length(&path)=1)
|
||||
,mac=&sysmacroname
|
||||
,msg=%str(path value must be provided)
|
||||
)
|
||||
%mp_abort(iftrue=(%mf_isblank(&name)=1)
|
||||
,mac=&sysmacroname
|
||||
,msg=%str(name value must be provided)
|
||||
)
|
||||
|
||||
options noquotelenmax;
|
||||
|
||||
* remove any trailing slash ;
|
||||
%if "%substr(&path,%length(&path),1)" = "/" %then
|
||||
%let path=%substr(&path,1,%length(&path)-1);
|
||||
|
||||
/* ensure folder exists */
|
||||
%put &sysmacroname: Path &path being checked / created;
|
||||
%mv_createfolder(path=&path)
|
||||
|
||||
%local base_uri; /* location of rest apis */
|
||||
%let base_uri=%mf_getplatform(VIYARESTAPI);
|
||||
|
||||
/* fetching folder details for provided path */
|
||||
%local fname1;
|
||||
%let fname1=%mf_getuniquefileref();
|
||||
proc http method='GET' out=&fname1 &oauth_bearer
|
||||
url="&base_uri/folders/folders/@item?path=&path";
|
||||
%if &grant_type=authorization_code %then %do;
|
||||
headers "Authorization"="Bearer &&&access_token_var";
|
||||
%end;
|
||||
run;
|
||||
%if &debug %then %do;
|
||||
data _null_;
|
||||
infile &fname1;
|
||||
input;
|
||||
putlog _infile_;
|
||||
run;
|
||||
%end;
|
||||
%mp_abort(iftrue=(&SYS_PROCHTTP_STATUS_CODE ne 200)
|
||||
,mac=&sysmacroname
|
||||
,msg=%str(&SYS_PROCHTTP_STATUS_CODE &SYS_PROCHTTP_STATUS_PHRASE)
|
||||
)
|
||||
|
||||
/* path exists. Grab follow on link to check members */
|
||||
%local libref1;
|
||||
%let libref1=%mf_getuniquelibref();
|
||||
libname &libref1 JSON fileref=&fname1;
|
||||
|
||||
data _null_;
|
||||
set &libref1..links;
|
||||
if rel='members' then call symputx('membercheck',quote("&base_uri"!!trim(href)),'l');
|
||||
else if rel='self' then call symputx('parentFolderUri',href,'l');
|
||||
run;
|
||||
data _null_;
|
||||
set &libref1..root;
|
||||
call symputx('folderid',id,'l');
|
||||
run;
|
||||
%local fname2;
|
||||
%let fname2=%mf_getuniquefileref();
|
||||
proc http method='GET'
|
||||
out=&fname2
|
||||
&oauth_bearer
|
||||
url=%unquote(%superq(membercheck));
|
||||
headers
|
||||
%if &grant_type=authorization_code %then %do;
|
||||
"Authorization"="Bearer &&&access_token_var"
|
||||
%end;
|
||||
'Accept'='application/vnd.sas.collection+json'
|
||||
'Accept-Language'='string';
|
||||
%if &debug=1 %then %do;
|
||||
debug level = 3;
|
||||
%end;
|
||||
run;
|
||||
/*data _null_;infile &fname2;input;putlog _infile_;run;*/
|
||||
%mp_abort(iftrue=(&SYS_PROCHTTP_STATUS_CODE ne 200)
|
||||
,mac=&sysmacroname
|
||||
,msg=%str(&SYS_PROCHTTP_STATUS_CODE &SYS_PROCHTTP_STATUS_PHRASE)
|
||||
)
|
||||
|
||||
%if %upcase(&replace)=YES %then %do;
|
||||
%mv_deletejes(path=&path, name=&name)
|
||||
%end;
|
||||
%else %do;
|
||||
/* check that job does not already exist in that folder */
|
||||
%local libref2;
|
||||
%let libref2=%mf_getuniquelibref();
|
||||
libname &libref2 JSON fileref=&fname2;
|
||||
%local exists; %let exists=0;
|
||||
data _null_;
|
||||
set &libref2..items;
|
||||
if contenttype='jobDefinition' and upcase(name)="%upcase(&name)" then
|
||||
call symputx('exists',1,'l');
|
||||
run;
|
||||
%mp_abort(iftrue=(&exists=1)
|
||||
,mac=&sysmacroname
|
||||
,msg=%str(Job &name already exists in &path)
|
||||
)
|
||||
libname &libref2 clear;
|
||||
%end;
|
||||
|
||||
/* set up the body of the request to create the service */
|
||||
%local fname3;
|
||||
%let fname3=%mf_getuniquefileref();
|
||||
data _null_;
|
||||
file &fname3 TERMSTR=' ';
|
||||
length string $32767;
|
||||
string=cats('{"version": 0,"name":"'
|
||||
,"&name"
|
||||
,'","type":"Compute","parameters":[{"name":"_addjesbeginendmacros"'
|
||||
,',"type":"CHARACTER","defaultValue":"false"}');
|
||||
context=quote(cats(symget('contextname')));
|
||||
if context ne '""' then do;
|
||||
string=cats(string,',{"version": 1,"name": "_contextName","defaultValue":'
|
||||
,context,',"type":"CHARACTER","label":"Context Name","required": false}');
|
||||
end;
|
||||
string=cats(string,'],"code":"');
|
||||
put string;
|
||||
run;
|
||||
|
||||
|
||||
/* insert the code, escaping double quotes and carriage returns */
|
||||
%local x fref freflist;
|
||||
%let freflist= &precode &code ;
|
||||
%do x=1 %to %sysfunc(countw(&freflist));
|
||||
%let fref=%scan(&freflist,&x);
|
||||
%put &sysmacroname: adding &fref;
|
||||
data _null_;
|
||||
length filein 8 fileid 8;
|
||||
filein = fopen("&fref","I",1,"B");
|
||||
fileid = fopen("&fname3","A",1,"B");
|
||||
rec = "20"x;
|
||||
do while(fread(filein)=0);
|
||||
rc = fget(filein,rec,1);
|
||||
if rec='"' then do; /* DOUBLE QUOTE */
|
||||
rc =fput(fileid,'\');rc =fwrite(fileid);
|
||||
rc =fput(fileid,'"');rc =fwrite(fileid);
|
||||
end;
|
||||
else if rec='0A'x then do; /* LF */
|
||||
rc =fput(fileid,'\');rc =fwrite(fileid);
|
||||
rc =fput(fileid,'n');rc =fwrite(fileid);
|
||||
end;
|
||||
else if rec='0D'x then do; /* CR */
|
||||
rc =fput(fileid,'\');rc =fwrite(fileid);
|
||||
rc =fput(fileid,'r');rc =fwrite(fileid);
|
||||
end;
|
||||
else if rec='09'x then do; /* TAB */
|
||||
rc =fput(fileid,'\');rc =fwrite(fileid);
|
||||
rc =fput(fileid,'t');rc =fwrite(fileid);
|
||||
end;
|
||||
else if rec='5C'x then do; /* BACKSLASH */
|
||||
rc =fput(fileid,'\');rc =fwrite(fileid);
|
||||
rc =fput(fileid,'\');rc =fwrite(fileid);
|
||||
end;
|
||||
else do;
|
||||
rc =fput(fileid,rec);
|
||||
rc =fwrite(fileid);
|
||||
end;
|
||||
end;
|
||||
rc=fclose(filein);
|
||||
rc=fclose(fileid);
|
||||
run;
|
||||
%end;
|
||||
|
||||
/* finish off the body of the code file loaded to JES */
|
||||
data _null_;
|
||||
file &fname3 mod TERMSTR=' ';
|
||||
put '"}';
|
||||
run;
|
||||
|
||||
/* now we can create the job!! */
|
||||
%local fname4;
|
||||
%let fname4=%mf_getuniquefileref();
|
||||
proc http method='POST'
|
||||
in=&fname3
|
||||
out=&fname4
|
||||
&oauth_bearer
|
||||
url="&base_uri/jobDefinitions/definitions?parentFolderUri=&parentFolderUri";
|
||||
headers 'Content-Type'='application/vnd.sas.job.definition+json'
|
||||
%if &grant_type=authorization_code %then %do;
|
||||
"Authorization"="Bearer &&&access_token_var"
|
||||
%end;
|
||||
"Accept"="application/vnd.sas.job.definition+json";
|
||||
%if &debug=1 %then %do;
|
||||
debug level = 3;
|
||||
%end;
|
||||
run;
|
||||
/*data _null_;infile &fname4;input;putlog _infile_;run;*/
|
||||
%mp_abort(iftrue=(&SYS_PROCHTTP_STATUS_CODE ne 201)
|
||||
,mac=&sysmacroname
|
||||
,msg=%str(&SYS_PROCHTTP_STATUS_CODE &SYS_PROCHTTP_STATUS_PHRASE)
|
||||
)
|
||||
/* clear refs */
|
||||
filename &fname1 clear;
|
||||
filename &fname2 clear;
|
||||
filename &fname3 clear;
|
||||
filename &fname4 clear;
|
||||
libname &libref1 clear;
|
||||
|
||||
/* get the url so we can give a helpful log message */
|
||||
%local url;
|
||||
data _null_;
|
||||
if symexist('_baseurl') then do;
|
||||
url=symget('_baseurl');
|
||||
if subpad(url,length(url)-9,9)='SASStudio'
|
||||
then url=substr(url,1,length(url)-11);
|
||||
else url="&systcpiphostname";
|
||||
end;
|
||||
else url="&systcpiphostname";
|
||||
call symputx('url',url);
|
||||
run;
|
||||
|
||||
|
||||
%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;
|
||||
/**
|
||||
@file
|
||||
@brief Creates a JobExecution web service if it doesn't already exist
|
||||
@details
|
||||
Code is passed in as one or more filerefs.
|
||||
@@ -11396,23 +11820,23 @@ run;
|
||||
rec = "20"x;
|
||||
do while(fread(filein)=0);
|
||||
rc = fget(filein,rec,1);
|
||||
if rec='"' then do;
|
||||
if rec='"' then do; /* DOUBLE QUOTE */
|
||||
rc =fput(fileid,'\');rc =fwrite(fileid);
|
||||
rc =fput(fileid,'"');rc =fwrite(fileid);
|
||||
end;
|
||||
else if rec='0A'x then do;
|
||||
rc =fput(fileid,'\');rc =fwrite(fileid);
|
||||
rc =fput(fileid,'r');rc =fwrite(fileid);
|
||||
end;
|
||||
else if rec='0D'x then do;
|
||||
else if rec='0A'x then do; /* LF */
|
||||
rc =fput(fileid,'\');rc =fwrite(fileid);
|
||||
rc =fput(fileid,'n');rc =fwrite(fileid);
|
||||
end;
|
||||
else if rec='09'x then do;
|
||||
else if rec='0D'x then do; /* CR */
|
||||
rc =fput(fileid,'\');rc =fwrite(fileid);
|
||||
rc =fput(fileid,'r');rc =fwrite(fileid);
|
||||
end;
|
||||
else if rec='09'x then do; /* TAB */
|
||||
rc =fput(fileid,'\');rc =fwrite(fileid);
|
||||
rc =fput(fileid,'t');rc =fwrite(fileid);
|
||||
end;
|
||||
else if rec='5C'x then do;
|
||||
else if rec='5C'x then do; /* BACKSLASH */
|
||||
rc =fput(fileid,'\');rc =fwrite(fileid);
|
||||
rc =fput(fileid,'\');rc =fwrite(fileid);
|
||||
end;
|
||||
|
||||
@@ -10,6 +10,15 @@
|
||||
The output will be one cards file in the `outloc` directory per dataset in the
|
||||
input `lib` library. If the `outloc` directory does not exist, it is created.
|
||||
|
||||
To create a single SAS file with the first 1000 records of each table in a
|
||||
library you could use this syntax:
|
||||
|
||||
%mp_lib2cards(lib=sashelp
|
||||
, outloc= /tmp
|
||||
, outfile= myfile.sas
|
||||
, maxobs= 1000
|
||||
)
|
||||
|
||||
<h4> SAS Macros </h4>
|
||||
@li mf_mkdir.sas
|
||||
@li mf_trimstr.sas
|
||||
|
||||
95
meta/mm_getfoldermembers.sas
Normal file
95
meta/mm_getfoldermembers.sas
Normal file
@@ -0,0 +1,95 @@
|
||||
/**
|
||||
@file
|
||||
@brief Returns all direct child members of a particular folder
|
||||
@details Displays the children for a particular folder, in a similar fashion
|
||||
to the viya counterpart (mv_getfoldermembers.sas)
|
||||
|
||||
Usage:
|
||||
|
||||
%mm_getfoldermembers(root=/, outds=rootfolders)
|
||||
|
||||
%mm_getfoldermembers(root=/User Folders/&sysuserid, outds=usercontent)
|
||||
|
||||
@param [in] root= the parent folder under which to return all contents
|
||||
@param [out] outds= the dataset to create that contains the list of directories
|
||||
@param [in] mDebug= set to 1 to show debug messages in the log
|
||||
|
||||
<h4> Data Outputs </h4>
|
||||
|
||||
Example for `root=/`:
|
||||
|
||||
|metauri $17|metaname $256|metatype $32|
|
||||
|---|---|---|
|
||||
|A5XLSNXI.AA000001|Products |Folder|
|
||||
|A5XLSNXI.AA000002|Shared Data |Folder|
|
||||
|A5XLSNXI.AA000003|User Folders |Folder|
|
||||
|A5XLSNXI.AA000004|System |Folder|
|
||||
|A5XLSNXI.AA00003K|30.SASApps |Folder|
|
||||
|A5XLSNXI.AA00006A|Public|Folder|
|
||||
|
||||
<h4> SAS Macros </h4>
|
||||
@li mm_getfoldertree.sas
|
||||
@li mf_getuniquefileref.sas
|
||||
@li mf_getuniquelibref.sas
|
||||
|
||||
@version 9.4
|
||||
@author Allan Bowe
|
||||
|
||||
**/
|
||||
%macro mm_getfoldermembers(
|
||||
root=
|
||||
,outds=work.mm_getfoldertree
|
||||
)/*/STORE SOURCE*/;
|
||||
|
||||
%if "&root" = "/" %then %do;
|
||||
%local fname1 fname2 fname3;
|
||||
%let fname1=%mf_getuniquefileref();
|
||||
%let fname2=%mf_getuniquefileref();
|
||||
%let fname3=%mf_getuniquefileref();
|
||||
data _null_ ;
|
||||
file &fname1 ;
|
||||
put '<GetMetadataObjects>' ;
|
||||
put '<Reposid>$METAREPOSITORY</Reposid>' ;
|
||||
put '<Type>Tree</Type>' ;
|
||||
put '<NS>SAS</NS>' ;
|
||||
put '<Flags>388</Flags>' ;
|
||||
put '<Options>' ;
|
||||
put '<XMLSelect search="Tree[SoftwareComponents/SoftwareComponent'@;
|
||||
put '[@Name=''BIP Service'']]"/>';
|
||||
put '</Options>' ;
|
||||
put '</GetMetadataObjects>' ;
|
||||
run ;
|
||||
proc metadata in=&fname1 out=&fname2 verbose;run;
|
||||
|
||||
/* create an XML map to read the response */
|
||||
data _null_;
|
||||
file &fname3;
|
||||
put '<SXLEMAP version="1.2" name="SASFolders">';
|
||||
put '<TABLE name="SASFolders">';
|
||||
put '<TABLE-PATH syntax="XPath">//Objects/Tree</TABLE-PATH>';
|
||||
put '<COLUMN name="metauri">><LENGTH>17</LENGTH>';
|
||||
put '<PATH syntax="XPath">//Objects/Tree/@Id</PATH></COLUMN>';
|
||||
put '<COLUMN name="metaname"><LENGTH>256</LENGTH>>';
|
||||
put '<PATH syntax="XPath">//Objects/Tree/@Name</PATH></COLUMN>';
|
||||
put '</TABLE></SXLEMAP>';
|
||||
run;
|
||||
%local libref1;
|
||||
%let libref1=%mf_getuniquelibref();
|
||||
libname &libref1 xml xmlfileref=&fname2 xmlmap=&fname3;
|
||||
|
||||
data &outds;
|
||||
length metatype $32;
|
||||
retain metatype 'Folder';
|
||||
set &libref1..sasfolders;
|
||||
run;
|
||||
|
||||
%end;
|
||||
%else %do;
|
||||
%mm_getfoldertree(root=&root, outds=&outds,depth=1)
|
||||
data &outds;
|
||||
set &outds(rename=(name=metaname publictype=metatype));
|
||||
keep metaname metauri metatype;
|
||||
run;
|
||||
%end;
|
||||
|
||||
%mend;
|
||||
@@ -1,18 +1,20 @@
|
||||
/**
|
||||
@file mm_getfoldertree.sas
|
||||
@file
|
||||
@brief Returns all folders / subfolder content for a particular root
|
||||
@details Shows all members and SubTrees recursively for a particular root.
|
||||
Note - for big sites, this returns a lot of data! So you may wish to reduce
|
||||
the logging to speed up the process (see example below)
|
||||
the logging to speed up the process (see example below), OR - use mm_tree.sas
|
||||
which uses proc metadata and is far more efficient.
|
||||
|
||||
Usage:
|
||||
|
||||
options ps=max nonotes nosource;
|
||||
%mm_getfoldertree(root=/My/Meta/Path, outds=iwantthisdataset)
|
||||
options notes source;
|
||||
|
||||
@param root= the parent folder under which to return all contents
|
||||
@param outds= the dataset to create that contains the list of directories
|
||||
@param mDebug= set to 1 to show debug messages in the log
|
||||
|
||||
@param [in] root= the parent folder under which to return all contents
|
||||
@param [out] outds= the dataset to create that contains the list of directories
|
||||
@param [in] mDebug= set to 1 to show debug messages in the log
|
||||
|
||||
<h4> SAS Macros </h4>
|
||||
|
||||
@@ -60,7 +62,7 @@ data &outds.TMP/view=&outds.TMP;
|
||||
__n1+1;
|
||||
/* Walk through all possible associations of this object. */
|
||||
__n2=1;
|
||||
if assoctype in ('Members','SubTrees') then
|
||||
if assoctype in ('Members','SubTrees') then
|
||||
do while(metadata_getnasn(pathuri,assoctype,__n2,metauri)>0);
|
||||
__n2+1;
|
||||
call missing(name,publictype,MetadataUpdated,MetadataCreated);
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
/**
|
||||
@file mm_updatestpservertype.sas
|
||||
@file
|
||||
@brief Updates a type 2 stored process to run on STP or WKS context
|
||||
@details Only works on Type 2 (9.3 compatible) STPs
|
||||
|
||||
Usage:
|
||||
|
||||
%mm_updatestpservertype(target=/some/meta/path/myStoredProcess
|
||||
,type=WKS)
|
||||
%mm_updatestpservertype(target=/some/meta/path/myStoredProcess
|
||||
,type=WKS)
|
||||
|
||||
<h4> SAS Macros </h4>
|
||||
|
||||
@param target= full path to the STP being deleted
|
||||
@param type= Either WKS or STP depending on whether Workspace or Stored Process
|
||||
|
||||
@@ -6,31 +6,40 @@
|
||||
|
||||
Usage:
|
||||
|
||||
%mm_updatestpsourcecode(stp=/my/metadata/path/mystpname
|
||||
,stpcode="/file/system/source.sas")
|
||||
%mm_updatestpsourcecode(stp=/my/metadata/path/mystpname
|
||||
,stpcode="/file/system/source.sas")
|
||||
|
||||
|
||||
@param stp= the BIP Tree folder path plus Stored Process Name
|
||||
@param stpcode= the source file (or fileref) containing the SAS code to load
|
||||
@param [in] stp= the BIP Tree folder path plus Stored Process Name
|
||||
@param [in] stpcode= the source file (or fileref) containing the SAS code to load
|
||||
into the stp. For multiple files, they should simply be concatenated first.
|
||||
@param minify= set to YES in order to strip comments, blank lines, and CRLFs.
|
||||
@param [in] minify= set to YES in order to strip comments, blank lines, and CRLFs.
|
||||
|
||||
@param frefin= change default inref if it clashes with an existing one
|
||||
@param frefout= change default outref if it clashes with an existing one
|
||||
@param frefin= deprecated - a unique fileref is now always used
|
||||
@param frefout= deprecated - a unique fileref is now always used
|
||||
@param mDebug= set to 1 to show debug messages in the log
|
||||
|
||||
@version 9.3
|
||||
@author Allan Bowe
|
||||
|
||||
<h4> SAS Macros </h4>
|
||||
@li mf_getuniquefileref.sas
|
||||
|
||||
**/
|
||||
|
||||
%macro mm_updatestpsourcecode(stp=
|
||||
,stpcode=
|
||||
,minify=NO
|
||||
,mdebug=0
|
||||
/* deprecated */
|
||||
,frefin=inmeta
|
||||
,frefout=outmeta
|
||||
,mdebug=0
|
||||
);
|
||||
|
||||
%if &frefin ne inmeta or &frefout ne outmeta %then %do;
|
||||
%put %str(WARN)ING: the frefin and frefout parameters will be deprecated in
|
||||
an upcoming release.;
|
||||
%end;
|
||||
|
||||
/* first, check if STP exists */
|
||||
%local tsuri;
|
||||
%let tsuri=stopifempty ;
|
||||
@@ -68,7 +77,9 @@ run;
|
||||
%return;
|
||||
%end;
|
||||
|
||||
filename &frefin temp lrecl=32767;
|
||||
%local frefin frefout;
|
||||
%let frefin=%mf_getuniquefileref();
|
||||
%let frefout=%mf_getuniquefileref();
|
||||
|
||||
/* write header XML */
|
||||
data _null_;
|
||||
@@ -81,7 +92,7 @@ run;
|
||||
/* write contents */
|
||||
%if %length(&stpcode)>2 %then %do;
|
||||
data _null_;
|
||||
file &frefin mod;
|
||||
file &frefin lrecl=32767 mod;
|
||||
infile &stpcode lrecl=32767;
|
||||
length outstr $32767;
|
||||
input outstr ;
|
||||
@@ -110,9 +121,6 @@ data _null_;
|
||||
</UpdateMetadata>";
|
||||
run;
|
||||
|
||||
|
||||
filename &frefout temp;
|
||||
|
||||
proc metadata in= &frefin out=&frefout;
|
||||
run;
|
||||
|
||||
@@ -124,5 +132,9 @@ run;
|
||||
put _infile_;
|
||||
run;
|
||||
%end;
|
||||
%else %do;
|
||||
filename &frefin clear;
|
||||
filename &frefout clear;
|
||||
%end;
|
||||
|
||||
%mend;
|
||||
3408
package-lock.json
generated
3408
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -28,9 +28,6 @@
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1",
|
||||
"docs": "./sasjs/utils/build.sh"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@sasjs/cli": "^2.4.0"
|
||||
"docs": "sasjs doc && ./sasjs/utils/build.sh"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8" />
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=9" />
|
||||
<meta property="og:type" content="website">
|
||||
<meta name="author" content="Allan Bowe">
|
||||
<meta name="generator" content="Doxygen $doxygenversion" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<!--BEGIN PROJECT_NAME-->
|
||||
|
||||
@@ -1,12 +1,9 @@
|
||||
#!/bin/bash
|
||||
####################################################################
|
||||
# PROJECT: Macro Core Docs Build #
|
||||
# To execute, use the npm command (npm run docs) #
|
||||
####################################################################
|
||||
|
||||
cd ../..
|
||||
|
||||
sasjs doc
|
||||
|
||||
# refresh github pages site
|
||||
rm -rf sasjsbuild/docsite
|
||||
git clone git@github.com:sasjs/core.github.io.git sasjsbuild/docsite
|
||||
|
||||
307
viya/mv_createjob.sas
Normal file
307
viya/mv_createjob.sas
Normal file
@@ -0,0 +1,307 @@
|
||||
/**
|
||||
@file
|
||||
@brief Creates a Viya Job
|
||||
@details
|
||||
Code is passed in as one or more filerefs.
|
||||
|
||||
%* Step 1 - compile macros ;
|
||||
filename mc url "https://raw.githubusercontent.com/sasjs/core/main/all.sas";
|
||||
%inc mc;
|
||||
|
||||
%* Step 2 - Create some SAS code and add it to a job;
|
||||
filename ft15f001 temp;
|
||||
parmcards4;
|
||||
data some_code;
|
||||
set sashelp.class;
|
||||
run;
|
||||
;;;;
|
||||
%mv_createjob(path=/Public/app/sasjstemp/jobs/myjobs,name=myjob)
|
||||
|
||||
The path to the job will then be shown in the log, eg as follows:
|
||||
|
||||

|
||||
|
||||
|
||||
<h4> SAS Macros </h4>
|
||||
@li mp_abort.sas
|
||||
@li mv_createfolder.sas
|
||||
@li mf_getuniquelibref.sas
|
||||
@li mf_getuniquefileref.sas
|
||||
@li mf_getplatform.sas
|
||||
@li mf_isblank.sas
|
||||
@li mv_deletejes.sas
|
||||
|
||||
@param path= The full path (on SAS Drive) where the job will be created
|
||||
@param name= The name of the job
|
||||
@param desc= The description of the job
|
||||
@param precode= Space separated list of filerefs, pointing to the code that
|
||||
needs to be attached to the beginning of the job
|
||||
@param code= Fileref(s) of the actual code to be added
|
||||
@param access_token_var= The global macro variable to contain the access token
|
||||
@param grant_type= valid values are "password" or "authorization_code" (unquoted).
|
||||
The default is authorization_code.
|
||||
@param replace= select NO to avoid replacing any existing job in that location
|
||||
@param contextname= Choose a specific context on which to run the Job. Leave
|
||||
blank to use the default context. From Viya 3.5 it is possible to configure
|
||||
a shared context - see
|
||||
https://go.documentation.sas.com/?docsetId=calcontexts&docsetTarget=n1hjn8eobk5pyhn1wg3ja0drdl6h.htm&docsetVersion=3.5&locale=en
|
||||
|
||||
@version VIYA V.03.04
|
||||
@author [Allan Bowe](https://www.linkedin.com/in/allanbowe)
|
||||
|
||||
**/
|
||||
|
||||
%macro mv_createjob(path=
|
||||
,name=
|
||||
,desc=Created by the mv_createjob.sas macro
|
||||
,precode=
|
||||
,code=ft15f001
|
||||
,access_token_var=ACCESS_TOKEN
|
||||
,grant_type=sas_services
|
||||
,replace=YES
|
||||
,debug=0
|
||||
,contextname=
|
||||
);
|
||||
%local oauth_bearer;
|
||||
%if &grant_type=detect %then %do;
|
||||
%if %symexist(&access_token_var) %then %let grant_type=authorization_code;
|
||||
%else %let grant_type=sas_services;
|
||||
%end;
|
||||
%if &grant_type=sas_services %then %do;
|
||||
%let oauth_bearer=oauth_bearer=sas_services;
|
||||
%let &access_token_var=;
|
||||
%end;
|
||||
%put &sysmacroname: grant_type=&grant_type;
|
||||
|
||||
/* initial validation checking */
|
||||
%mp_abort(iftrue=(&grant_type ne authorization_code and &grant_type ne password
|
||||
and &grant_type ne sas_services
|
||||
)
|
||||
,mac=&sysmacroname
|
||||
,msg=%str(Invalid value for grant_type: &grant_type)
|
||||
)
|
||||
%mp_abort(iftrue=(%mf_isblank(&path)=1)
|
||||
,mac=&sysmacroname
|
||||
,msg=%str(path value must be provided)
|
||||
)
|
||||
%mp_abort(iftrue=(%length(&path)=1)
|
||||
,mac=&sysmacroname
|
||||
,msg=%str(path value must be provided)
|
||||
)
|
||||
%mp_abort(iftrue=(%mf_isblank(&name)=1)
|
||||
,mac=&sysmacroname
|
||||
,msg=%str(name value must be provided)
|
||||
)
|
||||
|
||||
options noquotelenmax;
|
||||
|
||||
* remove any trailing slash ;
|
||||
%if "%substr(&path,%length(&path),1)" = "/" %then
|
||||
%let path=%substr(&path,1,%length(&path)-1);
|
||||
|
||||
/* ensure folder exists */
|
||||
%put &sysmacroname: Path &path being checked / created;
|
||||
%mv_createfolder(path=&path)
|
||||
|
||||
%local base_uri; /* location of rest apis */
|
||||
%let base_uri=%mf_getplatform(VIYARESTAPI);
|
||||
|
||||
/* fetching folder details for provided path */
|
||||
%local fname1;
|
||||
%let fname1=%mf_getuniquefileref();
|
||||
proc http method='GET' out=&fname1 &oauth_bearer
|
||||
url="&base_uri/folders/folders/@item?path=&path";
|
||||
%if &grant_type=authorization_code %then %do;
|
||||
headers "Authorization"="Bearer &&&access_token_var";
|
||||
%end;
|
||||
run;
|
||||
%if &debug %then %do;
|
||||
data _null_;
|
||||
infile &fname1;
|
||||
input;
|
||||
putlog _infile_;
|
||||
run;
|
||||
%end;
|
||||
%mp_abort(iftrue=(&SYS_PROCHTTP_STATUS_CODE ne 200)
|
||||
,mac=&sysmacroname
|
||||
,msg=%str(&SYS_PROCHTTP_STATUS_CODE &SYS_PROCHTTP_STATUS_PHRASE)
|
||||
)
|
||||
|
||||
/* path exists. Grab follow on link to check members */
|
||||
%local libref1;
|
||||
%let libref1=%mf_getuniquelibref();
|
||||
libname &libref1 JSON fileref=&fname1;
|
||||
|
||||
data _null_;
|
||||
set &libref1..links;
|
||||
if rel='members' then call symputx('membercheck',quote("&base_uri"!!trim(href)),'l');
|
||||
else if rel='self' then call symputx('parentFolderUri',href,'l');
|
||||
run;
|
||||
data _null_;
|
||||
set &libref1..root;
|
||||
call symputx('folderid',id,'l');
|
||||
run;
|
||||
%local fname2;
|
||||
%let fname2=%mf_getuniquefileref();
|
||||
proc http method='GET'
|
||||
out=&fname2
|
||||
&oauth_bearer
|
||||
url=%unquote(%superq(membercheck));
|
||||
headers
|
||||
%if &grant_type=authorization_code %then %do;
|
||||
"Authorization"="Bearer &&&access_token_var"
|
||||
%end;
|
||||
'Accept'='application/vnd.sas.collection+json'
|
||||
'Accept-Language'='string';
|
||||
%if &debug=1 %then %do;
|
||||
debug level = 3;
|
||||
%end;
|
||||
run;
|
||||
/*data _null_;infile &fname2;input;putlog _infile_;run;*/
|
||||
%mp_abort(iftrue=(&SYS_PROCHTTP_STATUS_CODE ne 200)
|
||||
,mac=&sysmacroname
|
||||
,msg=%str(&SYS_PROCHTTP_STATUS_CODE &SYS_PROCHTTP_STATUS_PHRASE)
|
||||
)
|
||||
|
||||
%if %upcase(&replace)=YES %then %do;
|
||||
%mv_deletejes(path=&path, name=&name)
|
||||
%end;
|
||||
%else %do;
|
||||
/* check that job does not already exist in that folder */
|
||||
%local libref2;
|
||||
%let libref2=%mf_getuniquelibref();
|
||||
libname &libref2 JSON fileref=&fname2;
|
||||
%local exists; %let exists=0;
|
||||
data _null_;
|
||||
set &libref2..items;
|
||||
if contenttype='jobDefinition' and upcase(name)="%upcase(&name)" then
|
||||
call symputx('exists',1,'l');
|
||||
run;
|
||||
%mp_abort(iftrue=(&exists=1)
|
||||
,mac=&sysmacroname
|
||||
,msg=%str(Job &name already exists in &path)
|
||||
)
|
||||
libname &libref2 clear;
|
||||
%end;
|
||||
|
||||
/* set up the body of the request to create the service */
|
||||
%local fname3;
|
||||
%let fname3=%mf_getuniquefileref();
|
||||
data _null_;
|
||||
file &fname3 TERMSTR=' ';
|
||||
length string $32767;
|
||||
string=cats('{"version": 0,"name":"'
|
||||
,"&name"
|
||||
,'","type":"Compute","parameters":[{"name":"_addjesbeginendmacros"'
|
||||
,',"type":"CHARACTER","defaultValue":"false"}');
|
||||
context=quote(cats(symget('contextname')));
|
||||
if context ne '""' then do;
|
||||
string=cats(string,',{"version": 1,"name": "_contextName","defaultValue":'
|
||||
,context,',"type":"CHARACTER","label":"Context Name","required": false}');
|
||||
end;
|
||||
string=cats(string,'],"code":"');
|
||||
put string;
|
||||
run;
|
||||
|
||||
|
||||
/* insert the code, escaping double quotes and carriage returns */
|
||||
%local x fref freflist;
|
||||
%let freflist= &precode &code ;
|
||||
%do x=1 %to %sysfunc(countw(&freflist));
|
||||
%let fref=%scan(&freflist,&x);
|
||||
%put &sysmacroname: adding &fref;
|
||||
data _null_;
|
||||
length filein 8 fileid 8;
|
||||
filein = fopen("&fref","I",1,"B");
|
||||
fileid = fopen("&fname3","A",1,"B");
|
||||
rec = "20"x;
|
||||
do while(fread(filein)=0);
|
||||
rc = fget(filein,rec,1);
|
||||
if rec='"' then do; /* DOUBLE QUOTE */
|
||||
rc =fput(fileid,'\');rc =fwrite(fileid);
|
||||
rc =fput(fileid,'"');rc =fwrite(fileid);
|
||||
end;
|
||||
else if rec='0A'x then do; /* LF */
|
||||
rc =fput(fileid,'\');rc =fwrite(fileid);
|
||||
rc =fput(fileid,'n');rc =fwrite(fileid);
|
||||
end;
|
||||
else if rec='0D'x then do; /* CR */
|
||||
rc =fput(fileid,'\');rc =fwrite(fileid);
|
||||
rc =fput(fileid,'r');rc =fwrite(fileid);
|
||||
end;
|
||||
else if rec='09'x then do; /* TAB */
|
||||
rc =fput(fileid,'\');rc =fwrite(fileid);
|
||||
rc =fput(fileid,'t');rc =fwrite(fileid);
|
||||
end;
|
||||
else if rec='5C'x then do; /* BACKSLASH */
|
||||
rc =fput(fileid,'\');rc =fwrite(fileid);
|
||||
rc =fput(fileid,'\');rc =fwrite(fileid);
|
||||
end;
|
||||
else do;
|
||||
rc =fput(fileid,rec);
|
||||
rc =fwrite(fileid);
|
||||
end;
|
||||
end;
|
||||
rc=fclose(filein);
|
||||
rc=fclose(fileid);
|
||||
run;
|
||||
%end;
|
||||
|
||||
/* finish off the body of the code file loaded to JES */
|
||||
data _null_;
|
||||
file &fname3 mod TERMSTR=' ';
|
||||
put '"}';
|
||||
run;
|
||||
|
||||
/* now we can create the job!! */
|
||||
%local fname4;
|
||||
%let fname4=%mf_getuniquefileref();
|
||||
proc http method='POST'
|
||||
in=&fname3
|
||||
out=&fname4
|
||||
&oauth_bearer
|
||||
url="&base_uri/jobDefinitions/definitions?parentFolderUri=&parentFolderUri";
|
||||
headers 'Content-Type'='application/vnd.sas.job.definition+json'
|
||||
%if &grant_type=authorization_code %then %do;
|
||||
"Authorization"="Bearer &&&access_token_var"
|
||||
%end;
|
||||
"Accept"="application/vnd.sas.job.definition+json";
|
||||
%if &debug=1 %then %do;
|
||||
debug level = 3;
|
||||
%end;
|
||||
run;
|
||||
/*data _null_;infile &fname4;input;putlog _infile_;run;*/
|
||||
%mp_abort(iftrue=(&SYS_PROCHTTP_STATUS_CODE ne 201)
|
||||
,mac=&sysmacroname
|
||||
,msg=%str(&SYS_PROCHTTP_STATUS_CODE &SYS_PROCHTTP_STATUS_PHRASE)
|
||||
)
|
||||
/* clear refs */
|
||||
filename &fname1 clear;
|
||||
filename &fname2 clear;
|
||||
filename &fname3 clear;
|
||||
filename &fname4 clear;
|
||||
libname &libref1 clear;
|
||||
|
||||
/* get the url so we can give a helpful log message */
|
||||
%local url;
|
||||
data _null_;
|
||||
if symexist('_baseurl') then do;
|
||||
url=symget('_baseurl');
|
||||
if subpad(url,length(url)-9,9)='SASStudio'
|
||||
then url=substr(url,1,length(url)-11);
|
||||
else url="&systcpiphostname";
|
||||
end;
|
||||
else url="&systcpiphostname";
|
||||
call symputx('url',url);
|
||||
run;
|
||||
|
||||
|
||||
%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;
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
@file mv_createwebservice.sas
|
||||
@file
|
||||
@brief Creates a JobExecution web service if it doesn't already exist
|
||||
@details
|
||||
Code is passed in as one or more filerefs.
|
||||
@@ -566,23 +566,23 @@ run;
|
||||
rec = "20"x;
|
||||
do while(fread(filein)=0);
|
||||
rc = fget(filein,rec,1);
|
||||
if rec='"' then do;
|
||||
if rec='"' then do; /* DOUBLE QUOTE */
|
||||
rc =fput(fileid,'\');rc =fwrite(fileid);
|
||||
rc =fput(fileid,'"');rc =fwrite(fileid);
|
||||
end;
|
||||
else if rec='0A'x then do;
|
||||
rc =fput(fileid,'\');rc =fwrite(fileid);
|
||||
rc =fput(fileid,'r');rc =fwrite(fileid);
|
||||
end;
|
||||
else if rec='0D'x then do;
|
||||
else if rec='0A'x then do; /* LF */
|
||||
rc =fput(fileid,'\');rc =fwrite(fileid);
|
||||
rc =fput(fileid,'n');rc =fwrite(fileid);
|
||||
end;
|
||||
else if rec='09'x then do;
|
||||
else if rec='0D'x then do; /* CR */
|
||||
rc =fput(fileid,'\');rc =fwrite(fileid);
|
||||
rc =fput(fileid,'r');rc =fwrite(fileid);
|
||||
end;
|
||||
else if rec='09'x then do; /* TAB */
|
||||
rc =fput(fileid,'\');rc =fwrite(fileid);
|
||||
rc =fput(fileid,'t');rc =fwrite(fileid);
|
||||
end;
|
||||
else if rec='5C'x then do;
|
||||
else if rec='5C'x then do; /* BACKSLASH */
|
||||
rc =fput(fileid,'\');rc =fwrite(fileid);
|
||||
rc =fput(fileid,'\');rc =fwrite(fileid);
|
||||
end;
|
||||
|
||||
@@ -106,6 +106,8 @@
|
||||
@li sas_services - will use oauth_bearer=sas_services
|
||||
@param [in] inds= The input dataset containing a list of jobs and parameters
|
||||
@param [in] maxconcurrency= The max number of parallel jobs to run. Default=8.
|
||||
@param [in] raise_err=0 Set to 1 to raise SYSCC when a job does not complete
|
||||
succcessfully
|
||||
@param [in] mdebug= set to 1 to enable DEBUG messages
|
||||
@param [out] outds= The output dataset containing the results
|
||||
@param [out] outref= The output fileref to which to append the log file(s).
|
||||
@@ -129,6 +131,7 @@
|
||||
,access_token_var=ACCESS_TOKEN
|
||||
,grant_type=sas_services
|
||||
,outref=0
|
||||
,raise_err=0
|
||||
,mdebug=0
|
||||
);
|
||||
%local oauth_bearer;
|
||||
@@ -307,7 +310,8 @@ data;run;%let jdswaitfor=&syslast;
|
||||
%end;
|
||||
%if &jid=&jcnt %then %do;
|
||||
/* we are at the end of the loop - time to see which jobs have finished */
|
||||
%mv_jobwaitfor(ANY,inds=&jdsrunning,outds=&jdswaitfor,outref=&outref)
|
||||
%mv_jobwaitfor(ANY,inds=&jdsrunning,outds=&jdswaitfor,outref=&outref
|
||||
,raise_err=&raise_err)
|
||||
%local done;
|
||||
%let done=%mf_nobs(&jdswaitfor);
|
||||
%if &done>0 %then %do;
|
||||
|
||||
@@ -70,6 +70,8 @@
|
||||
following format: `/jobExecution/jobs/&JOBID./state` and the corresponding
|
||||
job name. The uri should be in a `uri` variable, and the job path/name
|
||||
should be in a `_program` variable.
|
||||
@param [in] raise_err=0 Set to 1 to raise SYSCC when a job does not complete
|
||||
succcessfully
|
||||
@param [out] outds= The output dataset containing the list of states by job
|
||||
(default=work.mv_jobexecute)
|
||||
@param [out] outref= A fileref to which the spawned job logs should be appended.
|
||||
@@ -81,6 +83,7 @@
|
||||
@li mp_abort.sas
|
||||
@li mf_getplatform.sas
|
||||
@li mf_getuniquefileref.sas
|
||||
@li mf_getuniquelibref.sas
|
||||
@li mf_existvar.sas
|
||||
@li mf_nobs.sas
|
||||
@li mv_getjoblog.sas
|
||||
@@ -93,6 +96,7 @@
|
||||
,inds=0
|
||||
,outds=work.mv_jobwaitfor
|
||||
,outref=0
|
||||
,raise_err=0
|
||||
);
|
||||
%local oauth_bearer;
|
||||
%if &grant_type=detect %then %do;
|
||||
@@ -136,7 +140,7 @@ options noquotelenmax;
|
||||
data _null_;
|
||||
length jobparams $32767;
|
||||
set &inds end=last;
|
||||
call symputx(cats('joburi',_n_),substr(uri,1,55)!!'/state','l');
|
||||
call symputx(cats('joburi',_n_),substr(uri,1,55),'l');
|
||||
call symputx(cats('jobname',_n_),_program,'l');
|
||||
call symputx(cats('jobparams',_n_),jobparams,'l');
|
||||
if last then call symputx('uricnt',_n_,'l');
|
||||
@@ -151,7 +155,7 @@ run;
|
||||
%let fname0=%mf_getuniquefileref();
|
||||
|
||||
data &outds;
|
||||
format _program uri $128. state $32. timestamp datetime19. jobparams $32767.;
|
||||
format _program uri $128. state $32. stateDetails $32. timestamp datetime19. jobparams $32767.;
|
||||
stop;
|
||||
run;
|
||||
|
||||
@@ -159,7 +163,7 @@ run;
|
||||
%do i=1 %to &uricnt;
|
||||
%if "&&joburi&i" ne "0" %then %do;
|
||||
proc http method='GET' out=&fname0 &oauth_bearer url="&base_uri/&&joburi&i";
|
||||
headers "Accept"="text/plain"
|
||||
headers "Accept"="application/json"
|
||||
%if &grant_type=authorization_code %then %do;
|
||||
"Authorization"="Bearer &&&access_token_var"
|
||||
%end; ;
|
||||
@@ -173,12 +177,20 @@ run;
|
||||
%end;
|
||||
|
||||
%let status=notset;
|
||||
|
||||
%local libref1;
|
||||
%let libref1=%mf_getuniquelibref();
|
||||
libname &libref1 json fileref=&fname0;
|
||||
|
||||
data _null_;
|
||||
infile &fname0;
|
||||
input;
|
||||
call symputx('status',_infile_,'l');
|
||||
length state stateDetails $32;
|
||||
set &libref1..root;
|
||||
call symputx('status',state,'l');
|
||||
call symputx('stateDetails',stateDetails,'l');
|
||||
run;
|
||||
|
||||
libname &libref1 clear;
|
||||
|
||||
%if &status=completed or &status=failed or &status=canceled %then %do;
|
||||
%local plainuri;
|
||||
%let plainuri=%substr(&&joburi&i,1,55);
|
||||
@@ -187,6 +199,7 @@ run;
|
||||
_program="&&jobname&i",
|
||||
uri="&plainuri",
|
||||
state="&status",
|
||||
stateDetails=symget("stateDetails"),
|
||||
timestamp=datetime(),
|
||||
jobparams=symget("jobparams&i");
|
||||
%let joburi&i=0; /* do not re-check */
|
||||
@@ -205,6 +218,16 @@ run;
|
||||
,msg=%str(status &status not expected!!)
|
||||
)
|
||||
%end;
|
||||
|
||||
%if (&raise_err) %then %do;
|
||||
%if (&status = canceled or &status = failed or %length(&stateDetails)>0) %then %do;
|
||||
%if ("&stateDetails" = "%str(war)ning") %then %let SYSCC=4;
|
||||
%else %let SYSCC=5;
|
||||
%put %str(ERR)OR: Job &&jobname&i. did not complete successfully. &stateDetails;
|
||||
%return;
|
||||
%end;
|
||||
%end;
|
||||
|
||||
%end;
|
||||
%if &i=&uricnt %then %do;
|
||||
%local goback;
|
||||
|
||||
Reference in New Issue
Block a user