drone/internal/store/database/pullreq_reviewers.go
2023-01-06 20:29:20 +01:00

260 lines
8.0 KiB
Go

// Copyright 2022 Harness Inc. All rights reserved.
// Use of this source code is governed by the Polyform Free Trial License
// that can be found in the LICENSE.md file for this repository.
package database
import (
"context"
"time"
"github.com/harness/gitness/internal/store"
"github.com/harness/gitness/internal/store/database/dbtx"
"github.com/harness/gitness/types"
"github.com/harness/gitness/types/enum"
"github.com/guregu/null"
"github.com/jmoiron/sqlx"
"github.com/pkg/errors"
)
var _ store.PullReqReviewerStore = (*PullReqReviewerStore)(nil)
const maxPullRequestReviewers = 100
// NewPullReqReviewerStore returns a new PullReqReviewerStore.
func NewPullReqReviewerStore(db *sqlx.DB) *PullReqReviewerStore {
return &PullReqReviewerStore{
db: db,
}
}
// PullReqReviewerStore implements store.PullReqReviewerStore backed by a relational database.
type PullReqReviewerStore struct {
db *sqlx.DB
}
// pullReqReviewer is used to fetch pull request reviewer data from the database.
type pullReqReviewer struct {
PullReqID int64 `db:"pullreq_reviewer_pullreq_id"`
PrincipalID int64 `db:"pullreq_reviewer_principal_id"`
CreatedBy int64 `db:"pullreq_reviewer_created_by"`
Created int64 `db:"pullreq_reviewer_created"`
Updated int64 `db:"pullreq_reviewer_updated"`
RepoID int64 `db:"pullreq_reviewer_repo_id"`
Type enum.PullReqReviewerType `db:"pullreq_reviewer_type"`
LatestReviewID null.Int `db:"pullreq_reviewer_latest_review_id"`
ReviewDecision enum.PullReqReviewDecision `db:"pullreq_reviewer_review_decision"`
SHA string `db:"pullreq_reviewer_sha"`
ReviewerUID string `db:"reviewer_uid"`
ReviewerName string `db:"reviewer_name"`
ReviewerEmail string `db:"reviewer_email"`
AddedByUID string `db:"added_by_uid"`
AddedByName string `db:"added_by_name"`
AddedByEmail string `db:"added_by_email"`
}
const (
pullreqReviewerColumns = `
pullreq_reviewer_pullreq_id
,pullreq_reviewer_principal_id
,pullreq_reviewer_created_by
,pullreq_reviewer_created
,pullreq_reviewer_updated
,pullreq_reviewer_repo_id
,pullreq_reviewer_type
,pullreq_reviewer_latest_review_id
,pullreq_reviewer_review_decision
,pullreq_reviewer_sha
,reviewer.principal_uid as "reviewer_uid"
,reviewer.principal_display_name as "reviewer_name"
,reviewer.principal_email as "reviewer_email"
,added_by.principal_uid as "added_by_uid"
,added_by.principal_display_name as "added_by_name"
,added_by.principal_email as "added_by_email"`
pullreqReviewerSelectBase = `
SELECT` + pullreqReviewerColumns + `
FROM pullreq_reviewers
INNER JOIN principals reviewer on reviewer.principal_id = pullreq_reviewer_principal_id
INNER JOIN principals added_by on added_by.principal_id = pullreq_reviewer_created_by`
)
// Find finds the pull request reviewer by pull request id and principal id.
func (s *PullReqReviewerStore) Find(ctx context.Context, prID, principalID int64) (*types.PullReqReviewer, error) {
const sqlQuery = pullreqReviewerSelectBase + `
WHERE pullreq_reviewer_pullreq_id = $1 AND pullreq_reviewer_principal_id = $2`
db := dbtx.GetAccessor(ctx, s.db)
dst := &pullReqReviewer{}
if err := db.GetContext(ctx, dst, sqlQuery, prID, principalID); err != nil {
return nil, processSQLErrorf(err, "Failed to find pull request reviewer")
}
return mapPullReqReviewer(dst), nil
}
// Create creates a new pull request reviewer.
func (s *PullReqReviewerStore) Create(ctx context.Context, v *types.PullReqReviewer) error {
const sqlQuery = `
INSERT INTO pullreq_reviewers (
pullreq_reviewer_pullreq_id
,pullreq_reviewer_principal_id
,pullreq_reviewer_created_by
,pullreq_reviewer_created
,pullreq_reviewer_updated
,pullreq_reviewer_repo_id
,pullreq_reviewer_type
,pullreq_reviewer_latest_review_id
,pullreq_reviewer_review_decision
,pullreq_reviewer_sha
) values (
:pullreq_reviewer_pullreq_id
,:pullreq_reviewer_principal_id
,:pullreq_reviewer_created_by
,:pullreq_reviewer_created
,:pullreq_reviewer_updated
,:pullreq_reviewer_repo_id
,:pullreq_reviewer_type
,:pullreq_reviewer_latest_review_id
,:pullreq_reviewer_review_decision
,:pullreq_reviewer_sha
)`
db := dbtx.GetAccessor(ctx, s.db)
query, arg, err := db.BindNamed(sqlQuery, mapInternalPullReqReviewer(v))
if err != nil {
return processSQLErrorf(err, "Failed to bind pull request reviewer object")
}
if _, err = db.ExecContext(ctx, query, arg...); err != nil {
return processSQLErrorf(err, "Failed to insert pull request reviewer")
}
return nil
}
// Update updates the pull request reviewer.
func (s *PullReqReviewerStore) Update(ctx context.Context, v *types.PullReqReviewer) error {
const sqlQuery = `
UPDATE pullreq_reviewers
SET
pullreq_reviewer_updated = :pullreq_reviewer_updated
,pullreq_reviewer_latest_review_id = :pullreq_reviewer_latest_review_id
,pullreq_reviewer_review_decision = :pullreq_reviewer_review_decision
,pullreq_reviewer_sha = :pullreq_reviewer_sha
WHERE pullreq_reviewer_pullreq_id = :pullreq_reviewer_pullreq_id AND
pullreq_reviewer_principal_id = :pullreq_reviewer_principal_id`
db := dbtx.GetAccessor(ctx, s.db)
updatedAt := time.Now()
dbv := mapInternalPullReqReviewer(v)
dbv.Updated = updatedAt.UnixMilli()
query, arg, err := db.BindNamed(sqlQuery, dbv)
if err != nil {
return processSQLErrorf(err, "Failed to bind pull request activity object")
}
_, err = db.ExecContext(ctx, query, arg...)
if err != nil {
return processSQLErrorf(err, "Failed to update pull request activity")
}
v.Updated = dbv.Updated
return nil
}
// List returns a list of pull reviewers for a pull request.
func (s *PullReqReviewerStore) List(ctx context.Context, prID int64) ([]*types.PullReqReviewer, error) {
stmt := builder.
Select(pullreqReviewerColumns).
From("pullreq_reviewers").
InnerJoin("principals reviewer on reviewer.principal_id = pullreq_reviewer_principal_id").
LeftJoin("principals added_by on added_by.principal_id = pullreq_reviewer_created_by").
Where("pullreq_reviewer_pullreq_id = ?", prID).
OrderBy("pullreq_reviewer_created asc").
Limit(maxPullRequestReviewers) // memory safety limit
sql, args, err := stmt.ToSql()
if err != nil {
return nil, errors.Wrap(err, "Failed to convert pull request reviewer list query to sql")
}
dst := make([]*pullReqReviewer, 0)
db := dbtx.GetAccessor(ctx, s.db)
if err = db.SelectContext(ctx, &dst, sql, args...); err != nil {
return nil, processSQLErrorf(err, "Failed executing pull request reviewer list query")
}
return mapSlicePullReqReviewer(dst), nil
}
func mapPullReqReviewer(v *pullReqReviewer) *types.PullReqReviewer {
m := &types.PullReqReviewer{
PullReqID: v.PullReqID,
PrincipalID: v.PrincipalID,
CreatedBy: v.CreatedBy,
Created: v.Created,
Updated: v.Updated,
RepoID: v.RepoID,
Type: v.Type,
LatestReviewID: v.LatestReviewID.Ptr(),
ReviewDecision: v.ReviewDecision,
SHA: v.SHA,
Reviewer: types.PrincipalInfo{
ID: v.PrincipalID,
UID: v.ReviewerUID,
DisplayName: v.ReviewerName,
Email: v.ReviewerEmail,
},
AddedBy: types.PrincipalInfo{
ID: v.CreatedBy,
UID: v.AddedByUID,
DisplayName: v.AddedByName,
Email: v.AddedByEmail,
},
}
return m
}
func mapInternalPullReqReviewer(v *types.PullReqReviewer) *pullReqReviewer {
m := &pullReqReviewer{
PullReqID: v.PullReqID,
PrincipalID: v.PrincipalID,
CreatedBy: v.CreatedBy,
Created: v.Created,
Updated: v.Updated,
RepoID: v.RepoID,
Type: v.Type,
LatestReviewID: null.IntFromPtr(v.LatestReviewID),
ReviewDecision: v.ReviewDecision,
SHA: v.SHA,
ReviewerUID: "",
ReviewerName: "",
ReviewerEmail: "",
AddedByUID: "",
AddedByName: "",
AddedByEmail: "",
}
return m
}
func mapSlicePullReqReviewer(a []*pullReqReviewer) []*types.PullReqReviewer {
m := make([]*types.PullReqReviewer, len(a))
for i, act := range a {
m[i] = mapPullReqReviewer(act)
}
return m
}