Documentation
¶
Overview ¶
Package store owns the database schema, GORM models, repositories, and the goose-driven migration runner. Callers depend on small interfaces declared where they consume the store (handler packages); only this package needs to know about GORM.
Index ¶
- Variables
- func MigrateDown(ctx context.Context, db *gorm.DB) error
- func MigrateStatus(ctx context.Context, db *gorm.DB) (string, error)
- func MigrateUp(ctx context.Context, db *gorm.DB) error
- func NewTestDB(t *testing.T) *gorm.DB
- func Open(url string) (*gorm.DB, error)
- func RawCreateForTest(db *gorm.DB, pr PullRequest) error
- func SQLDB(db *gorm.DB) (*sql.DB, error)
- type CodeReview
- type CodeReviews
- func (r *CodeReviews) ActiveForUser(ctx context.Context, repository string, prNumber int, slackUserID string) (CodeReview, error)
- func (r *CodeReviews) Finish(ctx context.Context, repository string, prNumber int) error
- func (r *CodeReviews) GetActive(ctx context.Context, repository string, prNumber int) (CodeReview, error)
- func (r *CodeReviews) Reviewers(ctx context.Context, repository string, prNumber int) ([]CodeReview, error)
- func (r *CodeReviews) Start(ctx context.Context, repository string, prNumber int, ...) error
- type Message
- type PullRequest
- type PullRequests
- func (r *PullRequests) AddMessage(ctx context.Context, repository string, prNumber int, ...) error
- func (r *PullRequests) Delete(ctx context.Context, repository string, prNumber int) error
- func (r *PullRequests) DeleteStaleBefore(ctx context.Context, cutoff time.Time) (int64, error)
- func (r *PullRequests) FindStuck(ctx context.Context, cutoff time.Time) ([]PullRequest, error)
- func (r *PullRequests) ListOpen(ctx context.Context) ([]PullRequest, error)
- func (r *PullRequests) MarkClosed(ctx context.Context, repository string, prNumber int) error
- func (r *PullRequests) Messages(ctx context.Context, repository string, prNumber int) ([]Message, error)
- func (r *PullRequests) Touch(ctx context.Context, repository string, prNumber int) error
- type Reactions
- type RepoMapping
- type Target
Constants ¶
This section is empty.
Variables ¶
var ErrActiveReviewExists = errors.New("store: active code review exists")
ErrActiveReviewExists is returned by Start when the same user already has an active review on the PR — the partial unique index rejected the insert. Callers surface the conflict UX ("already reviewing") instead of a 500.
var ErrNotFound = routingdomain.ErrNotFound
ErrNotFound aliases the routing domain sentinel so all existing callers keep the same value.
Functions ¶
func MigrateDown ¶
MigrateDown rolls back the most recent migration.
func MigrateStatus ¶
MigrateStatus returns a human-readable list of each migration and whether it has been applied.
func NewTestDB ¶
NewTestDB returns a *gorm.DB backed by a fresh on-disk SQLite database inside t.TempDir, with all migrations applied. The database is closed and removed automatically when the test completes.
We use an on-disk file rather than `:memory:` because the goose-sqlite driver expects a stable database name to record migration state, and multiple in-memory connections in the same process are not always shared.
func Open ¶
Open opens a SQLite database for production use. The url accepts either a raw file path or the "file:..." DSN form used in our config defaults.
GORM's logger is silenced for "record not found" because we treat ErrNotFound as a normal return value, not a warning condition.
func RawCreateForTest ¶
func RawCreateForTest(db *gorm.DB, pr PullRequest) error
RawCreateForTest inserts a pull_requests row preserving the caller's CreatedAt/UpdatedAt/ClosedAt, bypassing GORM's autoCreate/UpdateTime. Used by tests that need to seed PRs with a controlled age and open/closed state. A zero CreatedAt defaults to UpdatedAt.
Types ¶
type CodeReview ¶
type CodeReview struct {
ID uint `gorm:"primaryKey"`
PullRequestID uint `gorm:"column:pull_request_id;not null"`
SlackUserID string `gorm:"column:slack_user_id;not null"`
SlackUserName string `gorm:"column:slack_user_name"`
StartedAt time.Time `gorm:"column:started_at;not null"`
FinishedAt *time.Time `gorm:"column:finished_at"`
}
CodeReview is one review "session" for a PR: who started reviewing it and when, with FinishedAt nil while the review is in progress and set once the reviewer's GitHub review lands. It hangs off a PullRequest (cascade-deleted with it) and a partial unique index enforces at most one active (FinishedAt IS NULL) review per PR — finished rows accumulate as history.
type CodeReviews ¶
type CodeReviews struct {
// contains filtered or unexported fields
}
CodeReviews persists per-PR review sessions and enforces at most one active review per (PR, user); multiple distinct users may review a PR concurrently. A review is identified to callers by its PR's natural key (repository, prNumber); the surrogate pull_request_id is resolved internally.
func NewCodeReviews ¶
func NewCodeReviews(db *gorm.DB) *CodeReviews
NewCodeReviews constructs a CodeReviews repository bound to db.
func (*CodeReviews) ActiveForUser ¶
func (r *CodeReviews) ActiveForUser(ctx context.Context, repository string, prNumber int, slackUserID string) (CodeReview, error)
ActiveForUser returns the user's active (unfinished) review on the PR, or ErrNotFound when that user has no review in progress here. It is the app-level guard the click handler checks before Start; the DB's partial unique index on (pull_request_id, slack_user_id) is the race-safe backstop.
func (*CodeReviews) Finish ¶
Finish marks the PR's active review finished. It is idempotent: no active review (or an untracked PR) is a no-op, mirroring MarkClosed.
func (*CodeReviews) GetActive ¶
func (r *CodeReviews) GetActive(ctx context.Context, repository string, prNumber int) (CodeReview, error)
GetActive returns the PR's active (unfinished) review, or ErrNotFound when the PR is untracked or has no review in progress.
func (*CodeReviews) Reviewers ¶
func (r *CodeReviews) Reviewers(ctx context.Context, repository string, prNumber int) ([]CodeReview, error)
Reviewers returns all code-review sessions for the PR ordered by started_at ascending (earliest first). An untracked PR or a PR with no reviews returns an empty slice and nil error.
func (*CodeReviews) Start ¶
func (r *CodeReviews) Start(ctx context.Context, repository string, prNumber int, slackUserID, slackUserName string) error
Start opens a review on the PR for the given Slack user. It returns ErrNotFound when the PR is not tracked, and ErrActiveReviewExists when the PR already has an active review — the DB's partial unique index is the source of truth, so two near-simultaneous Starts can't both win.
type Message ¶
type Message struct {
ID uint `gorm:"primaryKey"`
PullRequestID uint `gorm:"column:pull_request_id;not null"`
Channel string `gorm:"column:channel;not null"`
MessageID string `gorm:"column:message_id;not null"`
}
Message is one posted messenger message for a PR. (PullRequestID, Channel) is unique — at most one message per channel per PR. Channel is a room in the messenger; MessageID is the messenger's id for the post (Slack's ts).
type PullRequest ¶
type PullRequest struct {
ID uint `gorm:"primaryKey"`
Repository string `gorm:"column:gh_repository;not null"`
PRNumber int `gorm:"column:pr_number;not null"`
CreatedAt time.Time `gorm:"column:created_at;not null"`
UpdatedAt time.Time `gorm:"column:updated_at;not null"`
ClosedAt *time.Time `gorm:"column:closed_at"`
Messages []Message `gorm:"foreignKey:PullRequestID;constraint:OnDelete:CASCADE"`
}
PullRequest is one tracked PR. (Repository, PRNumber) is the natural key; CreatedAt is kept for later statistics, UpdatedAt is the activity clock (bumped on open and every review/comment) driving digest idle-detection and cleanup, and ClosedAt (nil = open) marks merged/closed so the digest skips it.
func (PullRequest) TableName ¶
func (PullRequest) TableName() string
TableName pins the table name; do not rely on GORM pluralization.
type PullRequests ¶
type PullRequests struct {
// contains filtered or unexported fields
}
PullRequests persists tracked PRs and their per-channel messenger messages.
func NewPullRequests ¶
func NewPullRequests(db *gorm.DB) *PullRequests
NewPullRequests constructs a PullRequests repository bound to db.
func (*PullRequests) AddMessage ¶
func (r *PullRequests) AddMessage(ctx context.Context, repository string, prNumber int, channel, messageID string) error
AddMessage records one posted message, creating the PR row on first sight. Insertion is idempotent on (pull_request_id, channel): re-adding the same channel for the same PR is a no-op, which makes the open fan-out safe to replay after a partial failure or GitHub redelivery.
func (*PullRequests) Delete ¶
Delete removes the PR and (by cascade) its messages. Missing PR is a no-op.
func (*PullRequests) DeleteStaleBefore ¶
DeleteStaleBefore removes PRs idle since before cutoff (messages cascade).
func (*PullRequests) FindStuck ¶
func (r *PullRequests) FindStuck(ctx context.Context, cutoff time.Time) ([]PullRequest, error)
FindStuck returns open PRs idle since before cutoff, messages preloaded, oldest first.
func (*PullRequests) ListOpen ¶
func (r *PullRequests) ListOpen(ctx context.Context) ([]PullRequest, error)
ListOpen returns every not-yet-closed PR, ordered for stable output.
func (*PullRequests) MarkClosed ¶
MarkClosed sets closed_at. Missing PR is a no-op.
type Reactions ¶
type Reactions = routingdomain.Reactions
Reactions aliases the routing domain value object during the migration.
type RepoMapping ¶
type RepoMapping = routingdomain.RepoMapping
RepoMapping aliases the routing domain value object during the migration.
type Target ¶
type Target = routingdomain.Target
Target aliases the routing domain value object during the migration.