drone/app/store/database/label_pullreq.go

298 lines
8.5 KiB
Go

// Copyright 2023 Harness, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package database
import (
"context"
"github.com/harness/gitness/app/store"
"github.com/harness/gitness/store/database"
"github.com/harness/gitness/store/database/dbtx"
"github.com/harness/gitness/types"
"github.com/harness/gitness/types/enum"
"github.com/Masterminds/squirrel"
"github.com/gotidy/ptr"
"github.com/guregu/null"
"github.com/jmoiron/sqlx"
"github.com/pkg/errors"
)
var _ store.PullReqLabelAssignmentStore = (*pullReqLabelStore)(nil)
func NewPullReqLabelStore(db *sqlx.DB) store.PullReqLabelAssignmentStore {
return &pullReqLabelStore{
db: db,
}
}
type pullReqLabelStore struct {
db *sqlx.DB
}
type pullReqLabel struct {
PullReqID int64 `db:"pullreq_label_pullreq_id"`
LabelID int64 `db:"pullreq_label_label_id"`
LabelValueID null.Int `db:"pullreq_label_label_value_id"`
Created int64 `db:"pullreq_label_created"`
Updated int64 `db:"pullreq_label_updated"`
CreatedBy int64 `db:"pullreq_label_created_by"`
UpdatedBy int64 `db:"pullreq_label_updated_by"`
}
type pullReqAssignmentInfo struct {
PullReqID int64 `db:"pullreq_label_pullreq_id"`
LabelID int64 `db:"label_id"`
LabelKey string `db:"label_key"`
LabelColor enum.LabelColor `db:"label_color"`
LabelScope int64 `db:"label_scope"`
ValueCount int64 `db:"label_value_count"`
ValueID null.Int `db:"label_value_id"`
Value null.String `db:"label_value_value"`
ValueColor null.String `db:"label_value_color"` // get's converted to *enum.LabelColor
}
const (
pullReqLabelColumns = `
pullreq_label_pullreq_id
,pullreq_label_label_id
,pullreq_label_label_value_id
,pullreq_label_created
,pullreq_label_updated
,pullreq_label_created_by
,pullreq_label_updated_by`
)
func (s *pullReqLabelStore) Assign(ctx context.Context, label *types.PullReqLabel) error {
const sqlQuery = `
INSERT INTO pullreq_labels (` + pullReqLabelColumns + `)
values (
:pullreq_label_pullreq_id
,:pullreq_label_label_id
,:pullreq_label_label_value_id
,:pullreq_label_created
,:pullreq_label_updated
,:pullreq_label_created_by
,:pullreq_label_updated_by
)
ON CONFLICT (pullreq_label_pullreq_id, pullreq_label_label_id)
DO UPDATE SET
pullreq_label_label_value_id = EXCLUDED.pullreq_label_label_value_id,
pullreq_label_updated = EXCLUDED.pullreq_label_updated,
pullreq_label_updated_by = EXCLUDED.pullreq_label_updated_by
RETURNING pullreq_label_created, pullreq_label_created_by
`
db := dbtx.GetAccessor(ctx, s.db)
query, args, err := db.BindNamed(sqlQuery, mapInternalPullReqLabel(label))
if err != nil {
return database.ProcessSQLErrorf(ctx, err, "failed to bind query")
}
if err = db.QueryRowContext(ctx, query, args...).Scan(&label.Created, &label.CreatedBy); err != nil {
return database.ProcessSQLErrorf(ctx, err, "failed to create pull request label")
}
return nil
}
func (s *pullReqLabelStore) Unassign(ctx context.Context, pullreqID int64, labelID int64) error {
const sqlQuery = `
DELETE FROM pullreq_labels
WHERE pullreq_label_pullreq_id = $1 AND pullreq_label_label_id = $2`
db := dbtx.GetAccessor(ctx, s.db)
if _, err := db.ExecContext(ctx, sqlQuery, pullreqID, labelID); err != nil {
return database.ProcessSQLErrorf(ctx, err, "failed to delete pullreq label")
}
return nil
}
func (s *pullReqLabelStore) FindByLabelID(
ctx context.Context,
pullreqID int64,
labelID int64,
) (*types.PullReqLabel, error) {
const sqlQuery = `SELECT ` + pullReqLabelColumns + `
FROM pullreq_labels
WHERE pullreq_label_pullreq_id = $1 AND pullreq_label_label_id = $2`
db := dbtx.GetAccessor(ctx, s.db)
var dst pullReqLabel
if err := db.GetContext(ctx, &dst, sqlQuery, pullreqID, labelID); err != nil {
return nil, database.ProcessSQLErrorf(ctx, err, "failed to find pullreq label by id")
}
return mapPullReqLabel(&dst), nil
}
func (s *pullReqLabelStore) ListAssigned(
ctx context.Context,
pullreqID int64,
) (map[int64]*types.LabelAssignment, error) {
const sqlQuery = `
SELECT
label_id
,label_repo_id
,label_space_id
,label_key
,label_value_id
,label_value_label_id
,label_value_value
,label_color
,label_value_color
,label_scope
,label_type
FROM pullreq_labels
INNER JOIN labels ON pullreq_label_label_id = label_id
LEFT JOIN label_values ON pullreq_label_label_value_id = label_value_id
WHERE pullreq_label_pullreq_id = $1`
db := dbtx.GetAccessor(ctx, s.db)
var dst []*struct {
labelInfo
labelValueInfo
}
if err := db.SelectContext(ctx, &dst, sqlQuery, pullreqID); err != nil {
return nil, database.ProcessSQLErrorf(ctx, err, "failed to list assigned label")
}
ret := make(map[int64]*types.LabelAssignment, len(dst))
for _, res := range dst {
li := mapLabelInfo(&res.labelInfo)
lvi := mapLabeValuelInfo(&res.labelValueInfo)
ret[li.ID] = &types.LabelAssignment{
LabelInfo: *li,
AssignedValue: lvi,
}
}
return ret, nil
}
func (s *pullReqLabelStore) ListAssignedByPullreqIDs(
ctx context.Context,
pullreqIDs []int64,
) (map[int64][]*types.LabelPullReqAssignmentInfo, error) {
stmt := database.Builder.Select(`
pullreq_label_pullreq_id
,label_id
,label_key
,label_color
,label_scope
,label_value_count
,label_value_id
,label_value_value
,label_value_color
`).
From("pullreq_labels").
InnerJoin("labels ON pullreq_label_label_id = label_id").
LeftJoin("label_values ON pullreq_label_label_value_id = label_value_id").
Where(squirrel.Eq{"pullreq_label_pullreq_id": pullreqIDs})
sql, args, err := stmt.ToSql()
if err != nil {
return nil, errors.Wrap(err, "Failed to convert query to sql")
}
db := dbtx.GetAccessor(ctx, s.db)
var dst []*pullReqAssignmentInfo
if err := db.SelectContext(ctx, &dst, sql, args...); err != nil {
return nil, database.ProcessSQLErrorf(ctx, err, "failed to list assigned label")
}
return mapPullReqAssignmentInfos(dst), nil
}
func (s *pullReqLabelStore) FindValueByLabelID(
ctx context.Context,
pullreqID int64,
labelID int64,
) (*types.LabelValue, error) {
const sqlQuery = `SELECT label_value_id, ` + labelValueColumns + `
FROM pullreq_labels
JOIN label_values ON pullreq_label_label_value_id = label_value_id
WHERE pullreq_label_pullreq_id = $1 AND pullreq_label_label_id = $2`
db := dbtx.GetAccessor(ctx, s.db)
var dst labelValue
if err := db.GetContext(ctx, &dst, sqlQuery, pullreqID, labelID); err != nil {
return nil, database.ProcessSQLErrorf(ctx, err, "Failed to find label")
}
return mapLabelValue(&dst), nil
}
func mapInternalPullReqLabel(lbl *types.PullReqLabel) *pullReqLabel {
return &pullReqLabel{
PullReqID: lbl.PullReqID,
LabelID: lbl.LabelID,
LabelValueID: null.IntFromPtr(lbl.ValueID),
Created: lbl.Created,
Updated: lbl.Updated,
CreatedBy: lbl.CreatedBy,
UpdatedBy: lbl.UpdatedBy,
}
}
func mapPullReqLabel(lbl *pullReqLabel) *types.PullReqLabel {
return &types.PullReqLabel{
PullReqID: lbl.PullReqID,
LabelID: lbl.LabelID,
ValueID: lbl.LabelValueID.Ptr(),
Created: lbl.Created,
Updated: lbl.Updated,
CreatedBy: lbl.CreatedBy,
UpdatedBy: lbl.UpdatedBy,
}
}
func mapPullReqAssignmentInfo(lbl *pullReqAssignmentInfo) *types.LabelPullReqAssignmentInfo {
var valueColor *enum.LabelColor
if lbl.ValueColor.Valid {
valueColor = ptr.Of(enum.LabelColor(lbl.ValueColor.String))
}
return &types.LabelPullReqAssignmentInfo{
PullReqID: lbl.PullReqID,
LabelID: lbl.LabelID,
LabelKey: lbl.LabelKey,
LabelColor: lbl.LabelColor,
LabelScope: lbl.LabelScope,
ValueCount: lbl.ValueCount,
ValueID: lbl.ValueID.Ptr(),
Value: lbl.Value.Ptr(),
ValueColor: valueColor,
}
}
func mapPullReqAssignmentInfos(
dbLabels []*pullReqAssignmentInfo,
) map[int64][]*types.LabelPullReqAssignmentInfo {
result := make(map[int64][]*types.LabelPullReqAssignmentInfo)
for _, lbl := range dbLabels {
result[lbl.PullReqID] = append(result[lbl.PullReqID], mapPullReqAssignmentInfo(lbl))
}
return result
}