Macros for SAS Application Developers
https://github.com/sasjs/core
mv_createjob.sas
Go to the documentation of this file.
1 /**
2  @file
3  @brief Creates a Viya Job
4  @details
5  Code is passed in as one or more filerefs.
6 
7  %* Step 1 - compile macros ;
8  filename mc url
9  "https://raw.githubusercontent.com/sasjs/core/main/all.sas";
10  %inc mc;
11 
12  %* Step 2 - Create some SAS code and add it to a job;
13  filename ft15f001 temp;
14  parmcards4;
15  data some_code;
16  set sashelp.class;
17  run;
18  ;;;;
19  %mv_createjob(path=/Public/app/sasjstemp/jobs/myjobs,name=myjob)
20 
21  The path to the job will then be shown in the log, eg as follows:
22 
23  ![viya job location](https://i.imgur.com/XRUDHgA.png)
24 
25 
26  <h4> SAS Macros </h4>
27  @li mp_abort.sas
28  @li mv_createfolder.sas
29  @li mf_getuniquelibref.sas
30  @li mf_getuniquefileref.sas
31  @li mf_getplatform.sas
32  @li mf_isblank.sas
33  @li mv_deletejes.sas
34 
35  @param [in] path= The full path (on SAS Drive) where the job will be created
36  @param [in] name= The name of the job
37  @param [in] desc= (Created by the mv_createjob.sas macro) The job description
38  @param [in] precode= ()
39  Space separated list of filerefs, pointing to the code that
40  needs to be attached to the beginning of the job
41  @param [in] code= (ft15f001) Fileref(s) of the actual code to be added
42  @param [in] access_token_var= (ACCESS_TOKEN)
43  Global macro variable containing the access token
44  @param [in] grant_type= (sas_services) Valid values:
45  @li sas_services
46  @li detect
47  @li authorization_code
48  @li password
49  @param [in] replace= (YES) select NO to avoid replacing any existing job
50  @param [in] addjesbeginendmacros= (false)
51  Relates to the `_addjesbeginendmacros` setting.
52  Normally this would always be false however due to a Viya bug
53  (https://github.com/sasjs/cli/issues/1229) this is now configurable. Valid
54  values:
55  @li true
56  @li false
57  @li 0 - this will prevent the flag from being set (job will default to true)
58  @param [in] contextname= () Choose a specific context on which to run the Job.
59  Leave blank to use the default context.
60  From Viya 3.5 it is possible to configure a shared context - see
61 https://go.documentation.sas.com/?docsetId=calcontexts&docsetTarget=n1hjn8eobk5pyhn1wg3ja0drdl6h.htm&docsetVersion=3.5&locale=en
62 
63  @version VIYA V.03.04
64  @author [Allan Bowe](https://www.linkedin.com/in/allanbowe)
65 
66 **/
67 
68 %macro mv_createjob(path=
69  ,name=
70  ,desc=Created by the mv_createjob.sas macro
71  ,precode=
72  ,code=ft15f001
73  ,access_token_var=ACCESS_TOKEN
74  ,grant_type=sas_services
75  ,replace=YES
76  ,debug=0
77  ,contextname=
78  ,addjesbeginendmacros=false
79  );
80 %local oauth_bearer;
81 %if &grant_type=detect %then %do;
82  %if %symexist(&access_token_var) %then %let grant_type=authorization_code;
83  %else %let grant_type=sas_services;
84 %end;
85 %if &grant_type=sas_services %then %do;
86  %let oauth_bearer=oauth_bearer=sas_services;
87  %let &access_token_var=;
88 %end;
89 
90 /* initial validation checking */
91 %mp_abort(iftrue=(&grant_type ne authorization_code and &grant_type ne password
92  and &grant_type ne sas_services
93  )
94  ,mac=&sysmacroname
95  ,msg=%str(Invalid value for grant_type: &grant_type)
96 )
97 %mp_abort(iftrue=(%mf_isblank(&path)=1)
98  ,mac=&sysmacroname
99  ,msg=%str(path value must be provided)
100 )
101 %mp_abort(iftrue=(%length(&path)=1)
102  ,mac=&sysmacroname
103  ,msg=%str(path value must be provided)
104 )
105 %mp_abort(iftrue=(%mf_isblank(&name)=1)
106  ,mac=&sysmacroname
107  ,msg=%str(name value must be provided)
108 )
109 
110 options noquotelenmax;
111 
112 * remove any trailing slash ;
113 %if "%substr(&path,%length(&path),1)" = "/" %then
114  %let path=%substr(&path,1,%length(&path)-1);
115 
116 /* ensure folder exists */
117 %put &sysmacroname: Path &path being checked / created;
118 %mv_createfolder(path=&path)
119 
120 %local base_uri; /* location of rest apis */
121 %let base_uri=%mf_getplatform(VIYARESTAPI);
122 
123 /* fetching folder details for provided path */
124 %local fname1;
125 %let fname1=%mf_getuniquefileref();
126 proc http method='GET' out=&fname1 &oauth_bearer
127  url="&base_uri/folders/folders/@item?path=&path";
128 %if &grant_type=authorization_code %then %do;
129  headers "Authorization"="Bearer &&&access_token_var";
130 %end;
131 run;
132 %if &debug %then %do;
133  data _null_;
134  infile &fname1;
135  input;
136  putlog _infile_;
137  run;
138 %end;
139 %mp_abort(iftrue=(&SYS_PROCHTTP_STATUS_CODE ne 200)
140  ,mac=&sysmacroname
141  ,msg=%str(&SYS_PROCHTTP_STATUS_CODE &SYS_PROCHTTP_STATUS_PHRASE)
142 )
143 
144 /* path exists. Grab follow on link to check members */
145 %local libref1;
146 %let libref1=%mf_getuniquelibref();
147 libname &libref1 JSON fileref=&fname1;
148 
149 data _null_;
150  set &libref1..links;
151  if rel='members' then call symputx('membercheck',quote("&base_uri"!!trim(href)),'l');
152  else if rel='self' then call symputx('parentFolderUri',href,'l');
153 run;
154 data _null_;
155  set &libref1..root;
156  call symputx('folderid',id,'l');
157 run;
158 %local fname2;
159 %let fname2=%mf_getuniquefileref();
160 proc http method='GET'
161  out=&fname2
162  &oauth_bearer
163  url=%unquote(%superq(membercheck));
164  headers
165  %if &grant_type=authorization_code %then %do;
166  "Authorization"="Bearer &&&access_token_var"
167  %end;
168  'Accept'='application/vnd.sas.collection+json'
169  'Accept-Language'='string';
170 %if &debug=1 %then %do;
171  debug level = 3;
172 %end;
173 run;
174 /*data _null_;infile &fname2;input;putlog _infile_;run;*/
175 %mp_abort(iftrue=(&SYS_PROCHTTP_STATUS_CODE ne 200)
176  ,mac=&sysmacroname
177  ,msg=%str(&SYS_PROCHTTP_STATUS_CODE &SYS_PROCHTTP_STATUS_PHRASE)
178 )
179 
180 %if %upcase(&replace)=YES %then %do;
181  %mv_deletejes(path=&path, name=&name)
182 %end;
183 %else %do;
184  /* check that job does not already exist in that folder */
185  %local libref2;
186  %let libref2=%mf_getuniquelibref();
187  libname &libref2 JSON fileref=&fname2;
188  %local exists; %let exists=0;
189  data _null_;
190  set &libref2..items;
191  if contenttype='jobDefinition' and upcase(name)="%upcase(&name)" then
192  call symputx('exists',1,'l');
193  run;
194  %mp_abort(iftrue=(&exists=1)
195  ,mac=&sysmacroname
196  ,msg=%str(Job &name already exists in &path)
197  )
198  libname &libref2 clear;
199 %end;
200 
201 /* set up the body of the request to create the service */
202 %local fname3 comma;
203 %let fname3=%mf_getuniquefileref();
204 data _null_;
205  file &fname3 TERMSTR=' ';
206  length string $32767;
207  string=cats('{"version": 0,"name":"'
208  ,"&name"
209  ,'","type":"Compute","parameters":['
210 %if &addjesbeginendmacros ne 0 %then %do;
211  ,'{"name":"_addjesbeginendmacros"'
212  ,',"type":"CHARACTER","defaultValue":"'
213  ,"&addjesbeginendmacros"
214  ,'"}'
215  %let comma=%str(,);
216 %end;
217  );
218  context=quote(cats(symget('contextname')));
219  if context ne '""' then do;
220  string=cats(string
221  ,"&comma"
222  ,'{"version": 1,"name": "_contextName","defaultValue":'
223  ,context,',"type":"CHARACTER","label":"Context Name","required": false}'
224  );
225  end;
226  string=cats(string,'],"code":"');
227  put string;
228 run;
229 
230 
231 /* insert the code, escaping double quotes and carriage returns */
232 %local x fref freflist;
233 %let freflist= &precode &code ;
234 %do x=1 %to %sysfunc(countw(&freflist));
235  %let fref=%scan(&freflist,&x);
236  %put &sysmacroname: adding &fref;
237  data _null_;
238  length filein 8 fileid 8;
239  filein = fopen("&fref","I",1,"B");
240  fileid = fopen("&fname3","A",1,"B");
241  rec = "20"x;
242  do while(fread(filein)=0);
243  rc = fget(filein,rec,1);
244  if rec='"' then do; /* DOUBLE QUOTE */
245  rc =fput(fileid,'\');rc =fwrite(fileid);
246  rc =fput(fileid,'"');rc =fwrite(fileid);
247  end;
248  else if rec='0A'x then do; /* LF */
249  rc =fput(fileid,'\');rc =fwrite(fileid);
250  rc =fput(fileid,'n');rc =fwrite(fileid);
251  end;
252  else if rec='0D'x then do; /* CR */
253  rc =fput(fileid,'\');rc =fwrite(fileid);
254  rc =fput(fileid,'r');rc =fwrite(fileid);
255  end;
256  else if rec='09'x then do; /* TAB */
257  rc =fput(fileid,'\');rc =fwrite(fileid);
258  rc =fput(fileid,'t');rc =fwrite(fileid);
259  end;
260  else if rec='5C'x then do; /* BACKSLASH */
261  rc =fput(fileid,'\');rc =fwrite(fileid);
262  rc =fput(fileid,'\');rc =fwrite(fileid);
263  end;
264  else if rec='01'x then do; /* Unprintable */
265  rc =fput(fileid,'\');rc =fwrite(fileid);
266  rc =fput(fileid,'u');rc =fwrite(fileid);
267  rc =fput(fileid,'0');rc =fwrite(fileid);
268  rc =fput(fileid,'0');rc =fwrite(fileid);
269  rc =fput(fileid,'0');rc =fwrite(fileid);
270  rc =fput(fileid,'1');rc =fwrite(fileid);
271  end;
272  else if rec='07'x then do; /* Bell Char */
273  rc =fput(fileid,'\');rc =fwrite(fileid);
274  rc =fput(fileid,'u');rc =fwrite(fileid);
275  rc =fput(fileid,'0');rc =fwrite(fileid);
276  rc =fput(fileid,'0');rc =fwrite(fileid);
277  rc =fput(fileid,'0');rc =fwrite(fileid);
278  rc =fput(fileid,'7');rc =fwrite(fileid);
279  end;
280  else if rec='1B'x then do; /* escape char */
281  rc =fput(fileid,'\');rc =fwrite(fileid);
282  rc =fput(fileid,'u');rc =fwrite(fileid);
283  rc =fput(fileid,'0');rc =fwrite(fileid);
284  rc =fput(fileid,'0');rc =fwrite(fileid);
285  rc =fput(fileid,'1');rc =fwrite(fileid);
286  rc =fput(fileid,'B');rc =fwrite(fileid);
287  end;
288  else do;
289  rc =fput(fileid,rec);
290  rc =fwrite(fileid);
291  end;
292  end;
293  rc=fclose(filein);
294  rc=fclose(fileid);
295  run;
296 %end;
297 
298 /* finish off the body of the code file loaded to JES */
299 data _null_;
300  file &fname3 mod TERMSTR=' ';
301  put '"}';
302 run;
303 
304 /* now we can create the job!! */
305 %local fname4;
306 %let fname4=%mf_getuniquefileref();
307 proc http method='POST'
308  in=&fname3
309  out=&fname4
310  &oauth_bearer
311  url="&base_uri/jobDefinitions/definitions?parentFolderUri=&parentFolderUri";
312  headers 'Content-Type'='application/vnd.sas.job.definition+json'
313  %if &grant_type=authorization_code %then %do;
314  "Authorization"="Bearer &&&access_token_var"
315  %end;
316  "Accept"="application/vnd.sas.job.definition+json";
317 %if &debug=1 %then %do;
318  debug level = 3;
319 %end;
320 run;
321 /*data _null_;infile &fname4;input;putlog _infile_;run;*/
322 %mp_abort(iftrue=(&SYS_PROCHTTP_STATUS_CODE ne 201)
323  ,mac=&sysmacroname
324  ,msg=%str(&SYS_PROCHTTP_STATUS_CODE &SYS_PROCHTTP_STATUS_PHRASE)
325 )
326 /* clear refs */
327 filename &fname1 clear;
328 filename &fname2 clear;
329 filename &fname3 clear;
330 filename &fname4 clear;
331 libname &libref1 clear;
332 
333 /* get the url so we can give a helpful log message */
334 %local url;
335 data _null_;
336  if symexist('_baseurl') then do;
337  url=symget('_baseurl');
338  if subpad(url,length(url)-9,9)='SASStudio'
339  then url=substr(url,1,length(url)-11);
340  else url="&systcpiphostname";
341  end;
342  else url="&systcpiphostname";
343  call symputx('url',url);
344 run;
345 
346 
347 %put &sysmacroname: Job &name successfully created in &path;
348 %put &sysmacroname:;
349 %put &sysmacroname: Check it out here:;
350 %put &sysmacroname:;%put;
351 %put &url/SASJobExecution?_PROGRAM=&path/&name;%put;
352 %put &sysmacroname:;
353 %put &sysmacroname:;
354 
355 %mend mv_createjob;