Production Ready Macros for SAS Application Developers
https://github.com/sasjs/core
mv_jobwaitfor.sas
Go to the documentation of this file.
1 /**
2  @file
3  @brief Takes a dataset of running jobs and waits for ANY or ALL of them to complete
4  @details Will poll `/jobs/{jobId}/state` at set intervals until ANY or ALL
5  jobs are completed. Completion is determined by reference to the returned
6  _state_, as per the following table:
7 
8  | state | Wait? | Notes|
9  |-----------|-------|------|
10  | idle | yes | We assume processing will continue. Beware of idle sessions with no code submitted! |
11  | pending | yes | Job is preparing to run |
12  | running | yes | Job is running|
13  | canceled | no | Job was cancelled|
14  | completed | no | Job finished - does not mean it was successful. Check stateDetails|
15  | failed | no | Job failed to execute, could be a problem when calling the apis|
16 
17 
18  ## Example
19 
20  First, compile the macros:
21 
22  filename mc url
23  "https://raw.githubusercontent.com/sasjs/core/main/all.sas";
24  %inc mc;
25 
26  Next, create a job (in this case, as a web service):
27 
28  filename ft15f001 temp;
29  parmcards4;
30  data ;
31  rand=ranuni(0)*1000000;
32  do x=1 to rand;
33  y=rand*x;
34  output;
35  end;
36  run;
37  ;;;;
38  %mv_createwebservice(path=/Public/temp,name=demo)
39 
40  Then, execute the job,multiple times, and wait for them all to finish:
41 
42  %mv_jobexecute(path=/Public/temp,name=demo,outds=work.ds1)
43  %mv_jobexecute(path=/Public/temp,name=demo,outds=work.ds2)
44  %mv_jobexecute(path=/Public/temp,name=demo,outds=work.ds3)
45  %mv_jobexecute(path=/Public/temp,name=demo,outds=work.ds4)
46 
47  data work.jobs;
48  set work.ds1 work.ds2 work.ds3 work.ds4;
49  where method='GET' and rel='state';
50  run;
51 
52  %mv_jobwaitfor(ALL,inds=work.jobs,outds=work.jobstates)
53 
54  Delete the job:
55 
56  %mv_deletejes(path=/Public/temp,name=demo)
57 
58  @param [in] access_token_var= The global macro variable to contain the access token
59  @param [in] grant_type= valid values:
60 
61  - password
62  - authorization_code
63  - detect - will check if access_token exists, if not will use sas_services if
64  a SASStudioV session else authorization_code. Default option.
65  - sas_services - will use oauth_bearer=sas_services
66 
67  @param [in] action=Either ALL (to wait for every job) or ANY (if one job
68  completes, processing will continue). Default=ALL.
69  @param [in] inds= The input dataset containing the list of job uris, in the
70  following format: `/jobExecution/jobs/&JOBID./state` and the corresponding
71  job name. The uri should be in a `uri` variable, and the job path/name
72  should be in a `_program` variable.
73  @param [out] outds= The output dataset containing the list of states by job
74  (default=work.mv_jobexecute)
75 
76 
77  @version VIYA V.03.04
78  @author Allan Bowe, source: https://github.com/sasjs/core
79 
80  <h4> Dependencies </h4>
81  @li mp_abort.sas
82  @li mf_getplatform.sas
83  @li mf_getuniquefileref.sas
84  @li mf_existvar.sas
85  @li mf_nobs.sas
86 
87 **/
88 
89 %macro mv_jobwaitfor(action
90  ,access_token_var=ACCESS_TOKEN
91  ,grant_type=sas_services
92  ,inds=0
93  ,outds=work.mv_jobwaitfor
94  );
95 %local oauth_bearer;
96 %if &grant_type=detect %then %do;
97  %if %symexist(&access_token_var) %then %let grant_type=authorization_code;
98  %else %let grant_type=sas_services;
99 %end;
100 %if &grant_type=sas_services %then %do;
101  %let oauth_bearer=oauth_bearer=sas_services;
102  %let &access_token_var=;
103 %end;
104 
105 %mp_abort(iftrue=(&grant_type ne authorization_code and &grant_type ne password
106  and &grant_type ne sas_services
107  )
108  ,mac=&sysmacroname
109  ,msg=%str(Invalid value for grant_type: &grant_type)
110 )
111 
112 %mp_abort(iftrue=("&inds"="0")
113  ,mac=&sysmacroname
114  ,msg=%str(input dataset not provided)
115 )
116 %mp_abort(iftrue=(%mf_existvar(&inds,uri)=0)
117  ,mac=&sysmacroname
118  ,msg=%str(The URI variable was not found in the input dataset(&inds))
119 )
120 %mp_abort(iftrue=(%mf_existvar(&inds,_program)=0)
121  ,mac=&sysmacroname
122  ,msg=%str(The _PROGRAM variable was not found in the input dataset(&inds))
123 )
124 
125 %if %mf_nobs(&inds)=0 %then %do;
126  %put NOTE: Zero observations in &inds, &sysmacroname will now exit;
127  %return;
128 %end;
129 
130 options noquotelenmax;
131 %local base_uri; /* location of rest apis */
132 %let base_uri=%mf_getplatform(VIYARESTAPI);
133 
134 data _null_;
135  length jobparams $32767;
136  set &inds end=last;
137  call symputx(cats('joburi',_n_),uri,'l');
138  call symputx(cats('jobname',_n_),_program,'l');
139  call symputx(cats('jobparams',_n_),jobparams,'l');
140  if last then call symputx('uricnt',_n_,'l');
141 run;
142 
143 %local runcnt;
144 %if &action=ALL %then %let runcnt=&uricnt;
145 %else %if &action=ANY %then %let runcnt=1;
146 %else %let runcnt=&uricnt;
147 
148 %local fname0 ;
149 %let fname0=%mf_getuniquefileref();
150 
151 data &outds;
152  format _program uri $128. state $32. timestamp datetime19. jobparams $32767.;
153  stop;
154 run;
155 
156 %local i;
157 %do i=1 %to &uricnt;
158  %if "&&joburi&i" ne "0" %then %do;
159  proc http method='GET' out=&fname0 &oauth_bearer url="&base_uri/&&joburi&i";
160  headers "Accept"="text/plain"
161  %if &grant_type=authorization_code %then %do;
162  "Authorization"="Bearer &&&access_token_var"
163  %end; ;
164  run;
165  %if &SYS_PROCHTTP_STATUS_CODE ne 200 and &SYS_PROCHTTP_STATUS_CODE ne 201 %then
166  %do;
167  data _null_;infile &fname0;input;putlog _infile_;run;
168  %mp_abort(mac=&sysmacroname
169  ,msg=%str(&SYS_PROCHTTP_STATUS_CODE &SYS_PROCHTTP_STATUS_PHRASE)
170  )
171  %end;
172 
173  %let status=notset;
174  data _null_;
175  infile &fname0;
176  input;
177  call symputx('status',_infile_,'l');
178  run;
179 
180  %if &status=completed or &status=failed or &status=canceled %then %do;
181  proc sql;
182  insert into &outds set
183  _program="&&jobname&i",
184  uri="&&joburi&i",
185  state="&status",
186  timestamp=datetime(),
187  jobparams=symget("jobparams&i");
188  %let joburi&i=0; /* do not re-check */
189  %end;
190  %else %if &status=idle or &status=pending or &status=running %then %do;
191  data _null_;
192  call sleep(1,1);
193  run;
194  %end;
195  %else %do;
196  %mp_abort(mac=&sysmacroname
197  ,msg=%str(status &status not expected!!)
198  )
199  %end;
200  %end;
201  %if &i=&uricnt %then %do;
202  %local goback;
203  %let goback=0;
204  proc sql noprint;
205  select count(*) into:goback from &outds;
206  %if &goback lt &runcnt %then %let i=0;
207  %end;
208 %end;
209 
210 /* clear refs */
211 filename &fname0 clear;
212 
213 %mend;