11import json
22import re
3- from typing import Pattern
3+ from typing import Optional , Pattern
44
55from databricks .sdk import WorkspaceClient
66
77
8- def make_getrun_path_pattern (run_id : int , page_token : str ) -> Pattern [str ]:
9- return re .compile (
10- rf'{ re .escape ("http://localhost/api/" )} 2.\d{ re .escape (f"/jobs/runs/get?page_token={ page_token } &run_id={ run_id } " )} '
11- )
8+ def make_getrun_path_pattern (run_id : int , page_token : Optional [str ] = None ) -> Pattern [str ]:
9+ if page_token :
10+ return re .compile (
11+ rf'{ re .escape ("http://localhost/api/" )} 2.\d{ re .escape (f"/jobs/runs/get?page_token={ page_token } &run_id={ run_id } " )} '
12+ )
13+ else :
14+ return re .compile (
15+ rf'{ re .escape ("http://localhost/api/" )} 2.\d{ re .escape (f"/jobs/runs/get?run_id={ run_id } " )} ' )
1216
1317
1418def make_getjob_path_pattern (job_id : int , page_token : str ) -> Pattern [str ]:
@@ -17,6 +21,12 @@ def make_getjob_path_pattern(job_id: int, page_token: str) -> Pattern[str]:
1721 )
1822
1923
24+ def make_listruns_path_pattern (page_token : str ) -> Pattern [str ]:
25+ return re .compile (
26+ rf'{ re .escape ("http://localhost/api/" )} 2.\d{ re .escape (f"/jobs/runs/list" )} \?(?:expand_tasks=(?:true|false)&)?page_token={ re .escape (page_token )} '
27+ )
28+
29+
2030def test_get_run_with_no_pagination (config , requests_mock ):
2131 run1 = {"tasks" : [{"run_id" : 0 }, {"run_id" : 1 }], }
2232 requests_mock .get (make_getrun_path_pattern (1337 , "initialToken" ), text = json .dumps (run1 ))
@@ -261,3 +271,190 @@ def test_get_job_pagination_with_tasks(config, requests_mock):
261271 }]
262272 }
263273 }
274+
275+
276+ def test_list_runs_without_task_expansion (config , requests_mock ):
277+ listruns_page1 = {
278+ "runs" : [{
279+ "run_id" : 100 ,
280+ "run_name" : "run100" ,
281+ }, {
282+ "run_id" :
283+ 200 ,
284+ "run_name" :
285+ "run200" ,
286+ "job_parameters" : [{
287+ "name" : "param1" ,
288+ "default" : "default1"
289+ }, {
290+ "name" : "param2" ,
291+ "default" : "default2"
292+ }]
293+ }, {
294+ "run_id" : 300 ,
295+ "run_name" : "run300" ,
296+ }],
297+ "next_page_token" :
298+ "tokenToSecondPage"
299+ }
300+ listruns_page2 = {
301+ "runs" : [{
302+ "run_id" : 400 ,
303+ "run_name" : "run400" ,
304+ "repair_history" : [{
305+ "id" : "repair400_1" ,
306+ }, {
307+ "id" : "repair400_2" ,
308+ }]
309+ }]
310+ }
311+
312+ requests_mock .get (make_listruns_path_pattern ("initialToken" ), text = json .dumps (listruns_page1 ))
313+ requests_mock .get (make_listruns_path_pattern ("tokenToSecondPage" ), text = json .dumps (listruns_page2 ))
314+ w = WorkspaceClient (config = config )
315+
316+ runs_list = list (w .jobs .list_runs (expand_tasks = False , page_token = "initialToken" ))
317+ runs_dict = [run .as_dict () for run in runs_list ]
318+
319+ assert runs_dict == [{
320+ "run_id" : 100 ,
321+ "run_name" : "run100" ,
322+ }, {
323+ "run_id" :
324+ 200 ,
325+ "run_name" :
326+ "run200" ,
327+ "job_parameters" : [{
328+ "name" : "param1" ,
329+ "default" : "default1"
330+ }, {
331+ "name" : "param2" ,
332+ "default" : "default2"
333+ }]
334+ }, {
335+ "run_id" : 300 ,
336+ "run_name" : "run300" ,
337+ }, {
338+ "run_id" : 400 ,
339+ "run_name" : "run400" ,
340+ "repair_history" : [{
341+ "id" : "repair400_1" ,
342+ }, {
343+ "id" : "repair400_2" ,
344+ }]
345+ }]
346+
347+ # only two requests should be made which are jobs/list requests
348+ assert requests_mock .call_count == 2
349+
350+
351+ def test_list_runs (config , requests_mock ):
352+ listruns_page1 = {
353+ "runs" : [{
354+ "run_id" : 100 ,
355+ "tasks" : [{
356+ "task_key" : "taskkey101"
357+ }, {
358+ "task_key" : "taskkey102"
359+ }],
360+ "has_more" : True
361+ }, {
362+ "run_id" : 200 ,
363+ "tasks" : [{
364+ "task_key" : "taskkey201"
365+ }]
366+ }, {
367+ "run_id" : 300 ,
368+ "tasks" : [{
369+ "task_key" : "taskkey301"
370+ }]
371+ }],
372+ "next_page_token" :
373+ "tokenToSecondPage"
374+ }
375+ listruns_page2 = {
376+ "runs" : [{
377+ "run_id" : 400 ,
378+ "tasks" : [{
379+ "task_key" : "taskkey401"
380+ }, {
381+ "task_key" : "taskkey402"
382+ }],
383+ "has_more" : True
384+ }]
385+ }
386+
387+ getrun_100_page1 = {
388+ "run_id" : 100 ,
389+ "tasks" : [{
390+ "task_key" : "taskkey101"
391+ }, {
392+ "task_key" : "taskkey102"
393+ }],
394+ "next_page_token" : "tokenToSecondPage_100"
395+ }
396+ getrun_100_page2 = {"run_id" : 100 , "tasks" : [{"task_key" : "taskkey103" }]}
397+ getrun_400_page1 = {
398+ "run_id" : 400 ,
399+ "tasks" : [{
400+ "task_key" : "taskkey401"
401+ }, {
402+ "task_key" : "taskkey403"
403+ }],
404+ "next_page_token" : "tokenToSecondPage_400"
405+ }
406+ getrun_400_page2 = {"run_id" : 400 , "tasks" : [{"task_key" : "taskkey402" }, {"task_key" : "taskkey404" }]}
407+
408+ requests_mock .get (make_listruns_path_pattern ("initialToken" ), text = json .dumps (listruns_page1 ))
409+ requests_mock .get (make_listruns_path_pattern ("tokenToSecondPage" ), text = json .dumps (listruns_page2 ))
410+
411+ requests_mock .get (make_getrun_path_pattern (100 ), text = json .dumps (getrun_100_page1 ))
412+ requests_mock .get (make_getrun_path_pattern (100 , "tokenToSecondPage_100" ),
413+ text = json .dumps (getrun_100_page2 ))
414+
415+ requests_mock .get (make_getrun_path_pattern (400 ), text = json .dumps (getrun_400_page1 ))
416+ requests_mock .get (make_getrun_path_pattern (400 , "tokenToSecondPage_400" ),
417+ text = json .dumps (getrun_400_page2 ))
418+ w = WorkspaceClient (config = config )
419+
420+ runs_list = list (w .jobs .list_runs (expand_tasks = True , page_token = "initialToken" ))
421+ runs_dict = [run .as_dict () for run in runs_list ]
422+
423+ assert runs_dict == [{
424+ "run_id" :
425+ 100 ,
426+ "tasks" : [{
427+ "task_key" : "taskkey101" ,
428+ }, {
429+ "task_key" : "taskkey102" ,
430+ }, {
431+ "task_key" : "taskkey103" ,
432+ }],
433+ }, {
434+ "run_id" : 200 ,
435+ "tasks" : [{
436+ "task_key" : "taskkey201" ,
437+ }],
438+ }, {
439+ "run_id" : 300 ,
440+ "tasks" : [{
441+ "task_key" : "taskkey301" ,
442+ }],
443+ }, {
444+ "run_id" :
445+ 400 ,
446+ "tasks" : [{
447+ "task_key" : "taskkey401" ,
448+ }, {
449+ "task_key" : "taskkey403" ,
450+ }, {
451+ "task_key" : "taskkey402" ,
452+ }, {
453+ "task_key" : "taskkey404" ,
454+ }],
455+ }]
456+
457+ # check that job_id 200 and 300 was never used in runs/get call
458+ history = requests_mock .request_history
459+ assert all ('300' not in request .qs .get ("run_id" , ['' ]) for request in history )
460+ assert all ('200' not in request .qs .get ("run_id" , ['' ]) for request in history )
0 commit comments