1
0
mirror of https://github.com/sasjs/core.git synced 2025-12-10 22:14:35 +00:00
Files
core/viya/mv_createwebservice.sas

815 lines
29 KiB
SAS

/**
@file
@brief Creates a JobExecution web service if it doesn't already exist
@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 code and add it to a web service;
filename ft15f001 temp;
parmcards4;
%webout(FETCH) %* fetch any tables sent from frontend;
%* do some sas, any inputs are now already WORK tables;
data example1 example2;
set sashelp.class;
run;
%* send data back;
%webout(OPEN)
%webout(ARR,example1) * Array format, fast, suitable for large tables;
%webout(OBJ,example2) * Object format, easier to work with ;
%webout(CLOSE)
;;;;
%mv_createwebservice(path=/Public/app/common,name=appinit)
Notes:
To minimise postgres requests, output json is stored in a temporary file
and then sent to _webout in one go at the end.
<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 [in] path= The full path (on SAS Drive) where the service will be
created
@param [in] name= The name of the service
@param [in] desc= The description of the service
@param [in] precode= Space separated list of filerefs, pointing to the code
that needs to be attached to the beginning of the service
@param [in] code= Fileref(s) of the actual code to be added
@param [in] access_token_var= The global macro variable to contain the access
token
@param [in] grant_type= valid values are "password" or "authorization_code"
(unquoted). The default is authorization_code.
@param [in] replace=(YES) Select NO to avoid replacing any existing service in
that location
@param [in] adapter= the macro uses the sasjs adapter by default. To use
another adapter, add a (different) fileref here.
@param [in] 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
@param [in] mdebug=(0) set to 1 to enable DEBUG messages
@version VIYA V.03.04
@author Allan Bowe, source: https://github.com/sasjs/core
**/
%macro mv_createwebservice(path=
,name=
,desc=Created by the mv_createwebservice.sas macro
,precode=
,code=ft15f001
,access_token_var=ACCESS_TOKEN
,grant_type=sas_services
,replace=YES
,adapter=sasjs
,mdebug=0
,contextname=
,debug=0 /* @TODO - Deprecate */
);
%local dbg;
%if &mdebug=1 %then %do;
%put &sysmacroname entry vars:;
%put _local_;
%end;
%else %let dbg=*;
%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;
/* 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 &mdebug=1 %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 &mdebug=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;
/**
* Add webout macro
* These put statements are auto generated - to change the macro, change the
* source (mv_webout) and run `build.py`
*/
filename &adapter temp lrecl=3000;
data _null_;
file &adapter;
put "/* Created on %sysfunc(datetime(),datetime19.) by &sysuserid */";
/* WEBOUT BEGIN */
put ' ';
put '%macro mp_jsonout(action,ds,jref=_webout,dslabel=,fmt=Y ';
put ' ,engine=DATASTEP ';
put ' ,missing=NULL ';
put ' ,showmeta=NO ';
put ')/*/STORE SOURCE*/; ';
put '%local tempds colinfo fmtds i numcols; ';
put '%let numcols=0; ';
put ' ';
put '%if &action=OPEN %then %do; ';
put ' options nobomfile; ';
put ' data _null_;file &jref encoding=''utf-8'' ; ';
put ' put ''{"PROCESSED_DTTM" : "'' "%sysfunc(datetime(),E8601DT26.6)" ''"''; ';
put ' run; ';
put '%end; ';
put '%else %if (&action=ARR or &action=OBJ) %then %do; ';
put ' options validvarname=upcase; ';
put ' data _null_; file &jref encoding=''utf-8'' mod; ';
put ' put ", ""%lowcase(%sysfunc(coalescec(&dslabel,&ds)))"":"; ';
put ' ';
put ' /* grab col defs */ ';
put ' proc contents noprint data=&ds ';
put ' out=_data_(keep=name type length format formatl formatd varnum label); ';
put ' run; ';
put ' %let colinfo=%scan(&syslast,2,.); ';
put ' proc sort data=&colinfo; ';
put ' by varnum; ';
put ' run; ';
put ' /* move meta to mac vars */ ';
put ' data _null_; ';
put ' if _n_=1 then call symputx(''numcols'',nobs,''l''); ';
put ' set &colinfo end=last nobs=nobs; ';
put ' name=upcase(name); ';
put ' /* fix formats */ ';
put ' if type=2 or type=6 then do; ';
put ' typelong=''char''; ';
put ' length fmt $49.; ';
put ' if format='''' then fmt=cats(''$'',length,''.''); ';
put ' else if formatl=0 then fmt=cats(format,''.''); ';
put ' else fmt=cats(format,formatl,''.''); ';
put ' newlen=max(formatl,length); ';
put ' end; ';
put ' else do; ';
put ' typelong=''num''; ';
put ' if format='''' then fmt=''best.''; ';
put ' else if formatl=0 then fmt=cats(format,''.''); ';
put ' else if formatd=0 then fmt=cats(format,formatl,''.''); ';
put ' else fmt=cats(format,formatl,''.'',formatd); ';
put ' /* needs to be wide, for datetimes etc */ ';
put ' newlen=max(length,formatl,24); ';
put ' end; ';
put ' /* 32 char unique name */ ';
put ' newname=''sasjs''!!substr(cats(put(md5(name),$hex32.)),1,27); ';
put ' ';
put ' call symputx(cats(''name'',_n_),name,''l''); ';
put ' call symputx(cats(''newname'',_n_),newname,''l''); ';
put ' call symputx(cats(''len'',_n_),newlen,''l''); ';
put ' call symputx(cats(''length'',_n_),length,''l''); ';
put ' call symputx(cats(''fmt'',_n_),fmt,''l''); ';
put ' call symputx(cats(''type'',_n_),type,''l''); ';
put ' call symputx(cats(''typelong'',_n_),typelong,''l''); ';
put ' call symputx(cats(''label'',_n_),coalescec(label,name),''l''); ';
put ' run; ';
put ' ';
put ' %let tempds=%substr(_%sysfunc(compress(%sysfunc(uuidgen()),-)),1,32); ';
put ' ';
put ' %if &engine=PROCJSON %then %do; ';
put ' %if &missing=STRING %then %do; ';
put ' %put &sysmacroname: Special Missings not supported in proc json.; ';
put ' %put &sysmacroname: Switching to DATASTEP engine; ';
put ' %goto datastep; ';
put ' %end; ';
put ' data &tempds /view=&tempds;set &ds; ';
put ' %if &fmt=N %then format _numeric_ best32.;; ';
put ' /* PRETTY is necessary to avoid line truncation in large files */ ';
put ' proc json out=&jref pretty ';
put ' %if &action=ARR %then nokeys ; ';
put ' ;export &tempds / nosastags fmtnumeric; ';
put ' run; ';
put ' %end; ';
put ' %else %if &engine=DATASTEP %then %do; ';
put ' %datastep: ';
put ' %if %sysfunc(exist(&ds)) ne 1 & %sysfunc(exist(&ds,VIEW)) ne 1 ';
put ' %then %do; ';
put ' %put &sysmacroname: &ds NOT FOUND!!!; ';
put ' %return; ';
put ' %end; ';
put ' ';
put ' %if &fmt=Y %then %do; ';
put ' data _data_; ';
put ' /* rename on entry */ ';
put ' set &ds(rename=( ';
put ' %do i=1 %to &numcols; ';
put ' &&name&i=&&newname&i ';
put ' %end; ';
put ' )); ';
put ' %do i=1 %to &numcols; ';
put ' length &&name&i $&&len&i; ';
put ' &&name&i=left(put(&&newname&i,&&fmt&i)); ';
put ' drop &&newname&i; ';
put ' %end; ';
put ' if _error_ then call symputx(''syscc'',1012); ';
put ' run; ';
put ' %let fmtds=&syslast; ';
put ' %end; ';
put ' ';
put ' proc format; /* credit yabwon for special null removal */ ';
put ' value bart (default=40) ';
put ' %if &missing=NULL %then %do; ';
put ' ._ - .z = null ';
put ' %end; ';
put ' %else %do; ';
put ' ._ = [quote()] ';
put ' . = null ';
put ' .a - .z = [quote()] ';
put ' %end; ';
put ' other = [best.]; ';
put ' ';
put ' data &tempds/view=&tempds; ';
put ' attrib _all_ label=''''; ';
put ' %do i=1 %to &numcols; ';
put ' %if &&typelong&i=char or &fmt=Y %then %do; ';
put ' length &&name&i $32767; ';
put ' format &&name&i $32767.; ';
put ' %end; ';
put ' %end; ';
put ' %if &fmt=Y %then %do; ';
put ' set &fmtds; ';
put ' %end; ';
put ' %else %do; ';
put ' set &ds; ';
put ' %end; ';
put ' format _numeric_ bart.; ';
put ' %do i=1 %to &numcols; ';
put ' %if &&typelong&i=char or &fmt=Y %then %do; ';
put ' &&name&i=''"''!!trim(prxchange(''s/"/\"/'',-1, ';
put ' prxchange(''s/''!!''0A''x!!''/\n/'',-1, ';
put ' prxchange(''s/''!!''0D''x!!''/\r/'',-1, ';
put ' prxchange(''s/''!!''09''x!!''/\t/'',-1, ';
put ' prxchange(''s/\\/\\\\/'',-1,&&name&i) ';
put ' )))))!!''"''; ';
put ' %end; ';
put ' %end; ';
put ' run; ';
put ' ';
put ' /* write to temp loc to avoid _webout truncation ';
put ' - https://support.sas.com/kb/49/325.html */ ';
put ' filename _sjs temp lrecl=131068 encoding=''utf-8''; ';
put ' data _null_; file _sjs lrecl=131068 encoding=''utf-8'' mod ; ';
put ' if _n_=1 then put "["; ';
put ' set &tempds; ';
put ' if _n_>1 then put "," @; put ';
put ' %if &action=ARR %then "[" ; %else "{" ; ';
put ' %do i=1 %to &numcols; ';
put ' %if &i>1 %then "," ; ';
put ' %if &action=OBJ %then """&&name&i"":" ; ';
put ' &&name&i ';
put ' %end; ';
put ' %if &action=ARR %then "]" ; %else "}" ; ; ';
put ' /* now write the long strings to _webout 1 byte at a time */ ';
put ' data _null_; ';
put ' length filein 8 fileid 8; ';
put ' filein=fopen("_sjs",''I'',1,''B''); ';
put ' fileid=fopen("&jref",''A'',1,''B''); ';
put ' rec=''20''x; ';
put ' do while(fread(filein)=0); ';
put ' rc=fget(filein,rec,1); ';
put ' rc=fput(fileid, rec); ';
put ' rc=fwrite(fileid); ';
put ' end; ';
put ' /* close out the table */ ';
put ' rc=fput(fileid, "]"); ';
put ' rc=fwrite(fileid); ';
put ' rc=fclose(filein); ';
put ' rc=fclose(fileid); ';
put ' run; ';
put ' filename _sjs clear; ';
put ' %end; ';
put ' ';
put ' proc sql; ';
put ' drop view &tempds; ';
put ' drop table &colinfo; ';
put ' ';
put ' %if &showmeta=YES %then %do; ';
put ' data _null_; file &jref encoding=''utf-8'' mod; ';
put ' put ", ""$%lowcase(%sysfunc(coalescec(&dslabel,&ds)))"":{""vars"":{"; ';
put ' do i=1 to &numcols; ';
put ' name=quote(trim(symget(cats(''name'',i)))); ';
put ' format=quote(trim(symget(cats(''fmt'',i)))); ';
put ' label=quote(trim(symget(cats(''label'',i)))); ';
put ' length=quote(trim(symget(cats(''length'',i)))); ';
put ' type=quote(trim(symget(cats(''typelong'',i)))); ';
put ' if i>1 then put "," @@; ';
put ' put name '':{"format":'' format '',"label":'' label ';
put ' '',"length":'' length '',"type":'' type ''}''; ';
put ' end; ';
put ' put ''}}''; ';
put ' run; ';
put ' %end; ';
put '%end; ';
put ' ';
put '%else %if &action=CLOSE %then %do; ';
put ' data _null_; file &jref encoding=''utf-8'' mod ; ';
put ' put "}"; ';
put ' run; ';
put '%end; ';
put '%mend mp_jsonout; ';
put '%macro mv_webout(action,ds,fref=_mvwtemp,dslabel=,fmt=Y,stream=Y,missing=NULL ';
put ' ,showmeta=NO ';
put '); ';
put '%global _webin_file_count _webin_fileuri _debug _omittextlog _webin_name ';
put ' sasjs_tables SYS_JES_JOB_URI; ';
put '%if %index("&_debug",log) %then %let _debug=131; ';
put ' ';
put '%local i tempds; ';
put '%let action=%upcase(&action); ';
put ' ';
put '%if &action=FETCH %then %do; ';
put ' %if %upcase(&_omittextlog)=FALSE or %str(&_debug) ge 131 %then %do; ';
put ' options mprint notes mprintnest; ';
put ' %end; ';
put ' ';
put ' %if not %symexist(_webin_fileuri1) %then %do; ';
put ' %let _webin_file_count=%eval(&_webin_file_count+0); ';
put ' %let _webin_fileuri1=&_webin_fileuri; ';
put ' %let _webin_name1=&_webin_name; ';
put ' %end; ';
put ' ';
put ' /* if the sasjs_tables param is passed, we expect param based upload */ ';
put ' %if %length(&sasjs_tables.XX)>2 %then %do; ';
put ' filename _sasjs "%sysfunc(pathname(work))/sasjs.lua"; ';
put ' data _null_; ';
put ' file _sasjs; ';
put ' put ''s=sas.symget("sasjs_tables")''; ';
put ' put ''if(s:sub(1,7) == "%nrstr(")''; ';
put ' put ''then''; ';
put ' put '' tablist=s:sub(8,s:len()-1)''; ';
put ' put ''else''; ';
put ' put '' tablist=s''; ';
put ' put ''end''; ';
put ' put ''for i = 1,sas.countw(tablist) ''; ';
put ' put ''do ''; ';
put ' put '' tab=sas.scan(tablist,i)''; ';
put ' put '' sasdata=""''; ';
put ' put '' if (sas.symexist("sasjs"..i.."data0")==0)''; ';
put ' put '' then''; ';
put ' /* TODO - condense this logic */ ';
put ' put '' s=sas.symget("sasjs"..i.."data")''; ';
put ' put '' if(s:sub(1,7) == "%nrstr(")''; ';
put ' put '' then''; ';
put ' put '' sasdata=s:sub(8,s:len()-1)''; ';
put ' put '' else''; ';
put ' put '' sasdata=s''; ';
put ' put '' end''; ';
put ' put '' else''; ';
put ' put '' for d = 1, sas.symget("sasjs"..i.."data0")''; ';
put ' put '' do''; ';
put ' put '' s=sas.symget("sasjs"..i.."data"..d)''; ';
put ' put '' if(s:sub(1,7) == "%nrstr(")''; ';
put ' put '' then''; ';
put ' put '' sasdata=sasdata..s:sub(8,s:len()-1)''; ';
put ' put '' else''; ';
put ' put '' sasdata=sasdata..s''; ';
put ' put '' end''; ';
put ' put '' end''; ';
put ' put '' end''; ';
put ' put '' file = io.open(sas.pathname("work").."/"..tab..".csv", "a")''; ';
put ' put '' io.output(file)''; ';
put ' put '' io.write(sasdata)''; ';
put ' put '' io.close(file)''; ';
put ' put ''end''; ';
put ' run; ';
put ' %inc _sasjs; ';
put ' ';
put ' /* now read in the data */ ';
put ' %do i=1 %to %sysfunc(countw(&sasjs_tables)); ';
put ' %local table; %let table=%scan(&sasjs_tables,&i); ';
put ' data _null_; ';
put ' infile "%sysfunc(pathname(work))/&table..csv" termstr=crlf ; ';
put ' input; ';
put ' if _n_=1 then call symputx(''input_statement'',_infile_); ';
put ' list; ';
put ' data &table; ';
put ' infile "%sysfunc(pathname(work))/&table..csv" firstobs=2 dsd ';
put ' termstr=crlf; ';
put ' input &input_statement; ';
put ' run; ';
put ' %end; ';
put ' %end; ';
put ' %else %do i=1 %to &_webin_file_count; ';
put ' /* read in any files that are sent */ ';
put ' /* this part needs refactoring for wide files */ ';
put ' filename indata filesrvc "&&_webin_fileuri&i" lrecl=999999; ';
put ' data _null_; ';
put ' infile indata termstr=crlf lrecl=32767; ';
put ' input; ';
put ' if _n_=1 then call symputx(''input_statement'',_infile_); ';
put ' %if %str(&_debug) ge 131 %then %do; ';
put ' if _n_<20 then putlog _infile_; ';
put ' else stop; ';
put ' %end; ';
put ' %else %do; ';
put ' stop; ';
put ' %end; ';
put ' run; ';
put ' data &&_webin_name&i; ';
put ' infile indata firstobs=2 dsd termstr=crlf ; ';
put ' input &input_statement; ';
put ' run; ';
put ' %let sasjs_tables=&sasjs_tables &&_webin_name&i; ';
put ' %end; ';
put '%end; ';
put '%else %if &action=OPEN %then %do; ';
put ' /* setup webout */ ';
put ' OPTIONS NOBOMFILE; ';
put ' %if "X&SYS_JES_JOB_URI.X"="XX" %then %do; ';
put ' filename _webout temp lrecl=999999 mod; ';
put ' %end; ';
put ' %else %do; ';
put ' filename _webout filesrvc parenturi="&SYS_JES_JOB_URI" ';
put ' name="_webout.json" lrecl=999999 mod; ';
put ' %end; ';
put ' ';
put ' /* setup temp ref */ ';
put ' %if %upcase(&fref) ne _WEBOUT %then %do; ';
put ' filename &fref temp lrecl=999999 permission=''A::u::rwx,A::g::rw-,A::o::---'' ';
put ' mod; ';
put ' %end; ';
put ' ';
put ' /* setup json */ ';
put ' data _null_;file &fref; ';
put ' put ''{"SYSDATE" : "'' "&SYSDATE" ''"''; ';
put ' put '',"SYSTIME" : "'' "&SYSTIME" ''"''; ';
put ' run; ';
put '%end; ';
put '%else %if &action=ARR or &action=OBJ %then %do; ';
put ' %mp_jsonout(&action,&ds,dslabel=&dslabel,fmt=&fmt ';
put ' ,jref=&fref,engine=DATASTEP,missing=&missing,showmeta=&showmeta ';
put ' ) ';
put '%end; ';
put '%else %if &action=CLOSE %then %do; ';
put ' %if %str(&_debug) ge 131 %then %do; ';
put ' /* send back first 10 records of each work table for debugging */ ';
put ' options obs=10; ';
put ' data;run;%let tempds=%scan(&syslast,2,.); ';
put ' ods output Members=&tempds; ';
put ' proc datasets library=WORK memtype=data; ';
put ' %local wtcnt;%let wtcnt=0; ';
put ' data _null_; ';
put ' set &tempds; ';
put ' if not (upcase(name) =:"DATA"); /* ignore temp datasets */ ';
put ' i+1; ';
put ' call symputx(cats(''wt'',i),name,''l''); ';
put ' call symputx(''wtcnt'',i,''l''); ';
put ' data _null_; file &fref mod; put ",""WORK"":{"; ';
put ' %do i=1 %to &wtcnt; ';
put ' %let wt=&&wt&i; ';
put ' data _null_; file &fref mod; ';
put ' dsid=open("WORK.&wt",''is''); ';
put ' nlobs=attrn(dsid,''NLOBS''); ';
put ' nvars=attrn(dsid,''NVARS''); ';
put ' rc=close(dsid); ';
put ' if &i>1 then put '',''@; ';
put ' put " ""&wt"" : {"; ';
put ' put ''"nlobs":'' nlobs; ';
put ' put '',"nvars":'' nvars; ';
put ' %mp_jsonout(OBJ,&wt,jref=&fref,dslabel=first10rows,showmeta=YES) ';
put ' data _null_; file &fref mod;put "}"; ';
put ' %end; ';
put ' data _null_; file &fref mod;put "}";run; ';
put ' %end; ';
put ' ';
put ' /* close off json */ ';
put ' data _null_;file &fref mod; ';
put ' _PROGRAM=quote(trim(resolve(symget(''_PROGRAM'')))); ';
put ' put ",""SYSUSERID"" : ""&sysuserid"" "; ';
put ' put ",""MF_GETUSER"" : ""%mf_getuser()"" "; ';
put ' SYS_JES_JOB_URI=quote(trim(resolve(symget(''SYS_JES_JOB_URI'')))); ';
put ' put '',"SYS_JES_JOB_URI" : '' SYS_JES_JOB_URI ; ';
put ' put ",""SYSJOBID"" : ""&sysjobid"" "; ';
put ' put ",""_DEBUG"" : ""&_debug"" "; ';
put ' put '',"_PROGRAM" : '' _PROGRAM ; ';
put ' put ",""SYSCC"" : ""&syscc"" "; ';
put ' put ",""SYSERRORTEXT"" : ""&syserrortext"" "; ';
put ' put ",""SYSHOSTNAME"" : ""&syshostname"" "; ';
put ' put ",""SYSSCPL"" : ""&sysscpl"" "; ';
put ' put ",""SYSSITE"" : ""&syssite"" "; ';
put ' sysvlong=quote(trim(symget(''sysvlong''))); ';
put ' put '',"SYSVLONG" : '' sysvlong; ';
put ' put ",""SYSWARNINGTEXT"" : ""&syswarningtext"" "; ';
put ' put '',"END_DTTM" : "'' "%sysfunc(datetime(),E8601DT26.6)" ''" ''; ';
put ' length memsize $32; ';
put ' memsize="%sysfunc(INPUTN(%sysfunc(getoption(memsize)), best.),sizekmg.)"; ';
put ' memsize=quote(cats(memsize)); ';
put ' put '',"MEMSIZE" : '' memsize; ';
put ' put "}"; ';
put ' ';
put ' %if %upcase(&fref) ne _WEBOUT and &stream=Y %then %do; ';
put ' data _null_; rc=fcopy("&fref","_webout");run; ';
put ' %end; ';
put ' ';
put '%end; ';
put ' ';
put '%mend mv_webout; ';
put ' ';
put '%macro mf_getuser(type=META ';
put ')/*/STORE SOURCE*/; ';
put ' %local user metavar; ';
put ' %if &type=OS %then %let metavar=_secureusername; ';
put ' %else %let metavar=_metaperson; ';
put ' ';
put ' %if %symexist(SYS_COMPUTE_SESSION_OWNER) %then %let user=&SYS_COMPUTE_SESSION_OWNER; ';
put ' %else %if %symexist(&metavar) %then %do; ';
put ' %if %length(&&&metavar)=0 %then %let user=&sysuserid; ';
put ' /* sometimes SAS will add @domain extension - remove for consistency */ ';
put ' %else %let user=%scan(&&&metavar,1,@); ';
put ' %end; ';
put ' %else %let user=&sysuserid; ';
put ' ';
put ' %quote(&user) ';
put ' ';
put '%mend mf_getuser; ';
/* WEBOUT END */
put '/* if calling viya service with _job param, _program will conflict */';
put '/* so it is provided by SASjs instead as __program */';
put '%global __program _program;';
put '%let _program=%sysfunc(coalescec(&__program,&_program));';
put ' ';
put '%macro webout(action,ds,dslabel=,fmt=,missing=NULL,showmeta=NO);';
put ' %mv_webout(&action,ds=&ds,dslabel=&dslabel,fmt=&fmt,missing=&missing';
put ' ,showmeta=&showmeta';
put ' )';
put '%mend;';
run;
/* insert the code, escaping double quotes and carriage returns */
%&dbg.put &sysmacroname: Creating final input file;
%local x fref freflist;
%let freflist= &adapter &precode &code ;
%do x=1 %to %sysfunc(countw(&freflist));
%let fref=%scan(&freflist,&x);
%&dbg.put &sysmacroname: adding &fref fileref;
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 if rec='01'x then do; /* Unprintable */
rc =fput(fileid,'\');rc =fwrite(fileid);
rc =fput(fileid,'u');rc =fwrite(fileid);
rc =fput(fileid,'0');rc =fwrite(fileid);
rc =fput(fileid,'0');rc =fwrite(fileid);
rc =fput(fileid,'0');rc =fwrite(fileid);
rc =fput(fileid,'1');rc =fwrite(fileid);
end;
else if rec='07'x then do; /* Bell Char */
rc =fput(fileid,'\');rc =fwrite(fileid);
rc =fput(fileid,'u');rc =fwrite(fileid);
rc =fput(fileid,'0');rc =fwrite(fileid);
rc =fput(fileid,'0');rc =fwrite(fileid);
rc =fput(fileid,'0');rc =fwrite(fileid);
rc =fput(fileid,'7');rc =fwrite(fileid);
end;
else if rec='1B'x then do; /* escape char */
rc =fput(fileid,'\');rc =fwrite(fileid);
rc =fput(fileid,'u');rc =fwrite(fileid);
rc =fput(fileid,'0');rc =fwrite(fileid);
rc =fput(fileid,'0');rc =fwrite(fileid);
rc =fput(fileid,'1');rc =fwrite(fileid);
rc =fput(fileid,'B');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;
%if &mdebug=1 and &SYS_PROCHTTP_STATUS_CODE ne 201 %then %do;
%put &sysmacroname: input about to be POSTed;
data _null_;infile &fname3;input;putlog _infile_;run;
%end;
%&dbg.put &sysmacroname: Creating the actual service!;
%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 &mdebug=1 %then %do;
debug level = 3;
%end;
run;
%if &mdebug=1 and &SYS_PROCHTTP_STATUS_CODE ne 201 %then %do;
%put &sysmacroname: output from POSTing job definition;
data _null_;infile &fname4;input;putlog _infile_;run;
%end;
%mp_abort(iftrue=(&SYS_PROCHTTP_STATUS_CODE ne 201)
,mac=&sysmacroname
,msg=%str(&SYS_PROCHTTP_STATUS_CODE &SYS_PROCHTTP_STATUS_PHRASE)
)
/* 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;
%if &mdebug=1 %then %do;
%put &sysmacroname exit vars:;
%put _local_;
%end;
%else %do;
/* clear refs */
filename &fname1 clear;
filename &fname2 clear;
filename &fname3 clear;
filename &fname4 clear;
filename &adapter clear;
libname &libref1 clear;
%end;
%put &sysmacroname: Job &name successfully created in &path;
%put &sysmacroname:;
%put &sysmacroname: Check it out here:;
%put &sysmacroname:;%put;
%put &url/SASJobExecution?_PROGRAM=&path/&name;%put;
%put &sysmacroname:;
%put &sysmacroname:;
%mend mv_createwebservice;