@@ -253,8 +253,19 @@ func (a *API) GetDAGSpec(ctx context.Context, request api.GetDAGSpecRequestObjec
253253 errs = append (errs , dag .BuildWarnings ... )
254254 }
255255
256+ details := toDAGDetails (dag )
257+ if details != nil {
258+ projectionDAG := dag
259+ if dag != nil {
260+ dagCopy := * dag
261+ dagCopy .Name = a .resolveDAGName (ctx , request .FileName )
262+ projectionDAG = & dagCopy
263+ }
264+ details .NextRun = a .projectNextRun (ctx , projectionDAG )
265+ }
266+
256267 return & api.GetDAGSpec200JSONResponse {
257- Dag : toDAGDetails ( dag ) ,
268+ Dag : details ,
258269 Spec : yamlSpec ,
259270 Errors : errs ,
260271 }, nil
@@ -387,6 +398,9 @@ func (a *API) getDAGDetailsData(ctx context.Context, fileName string) (api.GetDA
387398 }
388399
389400 details := toDAGDetails (dag )
401+ if details != nil {
402+ details .NextRun = a .projectNextRun (ctx , dag )
403+ }
390404
391405 localDAGs := make ([]api.LocalDag , 0 , len (dag .LocalDAGs ))
392406 for _ , localDAG := range dag .LocalDAGs {
@@ -1478,19 +1492,7 @@ func (a *API) GetDAGsListData(ctx context.Context, queryString string) (any, err
14781492
14791493func (a * API ) listDAGsData (ctx context.Context , listOpts exec.ListDAGsOptions ) (api.ListDAGs200JSONResponse , error ) {
14801494 projectionTime := time .Now ()
1481- var schedulerState * scheduler.SchedulerState
1482- if a .schedulerStateStore != nil {
1483- state , loadErr := a .schedulerStateStore .Load (ctx )
1484- if loadErr != nil {
1485- logger .Warn (ctx , "Failed to load scheduler state for DAG list projection" , tag .Error (loadErr ))
1486- } else {
1487- schedulerState = state
1488- }
1489- }
1490-
1491- nextRunProjection := func (dag * core.DAG , now time.Time ) time.Time {
1492- return scheduler .NextPlannedRun (dag , now , schedulerState )
1493- }
1495+ nextRunProjection := a .nextRunProjection (ctx )
14941496
14951497 listOpts .Time = & projectionTime
14961498 listOpts .NextRunProjection = nextRunProjection
@@ -1531,6 +1533,30 @@ func (a *API) listDAGsData(ctx context.Context, listOpts exec.ListDAGsOptions) (
15311533 }, nil
15321534}
15331535
1536+ func (a * API ) projectNextRun (ctx context.Context , dag * core.DAG ) * time.Time {
1537+ nextRun := a .nextRunProjection (ctx )(dag , time .Now ())
1538+ if nextRun .IsZero () {
1539+ return nil
1540+ }
1541+ return & nextRun
1542+ }
1543+
1544+ func (a * API ) nextRunProjection (ctx context.Context ) func (* core.DAG , time.Time ) time.Time {
1545+ var schedulerState * scheduler.SchedulerState
1546+ if a .schedulerStateStore != nil {
1547+ state , loadErr := a .schedulerStateStore .Load (ctx )
1548+ if loadErr != nil {
1549+ logger .Warn (ctx , "Failed to load scheduler state for DAG next-run projection" , tag .Error (loadErr ))
1550+ } else {
1551+ schedulerState = state
1552+ }
1553+ }
1554+
1555+ return func (dag * core.DAG , now time.Time ) time.Time {
1556+ return scheduler .NextPlannedRun (dag , now , schedulerState )
1557+ }
1558+ }
1559+
15341560// parseIntParam parses an integer string, returning defaultVal if parsing fails or value is <= 0.
15351561func parseIntParam (s string , defaultVal int ) int {
15361562 if s == "" {
0 commit comments