Monorepo for Tangled tangled.org

appview/pipelines: fix incorrect totals

the default query limits to 30 items, we need a separate query for
total pipeline counts.

Signed-off-by: oppiliappan <me@oppi.li>

oppi.li b0d4690d ef3dbf02

verified
+109 -63
+48 -1
appview/db/pipeline.go
··· 170 170 171 171 // this is a mega query, but the most useful one: 172 172 // get N pipelines, for each one get the latest status of its N workflows 173 + // 174 + // the pipelines table is aliased to `p` 175 + // the triggers table is aliased to `t` 173 176 func GetPipelineStatuses(e Execer, limit int, filters ...orm.Filter) ([]models.Pipeline, error) { 174 177 var conditions []string 175 178 var args []any 176 179 for _, filter := range filters { 177 - filter.Key = "p." + filter.Key // the table is aliased in the query to `p` 178 180 conditions = append(conditions, filter.Condition()) 179 181 args = append(args, filter.Arg()...) 180 182 } ··· 366 368 367 369 return all, nil 368 370 } 371 + 372 + // the pipelines table is aliased to `p` 373 + // the triggers table is aliased to `t` 374 + func GetTotalPipelineStatuses(e Execer, filters ...orm.Filter) (int64, error) { 375 + var conditions []string 376 + var args []any 377 + for _, filter := range filters { 378 + conditions = append(conditions, filter.Condition()) 379 + args = append(args, filter.Arg()...) 380 + } 381 + 382 + whereClause := "" 383 + if conditions != nil { 384 + whereClause = " where " + strings.Join(conditions, " and ") 385 + } 386 + 387 + query := fmt.Sprintf(` 388 + select 389 + count(1) 390 + from 391 + pipelines p 392 + join 393 + triggers t ON p.trigger_id = t.id 394 + %s 395 + `, whereClause) 396 + 397 + rows, err := e.Query(query, args...) 398 + if err != nil { 399 + return 0, err 400 + } 401 + defer rows.Close() 402 + 403 + for rows.Next() { 404 + var count int64 405 + err := rows.Scan(&count) 406 + if err != nil { 407 + return 0, err 408 + } 409 + 410 + return count, nil 411 + } 412 + 413 + // unreachable 414 + return 0, nil 415 + }
+6 -6
appview/pages/pages.go
··· 1348 1348 } 1349 1349 1350 1350 type PipelinesParams struct { 1351 - LoggedInUser *oauth.MultiAccountUser 1352 - RepoInfo repoinfo.RepoInfo 1353 - Pipelines []models.Pipeline 1354 - Active string 1355 - FilteringByPush bool 1356 - FilteringByPR bool 1351 + LoggedInUser *oauth.MultiAccountUser 1352 + RepoInfo repoinfo.RepoInfo 1353 + Pipelines []models.Pipeline 1354 + Active string 1355 + FilterKind string 1356 + Total int64 1357 1357 } 1358 1358 1359 1359 func (p *Pages) Pipelines(w io.Writer, params PipelinesParams) error {
+5 -10
appview/pages/templates/repo/pipelines/pipelines.html
··· 7 7 {{ end }} 8 8 9 9 {{ define "repoContent" }} 10 - {{ $active := "all" }} 11 - {{ if .FilteringByPush }} 12 - {{ $active = "push" }} 13 - {{ else if .FilteringByPR }} 14 - {{ $active = "pr" }} 15 - {{ end }} 10 + {{ $active := .FilterKind }} 16 11 17 12 {{ $all := 18 13 (dict ··· 28 23 "Meta" "") }} 29 24 {{ $pr := 30 25 (dict 31 - "Key" "pr" 26 + "Key" "pull_request" 32 27 "Value" "pull request" 33 28 "Icon" "git-pull-request" 34 29 "Meta" "") }} ··· 36 31 37 32 <div class="flex justify-between items-center gap-4"> 38 33 <div> 39 - {{ template "fragments/tabSelector" (dict "Name" "trigger" "Values" $values "Active" $active) }} 34 + {{ template "fragments/tabSelector" (dict "Name" "trigger" "Values" $values "Active" .FilterKind) }} 40 35 </div> 41 36 <div class="text-sm text-gray-600 dark:text-gray-400"> 42 - {{ len .Pipelines }} pipeline run{{ if ne (len .Pipelines) 1 }}s{{ end }} 37 + {{ .Total }} pipeline run{{ if ne .Total 1 }}s{{ end }} 43 38 </div> 44 39 </div> 45 40 {{ end }} ··· 52 47 {{ end }} 53 48 </div> 54 49 {{ else }} 55 - <div class="mt-2 py-12 flex flex-col items-center justify-center gap-6 text-center border border-gray-200 dark:border-gray-700 rounded bg-gray-50 dark:bg-gray-900"> 50 + <div class="mt-2 py-12 flex flex-col items-center justify-center gap-6 text-center border border-gray-200 dark:border-gray-700 rounded bg-white dark:bg-gray-800"> 56 51 <div class="flex justify-center"> 57 52 {{ i "package" "size-16 text-gray-300 dark:text-gray-700" }} 58 53 </div>
+38 -34
appview/pipelines/pipelines.go
··· 86 86 return 87 87 } 88 88 89 + filterKind := r.URL.Query().Get("trigger") 90 + filters := []orm.Filter{ 91 + orm.FilterEq("p.repo_owner", f.Did), 92 + orm.FilterEq("p.repo_name", f.Name), 93 + orm.FilterEq("p.knot", f.Knot), 94 + } 95 + switch filterKind { 96 + case "push": 97 + filters = append(filters, orm.FilterEq("t.kind", "push")) 98 + case "pull_request": 99 + filters = append(filters, orm.FilterEq("t.kind", "pull_request")) 100 + default: 101 + // no filters otherwise, default to "all" 102 + filterKind = "all" 103 + } 104 + 89 105 ps, err := db.GetPipelineStatuses( 90 106 p.db, 91 107 30, 92 - orm.FilterEq("repo_owner", f.Did), 93 - orm.FilterEq("repo_name", f.Name), 94 - orm.FilterEq("knot", f.Knot), 108 + filters..., 95 109 ) 96 110 if err != nil { 97 111 l.Error("failed to query db", "err", err) 98 112 return 99 113 } 100 114 101 - // Filter by trigger 102 - filterTrigger := r.URL.Query().Get("trigger") 103 - var filtered []models.Pipeline 104 - for _, pipeline := range ps { 105 - if filterTrigger == "push" && pipeline.Trigger != nil && pipeline.Trigger.IsPush() { 106 - filtered = append(filtered, pipeline) 107 - } else if filterTrigger == "pr" && pipeline.Trigger != nil && pipeline.Trigger.IsPullRequest() { 108 - filtered = append(filtered, pipeline) 109 - } else if filterTrigger == "" || filterTrigger == "all" { 110 - filtered = append(filtered, pipeline) 111 - } 115 + total, err := db.GetTotalPipelineStatuses(p.db, filters...) 116 + if err != nil { 117 + l.Error("failed to query db", "err", err) 118 + return 112 119 } 113 120 114 - filteringByPush := filterTrigger == "push" 115 - filteringByPR := filterTrigger == "pr" 116 - 117 121 p.pages.Pipelines(w, pages.PipelinesParams{ 118 - LoggedInUser: user, 119 - RepoInfo: p.repoResolver.GetRepoInfo(r, user), 120 - Pipelines: filtered, 121 - FilteringByPush: filteringByPush, 122 - FilteringByPR: filteringByPR, 122 + LoggedInUser: user, 123 + RepoInfo: p.repoResolver.GetRepoInfo(r, user), 124 + Pipelines: ps, 125 + FilterKind: filterKind, 126 + Total: total, 123 127 }) 124 128 } 125 129 ··· 148 152 ps, err := db.GetPipelineStatuses( 149 153 p.db, 150 154 1, 151 - orm.FilterEq("repo_owner", f.Did), 152 - orm.FilterEq("repo_name", f.Name), 153 - orm.FilterEq("knot", f.Knot), 154 - orm.FilterEq("id", pipelineId), 155 + orm.FilterEq("p.repo_owner", f.Did), 156 + orm.FilterEq("p.repo_name", f.Name), 157 + orm.FilterEq("p.knot", f.Knot), 158 + orm.FilterEq("p.id", pipelineId), 155 159 ) 156 160 if err != nil { 157 161 l.Error("failed to query db", "err", err) ··· 215 219 ps, err := db.GetPipelineStatuses( 216 220 p.db, 217 221 1, 218 - orm.FilterEq("repo_owner", f.Did), 219 - orm.FilterEq("repo_name", f.Name), 220 - orm.FilterEq("knot", f.Knot), 221 - orm.FilterEq("id", pipelineId), 222 + orm.FilterEq("p.repo_owner", f.Did), 223 + orm.FilterEq("p.repo_name", f.Name), 224 + orm.FilterEq("p.knot", f.Knot), 225 + orm.FilterEq("p.id", pipelineId), 222 226 ) 223 227 if err != nil || len(ps) != 1 { 224 228 l.Error("pipeline query failed", "err", err, "count", len(ps)) ··· 364 368 ps, err := db.GetPipelineStatuses( 365 369 p.db, 366 370 1, 367 - orm.FilterEq("repo_owner", f.Did), 368 - orm.FilterEq("repo_name", f.Name), 369 - orm.FilterEq("knot", f.Knot), 370 - orm.FilterEq("id", pipelineId), 371 + orm.FilterEq("p.repo_owner", f.Did), 372 + orm.FilterEq("p.repo_name", f.Name), 373 + orm.FilterEq("p.knot", f.Knot), 374 + orm.FilterEq("p.id", pipelineId), 371 375 ) 372 376 if err != nil { 373 377 return models.Pipeline{}, err
+8 -8
appview/pulls/pulls.go
··· 214 214 ps, err := db.GetPipelineStatuses( 215 215 s.db, 216 216 len(shas), 217 - orm.FilterEq("repo_owner", f.Did), 218 - orm.FilterEq("repo_name", f.Name), 219 - orm.FilterEq("knot", f.Knot), 220 - orm.FilterIn("sha", shas), 217 + orm.FilterEq("p.repo_owner", f.Did), 218 + orm.FilterEq("p.repo_name", f.Name), 219 + orm.FilterEq("p.knot", f.Knot), 220 + orm.FilterIn("p.sha", shas), 221 221 ) 222 222 if err != nil { 223 223 log.Printf("failed to fetch pipeline statuses: %s", err) ··· 636 636 ps, err := db.GetPipelineStatuses( 637 637 s.db, 638 638 len(shas), 639 - orm.FilterEq("repo_owner", f.Did), 640 - orm.FilterEq("repo_name", f.Name), 641 - orm.FilterEq("knot", f.Knot), 642 - orm.FilterIn("sha", shas), 639 + orm.FilterEq("p.repo_owner", f.Did), 640 + orm.FilterEq("p.repo_name", f.Name), 641 + orm.FilterEq("p.knot", f.Knot), 642 + orm.FilterIn("p.sha", shas), 643 643 ) 644 644 if err != nil { 645 645 log.Printf("failed to fetch pipeline statuses: %s", err)
+4 -4
appview/repo/repo_util.go
··· 103 103 ps, err := db.GetPipelineStatuses( 104 104 d, 105 105 len(shas), 106 - orm.FilterEq("repo_owner", repo.Did), 107 - orm.FilterEq("repo_name", repo.Name), 108 - orm.FilterEq("knot", repo.Knot), 109 - orm.FilterIn("sha", shas), 106 + orm.FilterEq("p.repo_owner", repo.Did), 107 + orm.FilterEq("p.repo_name", repo.Name), 108 + orm.FilterEq("p.knot", repo.Knot), 109 + orm.FilterIn("p.sha", shas), 110 110 ) 111 111 if err != nil { 112 112 return nil, err