Production Ready Macros for SAS Application Developers
https://github.com/sasjs/core
mv_createwebservice.sas
Go to the documentation of this file.
1 /**
2  @file mv_createwebservice.sas
3  @brief Creates a JobExecution web service if it doesn't already exist
4  @details Code is passed in as one or more filerefs.
5 
6  %* Step 1 - compile macros ;
7  filename mc url "https://raw.githubusercontent.com/sasjs/core/main/all.sas";
8  %inc mc;
9 
10  %* Step 2 - Create some code and add it to a web service;
11  filename ft15f001 temp;
12  parmcards4;
13  %webout(FETCH) %* fetch any tables sent from frontend;
14  %* do some sas, any inputs are now already WORK tables;
15  data example1 example2;
16  set sashelp.class;
17  run;
18  %* send data back;
19  %webout(OPEN)
20  %webout(ARR,example1) * Array format, fast, suitable for large tables ;
21  %webout(OBJ,example2) * Object format, easier to work with ;
22  %webout(CLOSE)
23  ;;;;
24  %mv_createwebservice(path=/Public/app/common,name=appinit)
25 
26 
27  Notes:
28  To minimise postgres requests, output json is stored in a temporary file
29  and then sent to _webout in one go at the end.
30 
31  <h4> Dependencies </h4>
32  @li mp_abort.sas
33  @li mv_createfolder.sas
34  @li mf_getuniquelibref.sas
35  @li mf_getuniquefileref.sas
36  @li mf_getplatform.sas
37  @li mf_isblank.sas
38  @li mv_deletejes.sas
39 
40  @param path= The full path (on SAS Drive) where the service will be created
41  @param name= The name of the service
42  @param desc= The description of the service
43  @param precode= Space separated list of filerefs, pointing to the code that
44  needs to be attached to the beginning of the service
45  @param code= Fileref(s) of the actual code to be added
46  @param access_token_var= The global macro variable to contain the access token
47  @param grant_type= valid values are "password" or "authorization_code" (unquoted).
48  The default is authorization_code.
49  @param replace= select NO to avoid replacing any existing service in that location
50  @param adapter= the macro uses the sasjs adapter by default. To use another
51  adapter, add a (different) fileref here.
52  @param contextname= Choose a specific context on which to run the Job. Leave
53  blank to use the default context. From Viya 3.5 it is possible to configure
54  a shared context - see https://go.documentation.sas.com/?docsetId=calcontexts&docsetTarget=n1hjn8eobk5pyhn1wg3ja0drdl6h.htm&docsetVersion=3.5&locale=en
55 
56  @version VIYA V.03.04
57  @author Allan Bowe
58  @source https://github.com/sasjs/core
59 
60 **/
61 
62 %macro mv_createwebservice(path=
63  ,name=
64  ,desc=Created by the mv_createwebservice.sas macro
65  ,precode=
66  ,code=ft15f001
67  ,access_token_var=ACCESS_TOKEN
68  ,grant_type=sas_services
69  ,replace=YES
70  ,adapter=sasjs
71  ,debug=0
72  ,contextname=
73  );
74 %local oauth_bearer;
75 %if &grant_type=detect %then %do;
76  %if %symexist(&access_token_var) %then %let grant_type=authorization_code;
77  %else %let grant_type=sas_services;
78 %end;
79 %if &grant_type=sas_services %then %do;
80  %let oauth_bearer=oauth_bearer=sas_services;
81  %let &access_token_var=;
82 %end;
83 %put &sysmacroname: grant_type=&grant_type;
84 
85 /* initial validation checking */
86 %mp_abort(iftrue=(&grant_type ne authorization_code and &grant_type ne password
87  and &grant_type ne sas_services
88  )
89  ,mac=&sysmacroname
90  ,msg=%str(Invalid value for grant_type: &grant_type)
91 )
92 %mp_abort(iftrue=(%mf_isblank(&path)=1)
93  ,mac=&sysmacroname
94  ,msg=%str(path value must be provided)
95 )
96 %mp_abort(iftrue=(%length(&path)=1)
97  ,mac=&sysmacroname
98  ,msg=%str(path value must be provided)
99 )
100 %mp_abort(iftrue=(%mf_isblank(&name)=1)
101  ,mac=&sysmacroname
102  ,msg=%str(name value must be provided)
103 )
104 
105 options noquotelenmax;
106 
107 * remove any trailing slash ;
108 %if "%substr(&path,%length(&path),1)" = "/" %then
109  %let path=%substr(&path,1,%length(&path)-1);
110 
111 /* ensure folder exists */
112 %put &sysmacroname: Path &path being checked / created;
113 %mv_createfolder(path=&path)
114 
115 %local base_uri; /* location of rest apis */
116 %let base_uri=%mf_getplatform(VIYARESTAPI);
117 
118 /* fetching folder details for provided path */
119 %local fname1;
120 %let fname1=%mf_getuniquefileref();
121 proc http method='GET' out=&fname1 &oauth_bearer
122  url="&base_uri/folders/folders/@item?path=&path";
123 %if &grant_type=authorization_code %then %do;
124  headers "Authorization"="Bearer &&&access_token_var";
125 %end;
126 run;
127 %if &debug %then %do;
128  data _null_;
129  infile &fname1;
130  input;
131  putlog _infile_;
132  run;
133 %end;
134 %mp_abort(iftrue=(&SYS_PROCHTTP_STATUS_CODE ne 200)
135  ,mac=&sysmacroname
136  ,msg=%str(&SYS_PROCHTTP_STATUS_CODE &SYS_PROCHTTP_STATUS_PHRASE)
137 )
138 
139 /* path exists. Grab follow on link to check members */
140 %local libref1;
141 %let libref1=%mf_getuniquelibref();
142 libname &libref1 JSON fileref=&fname1;
143 
144 data _null_;
145  set &libref1..links;
146  if rel='members' then call symputx('membercheck',quote("&base_uri"!!trim(href)),'l');
147  else if rel='self' then call symputx('parentFolderUri',href,'l');
148 run;
149 data _null_;
150  set &libref1..root;
151  call symputx('folderid',id,'l');
152 run;
153 %local fname2;
154 %let fname2=%mf_getuniquefileref();
155 proc http method='GET'
156  out=&fname2
157  &oauth_bearer
158  url=%unquote(%superq(membercheck));
159  headers
160  %if &grant_type=authorization_code %then %do;
161  "Authorization"="Bearer &&&access_token_var"
162  %end;
163  'Accept'='application/vnd.sas.collection+json'
164  'Accept-Language'='string';
165 %if &debug=1 %then %do;
166  debug level = 3;
167 %end;
168 run;
169 /*data _null_;infile &fname2;input;putlog _infile_;run;*/
170 %mp_abort(iftrue=(&SYS_PROCHTTP_STATUS_CODE ne 200)
171  ,mac=&sysmacroname
172  ,msg=%str(&SYS_PROCHTTP_STATUS_CODE &SYS_PROCHTTP_STATUS_PHRASE)
173 )
174 
175 %if %upcase(&replace)=YES %then %do;
176  %mv_deletejes(path=&path, name=&name)
177 %end;
178 %else %do;
179  /* check that job does not already exist in that folder */
180  %local libref2;
181  %let libref2=%mf_getuniquelibref();
182  libname &libref2 JSON fileref=&fname2;
183  %local exists; %let exists=0;
184  data _null_;
185  set &libref2..items;
186  if contenttype='jobDefinition' and upcase(name)="%upcase(&name)" then
187  call symputx('exists',1,'l');
188  run;
189  %mp_abort(iftrue=(&exists=1)
190  ,mac=&sysmacroname
191  ,msg=%str(Job &name already exists in &path)
192  )
193  libname &libref2 clear;
194 %end;
195 
196 /* set up the body of the request to create the service */
197 %local fname3;
198 %let fname3=%mf_getuniquefileref();
199 data _null_;
200  file &fname3 TERMSTR=' ';
201  length string $32767;
202  string=cats('{"version": 0,"name":"'
203  ,"&name"
204  ,'","type":"Compute","parameters":[{"name":"_addjesbeginendmacros"'
205  ,',"type":"CHARACTER","defaultValue":"false"}');
206  context=quote(cats(symget('contextname')));
207  if context ne '""' then do;
208  string=cats(string,',{"version": 1,"name": "_contextName","defaultValue":'
209  ,context,',"type":"CHARACTER","label":"Context Name","required": false}');
210  end;
211  string=cats(string,'],"code":"');
212  put string;
213 run;
214 
215 /**
216  * Add webout macro
217  * These put statements are auto generated - to change the macro, change the
218  * source (mv_webout) and run `build.py`
219  */
220 filename sasjs temp lrecl=3000;
221 data _null_;
222  file sasjs;
223  put "/* Created on %sysfunc(datetime(),datetime19.) by &sysuserid */";
224 /* WEBOUT BEGIN */
225  put ' ';
226  put '%macro mp_jsonout(action,ds,jref=_webout,dslabel=,fmt=Y,engine=PROCJSON,dbg=0 ';
227  put ')/*/STORE SOURCE*/; ';
228  put '%put output location=&jref; ';
229  put '%if &action=OPEN %then %do; ';
230  put ' data _null_;file &jref encoding=''utf-8''; ';
231  put ' put ''{"START_DTTM" : "'' "%sysfunc(datetime(),datetime20.3)" ''"''; ';
232  put ' run; ';
233  put '%end; ';
234  put '%else %if (&action=ARR or &action=OBJ) %then %do; ';
235  put ' options validvarname=upcase; ';
236  put ' data _null_;file &jref mod encoding=''utf-8''; ';
237  put ' put ", ""%lowcase(%sysfunc(coalescec(&dslabel,&ds)))"":"; ';
238  put ' ';
239  put ' %if &engine=PROCJSON %then %do; ';
240  put ' data;run;%let tempds=&syslast; ';
241  put ' proc sql;drop table &tempds; ';
242  put ' data &tempds /view=&tempds;set &ds; ';
243  put ' %if &fmt=N %then format _numeric_ best32.;; ';
244  put ' proc json out=&jref pretty ';
245  put ' %if &action=ARR %then nokeys ; ';
246  put ' ;export &tempds / nosastags fmtnumeric; ';
247  put ' run; ';
248  put ' proc sql;drop view &tempds; ';
249  put ' %end; ';
250  put ' %else %if &engine=DATASTEP %then %do; ';
251  put ' %local cols i tempds; ';
252  put ' %let cols=0; ';
253  put ' %if %sysfunc(exist(&ds)) ne 1 & %sysfunc(exist(&ds,VIEW)) ne 1 %then %do; ';
254  put ' %put &sysmacroname: &ds NOT FOUND!!!; ';
255  put ' %return; ';
256  put ' %end; ';
257  put ' data _null_;file &jref mod ; ';
258  put ' put "["; call symputx(''cols'',0,''l''); ';
259  put ' proc sort data=sashelp.vcolumn(where=(libname=''WORK'' & memname="%upcase(&ds)")) ';
260  put ' out=_data_; ';
261  put ' by varnum; ';
262  put ' ';
263  put ' data _null_; ';
264  put ' set _last_ end=last; ';
265  put ' call symputx(cats(''name'',_n_),name,''l''); ';
266  put ' call symputx(cats(''type'',_n_),type,''l''); ';
267  put ' call symputx(cats(''len'',_n_),length,''l''); ';
268  put ' if last then call symputx(''cols'',_n_,''l''); ';
269  put ' run; ';
270  put ' ';
271  put ' proc format; /* credit yabwon for special null removal */ ';
272  put ' value bart ._ - .z = null ';
273  put ' other = [best.]; ';
274  put ' ';
275  put ' data;run; %let tempds=&syslast; /* temp table for spesh char management */ ';
276  put ' proc sql; drop table &tempds; ';
277  put ' data &tempds/view=&tempds; ';
278  put ' attrib _all_ label=''''; ';
279  put ' %do i=1 %to &cols; ';
280  put ' %if &&type&i=char %then %do; ';
281  put ' length &&name&i $32767; ';
282  put ' format &&name&i $32767.; ';
283  put ' %end; ';
284  put ' %end; ';
285  put ' set &ds; ';
286  put ' format _numeric_ bart.; ';
287  put ' %do i=1 %to &cols; ';
288  put ' %if &&type&i=char %then %do; ';
289  put ' &&name&i=''"''!!trim(prxchange(''s/"/\"/'',-1, ';
290  put ' prxchange(''s/''!!''0A''x!!''/\n/'',-1, ';
291  put ' prxchange(''s/''!!''0D''x!!''/\r/'',-1, ';
292  put ' prxchange(''s/''!!''09''x!!''/\t/'',-1, ';
293  put ' prxchange(''s/\\/\\\\/'',-1,&&name&i) ';
294  put ' )))))!!''"''; ';
295  put ' %end; ';
296  put ' %end; ';
297  put ' run; ';
298  put ' /* write to temp loc to avoid _webout truncation - https://support.sas.com/kb/49/325.html */ ';
299  put ' filename _sjs temp lrecl=131068 encoding=''utf-8''; ';
300  put ' data _null_; file _sjs lrecl=131068 encoding=''utf-8'' mod; ';
301  put ' set &tempds; ';
302  put ' if _n_>1 then put "," @; put ';
303  put ' %if &action=ARR %then "[" ; %else "{" ; ';
304  put ' %do i=1 %to &cols; ';
305  put ' %if &i>1 %then "," ; ';
306  put ' %if &action=OBJ %then """&&name&i"":" ; ';
307  put ' &&name&i ';
308  put ' %end; ';
309  put ' %if &action=ARR %then "]" ; %else "}" ; ; ';
310  put ' proc sql; ';
311  put ' drop view &tempds; ';
312  put ' /* now write the long strings to _webout 1 byte at a time */ ';
313  put ' data _null_; ';
314  put ' length filein 8 fileid 8; ';
315  put ' filein = fopen("_sjs",''I'',1,''B''); ';
316  put ' fileid = fopen("&jref",''A'',1,''B''); ';
317  put ' rec = ''20''x; ';
318  put ' do while(fread(filein)=0); ';
319  put ' rc = fget(filein,rec,1); ';
320  put ' rc = fput(fileid, rec); ';
321  put ' rc =fwrite(fileid); ';
322  put ' end; ';
323  put ' rc = fclose(filein); ';
324  put ' rc = fclose(fileid); ';
325  put ' run; ';
326  put ' filename _sjs clear; ';
327  put ' data _null_; file &jref mod encoding=''utf-8''; ';
328  put ' put "]"; ';
329  put ' run; ';
330  put ' %end; ';
331  put '%end; ';
332  put ' ';
333  put '%else %if &action=CLOSE %then %do; ';
334  put ' data _null_;file &jref encoding=''utf-8''; ';
335  put ' put "}"; ';
336  put ' run; ';
337  put '%end; ';
338  put '%mend; ';
339  put '%macro mv_webout(action,ds,fref=_mvwtemp,dslabel=,fmt=Y); ';
340  put '%global _webin_file_count _webin_fileuri _debug _omittextlog _webin_name ';
341  put ' sasjs_tables SYS_JES_JOB_URI; ';
342  put '%if %index("&_debug",log) %then %let _debug=131; ';
343  put ' ';
344  put '%local i tempds; ';
345  put '%let action=%upcase(&action); ';
346  put ' ';
347  put '%if &action=FETCH %then %do; ';
348  put ' %if %upcase(&_omittextlog)=FALSE or %str(&_debug) ge 131 %then %do; ';
349  put ' options mprint notes mprintnest; ';
350  put ' %end; ';
351  put ' ';
352  put ' %if not %symexist(_webin_fileuri1) %then %do; ';
353  put ' %let _webin_file_count=%eval(&_webin_file_count+0); ';
354  put ' %let _webin_fileuri1=&_webin_fileuri; ';
355  put ' %let _webin_name1=&_webin_name; ';
356  put ' %end; ';
357  put ' ';
358  put ' /* if the sasjs_tables param is passed, we expect param based upload */ ';
359  put ' %if %length(&sasjs_tables.XX)>2 %then %do; ';
360  put ' filename _sasjs "%sysfunc(pathname(work))/sasjs.lua"; ';
361  put ' data _null_; ';
362  put ' file _sasjs; ';
363  put ' put ''s=sas.symget("sasjs_tables")''; ';
364  put ' put ''if(s:sub(1,7) == "%nrstr(")''; ';
365  put ' put ''then''; ';
366  put ' put '' tablist=s:sub(8,s:len()-1)''; ';
367  put ' put ''else''; ';
368  put ' put '' tablist=s''; ';
369  put ' put ''end''; ';
370  put ' put ''for i = 1,sas.countw(tablist) ''; ';
371  put ' put ''do ''; ';
372  put ' put '' tab=sas.scan(tablist,i)''; ';
373  put ' put '' sasdata=""''; ';
374  put ' put '' if (sas.symexist("sasjs"..i.."data0")==0)''; ';
375  put ' put '' then''; ';
376  put ' /* TODO - condense this logic */ ';
377  put ' put '' s=sas.symget("sasjs"..i.."data")''; ';
378  put ' put '' if(s:sub(1,7) == "%nrstr(")''; ';
379  put ' put '' then''; ';
380  put ' put '' sasdata=s:sub(8,s:len()-1)''; ';
381  put ' put '' else''; ';
382  put ' put '' sasdata=s''; ';
383  put ' put '' end''; ';
384  put ' put '' else''; ';
385  put ' put '' for d = 1, sas.symget("sasjs"..i.."data0")''; ';
386  put ' put '' do''; ';
387  put ' put '' s=sas.symget("sasjs"..i.."data"..d)''; ';
388  put ' put '' if(s:sub(1,7) == "%nrstr(")''; ';
389  put ' put '' then''; ';
390  put ' put '' sasdata=sasdata..s:sub(8,s:len()-1)''; ';
391  put ' put '' else''; ';
392  put ' put '' sasdata=sasdata..s''; ';
393  put ' put '' end''; ';
394  put ' put '' end''; ';
395  put ' put '' end''; ';
396  put ' put '' file = io.open(sas.pathname("work").."/"..tab..".csv", "a")''; ';
397  put ' put '' io.output(file)''; ';
398  put ' put '' io.write(sasdata)''; ';
399  put ' put '' io.close(file)''; ';
400  put ' put ''end''; ';
401  put ' run; ';
402  put ' %inc _sasjs; ';
403  put ' ';
404  put ' /* now read in the data */ ';
405  put ' %do i=1 %to %sysfunc(countw(&sasjs_tables)); ';
406  put ' %local table; %let table=%scan(&sasjs_tables,&i); ';
407  put ' data _null_; ';
408  put ' infile "%sysfunc(pathname(work))/&table..csv" termstr=crlf ; ';
409  put ' input; ';
410  put ' if _n_=1 then call symputx(''input_statement'',_infile_); ';
411  put ' list; ';
412  put ' data &table; ';
413  put ' infile "%sysfunc(pathname(work))/&table..csv" firstobs=2 dsd termstr=crlf; ';
414  put ' input &input_statement; ';
415  put ' run; ';
416  put ' %end; ';
417  put ' %end; ';
418  put ' %else %do i=1 %to &_webin_file_count; ';
419  put ' /* read in any files that are sent */ ';
420  put ' /* this part needs refactoring for wide files */ ';
421  put ' filename indata filesrvc "&&_webin_fileuri&i" lrecl=999999; ';
422  put ' data _null_; ';
423  put ' infile indata termstr=crlf lrecl=32767; ';
424  put ' input; ';
425  put ' if _n_=1 then call symputx(''input_statement'',_infile_); ';
426  put ' %if %str(&_debug) ge 131 %then %do; ';
427  put ' if _n_<20 then putlog _infile_; ';
428  put ' else stop; ';
429  put ' %end; ';
430  put ' %else %do; ';
431  put ' stop; ';
432  put ' %end; ';
433  put ' run; ';
434  put ' data &&_webin_name&i; ';
435  put ' infile indata firstobs=2 dsd termstr=crlf ; ';
436  put ' input &input_statement; ';
437  put ' run; ';
438  put ' %let sasjs_tables=&sasjs_tables &&_webin_name&i; ';
439  put ' %end; ';
440  put '%end; ';
441  put '%else %if &action=OPEN %then %do; ';
442  put ' /* setup webout */ ';
443  put ' OPTIONS NOBOMFILE; ';
444  put ' %if "X&SYS_JES_JOB_URI.X"="XX" %then %do; ';
445  put ' filename _webout temp lrecl=999999 mod; ';
446  put ' %end; ';
447  put ' %else %do; ';
448  put ' filename _webout filesrvc parenturi="&SYS_JES_JOB_URI" ';
449  put ' name="_webout.json" lrecl=999999 mod; ';
450  put ' %end; ';
451  put ' ';
452  put ' /* setup temp ref */ ';
453  put ' %if %upcase(&fref) ne _WEBOUT %then %do; ';
454  put ' filename &fref temp lrecl=999999 permission=''A::u::rwx,A::g::rw-,A::o::---'' mod; ';
455  put ' %end; ';
456  put ' ';
457  put ' /* setup json */ ';
458  put ' data _null_;file &fref; ';
459  put ' put ''{"START_DTTM" : "'' "%sysfunc(datetime(),datetime20.3)" ''"''; ';
460  put ' run; ';
461  put '%end; ';
462  put '%else %if &action=ARR or &action=OBJ %then %do; ';
463  put ' %mp_jsonout(&action,&ds,dslabel=&dslabel,fmt=&fmt ';
464  put ' ,jref=&fref,engine=PROCJSON,dbg=%str(&_debug) ';
465  put ' ) ';
466  put '%end; ';
467  put '%else %if &action=CLOSE %then %do; ';
468  put ' %if %str(&_debug) ge 131 %then %do; ';
469  put ' /* send back first 10 records of each work table for debugging */ ';
470  put ' options obs=10; ';
471  put ' data;run;%let tempds=%scan(&syslast,2,.); ';
472  put ' ods output Members=&tempds; ';
473  put ' proc datasets library=WORK memtype=data; ';
474  put ' %local wtcnt;%let wtcnt=0; ';
475  put ' data _null_; set &tempds; ';
476  put ' if not (name =:"DATA"); ';
477  put ' i+1; ';
478  put ' call symputx(''wt''!!left(i),name); ';
479  put ' call symputx(''wtcnt'',i); ';
480  put ' data _null_; file &fref mod; put ",""WORK"":{"; ';
481  put ' %do i=1 %to &wtcnt; ';
482  put ' %let wt=&&wt&i; ';
483  put ' proc contents noprint data=&wt ';
484  put ' out=_data_ (keep=name type length format:); ';
485  put ' run;%let tempds=%scan(&syslast,2,.); ';
486  put ' data _null_; file &fref mod; ';
487  put ' dsid=open("WORK.&wt",''is''); ';
488  put ' nlobs=attrn(dsid,''NLOBS''); ';
489  put ' nvars=attrn(dsid,''NVARS''); ';
490  put ' rc=close(dsid); ';
491  put ' if &i>1 then put '',''@; ';
492  put ' put " ""&wt"" : {"; ';
493  put ' put ''"nlobs":'' nlobs; ';
494  put ' put '',"nvars":'' nvars; ';
495  put ' %mp_jsonout(OBJ,&tempds,jref=&fref,dslabel=colattrs,engine=DATASTEP) ';
496  put ' %mp_jsonout(OBJ,&wt,jref=&fref,dslabel=first10rows,engine=DATASTEP) ';
497  put ' data _null_; file &fref mod;put "}"; ';
498  put ' %end; ';
499  put ' data _null_; file &fref mod;put "}";run; ';
500  put ' %end; ';
501  put ' ';
502  put ' /* close off json */ ';
503  put ' data _null_;file &fref mod; ';
504  put ' _PROGRAM=quote(trim(resolve(symget(''_PROGRAM'')))); ';
505  put ' put ",""SYSUSERID"" : ""&sysuserid"" "; ';
506  put ' put ",""MF_GETUSER"" : ""%mf_getuser()"" "; ';
507  put ' SYS_JES_JOB_URI=quote(trim(resolve(symget(''SYS_JES_JOB_URI'')))); ';
508  put ' put '',"SYS_JES_JOB_URI" : '' SYS_JES_JOB_URI ; ';
509  put ' put ",""SYSJOBID"" : ""&sysjobid"" "; ';
510  put ' put ",""_DEBUG"" : ""&_debug"" "; ';
511  put ' put '',"_PROGRAM" : '' _PROGRAM ; ';
512  put ' put ",""SYSCC"" : ""&syscc"" "; ';
513  put ' put ",""SYSERRORTEXT"" : ""&syserrortext"" "; ';
514  put ' put ",""SYSHOSTNAME"" : ""&syshostname"" "; ';
515  put ' put ",""SYSSITE"" : ""&syssite"" "; ';
516  put ' put ",""SYSWARNINGTEXT"" : ""&syswarningtext"" "; ';
517  put ' put '',"END_DTTM" : "'' "%sysfunc(datetime(),datetime20.3)" ''" ''; ';
518  put ' put "}"; ';
519  put ' ';
520  put ' %if %upcase(&fref) ne _WEBOUT %then %do; ';
521  put ' data _null_; rc=fcopy("&fref","_webout");run; ';
522  put ' %end; ';
523  put ' ';
524  put '%end; ';
525  put ' ';
526  put '%mend; ';
527  put ' ';
528  put '%macro mf_getuser(type=META ';
529  put ')/*/STORE SOURCE*/; ';
530  put ' %local user metavar; ';
531  put ' %if &type=OS %then %let metavar=_secureusername; ';
532  put ' %else %let metavar=_metaperson; ';
533  put ' ';
534  put ' %if %symexist(SYS_COMPUTE_SESSION_OWNER) %then %let user=&SYS_COMPUTE_SESSION_OWNER; ';
535  put ' %else %if %symexist(&metavar) %then %do; ';
536  put ' %if %length(&&&metavar)=0 %then %let user=&sysuserid; ';
537  put ' /* sometimes SAS will add @domain extension - remove for consistency */ ';
538  put ' %else %let user=%scan(&&&metavar,1,@); ';
539  put ' %end; ';
540  put ' %else %let user=&sysuserid; ';
541  put ' ';
542  put ' %quote(&user) ';
543  put ' ';
544  put '%mend; ';
545 /* WEBOUT END */
546  put '/* if calling viya service with _job param, _program will conflict */';
547  put '/* so it is provided by SASjs instead as __program */';
548  put '%global __program _program;';
549  put '%let _program=%sysfunc(coalescec(&__program,&_program));';
550  put ' ';
551  put '%macro webout(action,ds,dslabel=,fmt=);';
552  put ' %mv_webout(&action,ds=&ds,dslabel=&dslabel,fmt=&fmt)';
553  put '%mend;';
554 run;
555 
556 /* insert the code, escaping double quotes and carriage returns */
557 %local x fref freflist;
558 %let freflist= &adapter &precode &code ;
559 %do x=1 %to %sysfunc(countw(&freflist));
560  %let fref=%scan(&freflist,&x);
561  %put &sysmacroname: adding &fref;
562  data _null_;
563  length filein 8 fileid 8;
564  filein = fopen("&fref","I",1,"B");
565  fileid = fopen("&fname3","A",1,"B");
566  rec = "20"x;
567  do while(fread(filein)=0);
568  rc = fget(filein,rec,1);
569  if rec='"' then do;
570  rc =fput(fileid,'\');rc =fwrite(fileid);
571  rc =fput(fileid,'"');rc =fwrite(fileid);
572  end;
573  else if rec='0A'x then do;
574  rc =fput(fileid,'\');rc =fwrite(fileid);
575  rc =fput(fileid,'r');rc =fwrite(fileid);
576  end;
577  else if rec='0D'x then do;
578  rc =fput(fileid,'\');rc =fwrite(fileid);
579  rc =fput(fileid,'n');rc =fwrite(fileid);
580  end;
581  else if rec='09'x then do;
582  rc =fput(fileid,'\');rc =fwrite(fileid);
583  rc =fput(fileid,'t');rc =fwrite(fileid);
584  end;
585  else if rec='5C'x then do;
586  rc =fput(fileid,'\');rc =fwrite(fileid);
587  rc =fput(fileid,'\');rc =fwrite(fileid);
588  end;
589  else do;
590  rc =fput(fileid,rec);
591  rc =fwrite(fileid);
592  end;
593  end;
594  rc=fclose(filein);
595  rc=fclose(fileid);
596  run;
597 %end;
598 
599 /* finish off the body of the code file loaded to JES */
600 data _null_;
601  file &fname3 mod TERMSTR=' ';
602  put '"}';
603 run;
604 
605 /* now we can create the job!! */
606 %local fname4;
607 %let fname4=%mf_getuniquefileref();
608 proc http method='POST'
609  in=&fname3
610  out=&fname4
611  &oauth_bearer
612  url="&base_uri/jobDefinitions/definitions?parentFolderUri=&parentFolderUri";
613  headers 'Content-Type'='application/vnd.sas.job.definition+json'
614  %if &grant_type=authorization_code %then %do;
615  "Authorization"="Bearer &&&access_token_var"
616  %end;
617  "Accept"="application/vnd.sas.job.definition+json";
618 %if &debug=1 %then %do;
619  debug level = 3;
620 %end;
621 run;
622 /*data _null_;infile &fname4;input;putlog _infile_;run;*/
623 %mp_abort(iftrue=(&SYS_PROCHTTP_STATUS_CODE ne 201)
624  ,mac=&sysmacroname
625  ,msg=%str(&SYS_PROCHTTP_STATUS_CODE &SYS_PROCHTTP_STATUS_PHRASE)
626 )
627 /* clear refs */
628 filename &fname1 clear;
629 filename &fname2 clear;
630 filename &fname3 clear;
631 filename &fname4 clear;
632 filename &adapter clear;
633 libname &libref1 clear;
634 
635 /* get the url so we can give a helpful log message */
636 %local url;
637 data _null_;
638  if symexist('_baseurl') then do;
639  url=symget('_baseurl');
640  if subpad(url,length(url)-9,9)='SASStudio'
641  then url=substr(url,1,length(url)-11);
642  else url="&systcpiphostname";
643  end;
644  else url="&systcpiphostname";
645  call symputx('url',url);
646 run;
647 
648 
649 %put &sysmacroname: Job &name successfully created in &path;
650 %put &sysmacroname:;
651 %put &sysmacroname: Check it out here:;
652 %put &sysmacroname:;%put;
653 %put &url/SASJobExecution?_PROGRAM=&path/&name;%put;
654 %put &sysmacroname:;
655 %put &sysmacroname:;
656 
657 %mend;