+61
appview/db/pulls.go
+61
appview/db/pulls.go
···
281
return GetPullsWithLimit(e, 0, filters...)
282
}
283
284
+
func GetPullIDs(e Execer, opts models.PullSearchOptions) ([]int64, error) {
285
+
var ids []int64
286
+
287
+
var filters []filter
288
+
filters = append(filters, FilterEq("state", opts.State))
289
+
if opts.RepoAt != "" {
290
+
filters = append(filters, FilterEq("repo_at", opts.RepoAt))
291
+
}
292
+
293
+
var conditions []string
294
+
var args []any
295
+
296
+
for _, filter := range filters {
297
+
conditions = append(conditions, filter.Condition())
298
+
args = append(args, filter.Arg()...)
299
+
}
300
+
301
+
whereClause := ""
302
+
if conditions != nil {
303
+
whereClause = " where " + strings.Join(conditions, " and ")
304
+
}
305
+
pageClause := ""
306
+
if opts.Page.Limit != 0 {
307
+
pageClause = fmt.Sprintf(
308
+
" limit %d offset %d ",
309
+
opts.Page.Limit,
310
+
opts.Page.Offset,
311
+
)
312
+
}
313
+
314
+
query := fmt.Sprintf(
315
+
`
316
+
select
317
+
id
318
+
from
319
+
pulls
320
+
%s
321
+
%s`,
322
+
whereClause,
323
+
pageClause,
324
+
)
325
+
args = append(args, opts.Page.Limit, opts.Page.Offset)
326
+
rows, err := e.Query(query, args...)
327
+
if err != nil {
328
+
return nil, err
329
+
}
330
+
defer rows.Close()
331
+
332
+
for rows.Next() {
333
+
var id int64
334
+
err := rows.Scan(&id)
335
+
if err != nil {
336
+
return nil, err
337
+
}
338
+
339
+
ids = append(ids, id)
340
+
}
341
+
342
+
return ids, nil
343
+
}
344
+
345
func GetPull(e Execer, repoAt syntax.ATURI, pullId int) (*models.Pull, error) {
346
pulls, err := GetPullsWithLimit(e, 1, FilterEq("repo_at", repoAt), FilterEq("pull_id", pullId))
347
if err != nil {
+1
appview/pages/pages.go
+1
appview/pages/pages.go
+7
appview/pages/templates/repo/pulls/pulls.html
+7
appview/pages/templates/repo/pulls/pulls.html
···
31
{{ i "ban" "w-4 h-4" }}
32
<span>{{ .RepoInfo.Stats.PullCount.Closed }} closed</span>
33
</a>
34
+
<form class="flex gap-4" method="GET">
35
+
<input type="hidden" name="state" value="{{ .FilteringBy.String }}">
36
+
<input class="" type="text" name="q" value="{{ .FilterQuery }}">
37
+
<button class="btn" type="submit">
38
+
search
39
+
</button>
40
+
</form>
41
</div>
42
<a
43
href="/{{ .RepoInfo.FullName }}/pulls/new"
+35
-2
appview/pulls/pulls.go
+35
-2
appview/pulls/pulls.go
···
17
"tangled.org/core/api/tangled"
18
"tangled.org/core/appview/config"
19
"tangled.org/core/appview/db"
20
"tangled.org/core/appview/models"
21
"tangled.org/core/appview/notify"
22
"tangled.org/core/appview/oauth"
···
49
enforcer *rbac.Enforcer
50
logger *slog.Logger
51
validator *validator.Validator
52
}
53
54
func New(
···
61
notifier notify.Notifier,
62
enforcer *rbac.Enforcer,
63
validator *validator.Validator,
64
logger *slog.Logger,
65
) *Pulls {
66
return &Pulls{
···
74
enforcer: enforcer,
75
logger: logger,
76
validator: validator,
77
}
78
}
79
···
544
}
545
546
func (s *Pulls) RepoPulls(w http.ResponseWriter, r *http.Request) {
547
user := s.oauth.GetUser(r)
548
params := r.URL.Query()
549
···
561
return
562
}
563
564
pulls, err := db.GetPulls(
565
s.db,
566
-
db.FilterEq("repo_at", f.RepoAt()),
567
-
db.FilterEq("state", state),
568
)
569
if err != nil {
570
log.Println("failed to get pulls", err)
···
651
Pulls: pulls,
652
LabelDefs: defs,
653
FilteringBy: state,
654
Stacks: stacks,
655
Pipelines: m,
656
})
···
17
"tangled.org/core/api/tangled"
18
"tangled.org/core/appview/config"
19
"tangled.org/core/appview/db"
20
+
pulls_indexer "tangled.org/core/appview/indexer/pulls"
21
"tangled.org/core/appview/models"
22
"tangled.org/core/appview/notify"
23
"tangled.org/core/appview/oauth"
···
50
enforcer *rbac.Enforcer
51
logger *slog.Logger
52
validator *validator.Validator
53
+
indexer *pulls_indexer.Indexer
54
}
55
56
func New(
···
63
notifier notify.Notifier,
64
enforcer *rbac.Enforcer,
65
validator *validator.Validator,
66
+
indexer *pulls_indexer.Indexer,
67
logger *slog.Logger,
68
) *Pulls {
69
return &Pulls{
···
77
enforcer: enforcer,
78
logger: logger,
79
validator: validator,
80
+
indexer: indexer,
81
}
82
}
83
···
548
}
549
550
func (s *Pulls) RepoPulls(w http.ResponseWriter, r *http.Request) {
551
+
l := s.logger.With("handler", "RepoPulls")
552
+
553
user := s.oauth.GetUser(r)
554
params := r.URL.Query()
555
···
567
return
568
}
569
570
+
keyword := params.Get("q")
571
+
572
+
var ids []int64
573
+
searchOpts := models.PullSearchOptions{
574
+
Keyword: keyword,
575
+
RepoAt: f.RepoAt().String(),
576
+
State: state,
577
+
// Page: page,
578
+
}
579
+
l.Debug("searching with", "searchOpts", searchOpts)
580
+
if keyword != "" {
581
+
res, err := s.indexer.Search(r.Context(), searchOpts)
582
+
if err != nil {
583
+
l.Error("failed to search for pulls", "err", err)
584
+
return
585
+
}
586
+
ids = res.Hits
587
+
l.Debug("searched pulls with indexer", "count", len(ids))
588
+
} else {
589
+
ids, err = db.GetPullIDs(s.db, searchOpts)
590
+
if err != nil {
591
+
l.Error("failed to get all pull ids", "err", err)
592
+
return
593
+
}
594
+
l.Debug("indexed all pulls from the db", "count", len(ids))
595
+
}
596
+
597
pulls, err := db.GetPulls(
598
s.db,
599
+
db.FilterIn("id", ids),
600
)
601
if err != nil {
602
log.Println("failed to get pulls", err)
···
683
Pulls: pulls,
684
LabelDefs: defs,
685
FilteringBy: state,
686
+
FilterQuery: keyword,
687
Stacks: stacks,
688
Pipelines: m,
689
})