@@ -66,6 +66,17 @@ func (q *Queries) AssignUserToIssue(ctx context.Context, db DBTX, arg AssignUser |
| 66 | 66 | return err |
| 67 | 67 | } |
| 68 | 68 | |
| 69 | +const countIssueEvents = `-- name: CountIssueEvents :one |
| 70 | +SELECT COUNT(*) FROM issue_events WHERE issue_id = $1 |
| 71 | +` |
| 72 | + |
| 73 | +func (q *Queries) CountIssueEvents(ctx context.Context, db DBTX, issueID int64) (int64, error) { |
| 74 | + row := db.QueryRow(ctx, countIssueEvents, issueID) |
| 75 | + var count int64 |
| 76 | + err := row.Scan(&count) |
| 77 | + return count, err |
| 78 | +} |
| 79 | + |
| 69 | 80 | const countIssues = `-- name: CountIssues :one |
| 70 | 81 | SELECT count(*)::bigint FROM issues |
| 71 | 82 | WHERE repo_id = $1 |
@@ -598,6 +609,67 @@ func (q *Queries) ListIssueEvents(ctx context.Context, db DBTX, issueID int64) ( |
| 598 | 609 | return items, nil |
| 599 | 610 | } |
| 600 | 611 | |
| 612 | +const listIssueEventsWithActor = `-- name: ListIssueEventsWithActor :many |
| 613 | +SELECT e.id, e.issue_id, e.actor_user_id, e.kind, e.meta, e.ref_target_id, |
| 614 | + e.created_at, u.username AS actor_username |
| 615 | +FROM issue_events e |
| 616 | +LEFT JOIN users u ON u.id = e.actor_user_id |
| 617 | +WHERE e.issue_id = $1 |
| 618 | +ORDER BY e.created_at ASC, e.id ASC |
| 619 | +LIMIT $2 OFFSET $3 |
| 620 | +` |
| 621 | + |
| 622 | +type ListIssueEventsWithActorParams struct { |
| 623 | + IssueID int64 |
| 624 | + Limit int32 |
| 625 | + Offset int32 |
| 626 | +} |
| 627 | + |
| 628 | +type ListIssueEventsWithActorRow struct { |
| 629 | + ID int64 |
| 630 | + IssueID int64 |
| 631 | + ActorUserID pgtype.Int8 |
| 632 | + Kind string |
| 633 | + Meta []byte |
| 634 | + RefTargetID pgtype.Int8 |
| 635 | + CreatedAt pgtype.Timestamptz |
| 636 | + ActorUsername pgtype.Text |
| 637 | +} |
| 638 | + |
| 639 | +// Paginated timeline shape for the REST `/issues/{n}/events` endpoint: |
| 640 | +// the same event rows ListIssueEvents returns, but LEFT-joined to users |
| 641 | +// so the response can carry `actor_username` without a second round-trip. |
| 642 | +// Suspended/deleted actor rows still appear (the timeline is historical |
| 643 | +// truth), with NULL username when the user row is unrecoverable. |
| 644 | +func (q *Queries) ListIssueEventsWithActor(ctx context.Context, db DBTX, arg ListIssueEventsWithActorParams) ([]ListIssueEventsWithActorRow, error) { |
| 645 | + rows, err := db.Query(ctx, listIssueEventsWithActor, arg.IssueID, arg.Limit, arg.Offset) |
| 646 | + if err != nil { |
| 647 | + return nil, err |
| 648 | + } |
| 649 | + defer rows.Close() |
| 650 | + items := []ListIssueEventsWithActorRow{} |
| 651 | + for rows.Next() { |
| 652 | + var i ListIssueEventsWithActorRow |
| 653 | + if err := rows.Scan( |
| 654 | + &i.ID, |
| 655 | + &i.IssueID, |
| 656 | + &i.ActorUserID, |
| 657 | + &i.Kind, |
| 658 | + &i.Meta, |
| 659 | + &i.RefTargetID, |
| 660 | + &i.CreatedAt, |
| 661 | + &i.ActorUsername, |
| 662 | + ); err != nil { |
| 663 | + return nil, err |
| 664 | + } |
| 665 | + items = append(items, i) |
| 666 | + } |
| 667 | + if err := rows.Err(); err != nil { |
| 668 | + return nil, err |
| 669 | + } |
| 670 | + return items, nil |
| 671 | +} |
| 672 | + |
| 601 | 673 | const listIssues = `-- name: ListIssues :many |
| 602 | 674 | SELECT id, repo_id, number, kind, title, body, body_html_cached, md_pipeline_version, author_user_id, state, state_reason, locked, lock_reason, milestone_id, created_at, updated_at, edited_at, closed_at, closed_by_user_id FROM issues |
| 603 | 675 | WHERE repo_id = $1 |