mirror of https://github.com/harness/drone.git
326 lines
8.4 KiB
Go
326 lines
8.4 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/guregu/null"
|
|
"github.com/jmoiron/sqlx"
|
|
"github.com/pkg/errors"
|
|
)
|
|
|
|
const (
|
|
labelValueColumns = `
|
|
label_value_label_id
|
|
,label_value_value
|
|
,label_value_color
|
|
,label_value_created
|
|
,label_value_updated
|
|
,label_value_created_by
|
|
,label_value_updated_by`
|
|
|
|
labelValueSelectBase = `SELECT label_value_id, ` + labelValueColumns + ` FROM label_values`
|
|
)
|
|
|
|
type labelValue struct {
|
|
ID int64 `db:"label_value_id"`
|
|
LabelID int64 `db:"label_value_label_id"`
|
|
Value string `db:"label_value_value"`
|
|
Color enum.LabelColor `db:"label_value_color"`
|
|
Created int64 `db:"label_value_created"`
|
|
Updated int64 `db:"label_value_updated"`
|
|
CreatedBy int64 `db:"label_value_created_by"`
|
|
UpdatedBy int64 `db:"label_value_updated_by"`
|
|
}
|
|
|
|
type labelValueInfo struct {
|
|
ValueID null.Int `db:"label_value_id"`
|
|
LabelID null.Int `db:"label_value_label_id"`
|
|
Value null.String `db:"label_value_value"`
|
|
ValueColor null.String `db:"label_value_color"`
|
|
}
|
|
|
|
type labelValueStore struct {
|
|
db *sqlx.DB
|
|
}
|
|
|
|
func NewLabelValueStore(
|
|
db *sqlx.DB,
|
|
) store.LabelValueStore {
|
|
return &labelValueStore{
|
|
db: db,
|
|
}
|
|
}
|
|
|
|
var _ store.LabelValueStore = (*labelValueStore)(nil)
|
|
|
|
func (s *labelValueStore) Define(ctx context.Context, lblVal *types.LabelValue) error {
|
|
const sqlQuery = `
|
|
INSERT INTO label_values (` + labelValueColumns + `)` + `
|
|
values (
|
|
:label_value_label_id
|
|
,:label_value_value
|
|
,:label_value_color
|
|
,:label_value_created
|
|
,:label_value_updated
|
|
,:label_value_created_by
|
|
,:label_value_updated_by
|
|
)
|
|
RETURNING label_value_id`
|
|
|
|
db := dbtx.GetAccessor(ctx, s.db)
|
|
|
|
query, args, err := db.BindNamed(sqlQuery, mapInternalLabelValue(lblVal))
|
|
if err != nil {
|
|
return database.ProcessSQLErrorf(ctx, err, "Failed to bind query")
|
|
}
|
|
|
|
if err = db.QueryRowContext(ctx, query, args...).Scan(&lblVal.ID); err != nil {
|
|
return database.ProcessSQLErrorf(ctx, err, "Failed to create label value")
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (s *labelValueStore) Update(ctx context.Context, lblVal *types.LabelValue) error {
|
|
const sqlQuery = `
|
|
UPDATE label_values SET
|
|
label_value_value = :label_value_value
|
|
,label_value_color = :label_value_color
|
|
,label_value_updated = :label_value_updated
|
|
,label_value_updated_by = :label_value_updated_by
|
|
WHERE label_value_id = :label_value_id`
|
|
|
|
db := dbtx.GetAccessor(ctx, s.db)
|
|
query, args, err := db.BindNamed(sqlQuery, mapInternalLabelValue(lblVal))
|
|
if err != nil {
|
|
return database.ProcessSQLErrorf(ctx, err, "Failed to bind query")
|
|
}
|
|
|
|
if _, err := db.ExecContext(ctx, query, args...); err != nil {
|
|
return database.ProcessSQLErrorf(ctx, err, "Failed to update label value")
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (s *labelValueStore) Delete(
|
|
ctx context.Context,
|
|
labelID int64,
|
|
value string,
|
|
) error {
|
|
const sqlQuery = `
|
|
DELETE FROM label_values
|
|
WHERE label_value_label_id = $1 AND LOWER(label_value_value) = LOWER($2)`
|
|
|
|
db := dbtx.GetAccessor(ctx, s.db)
|
|
|
|
if _, err := db.ExecContext(ctx, sqlQuery, labelID, value); err != nil {
|
|
return database.ProcessSQLErrorf(ctx, err, "Failed to delete label")
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (s *labelValueStore) DeleteMany(
|
|
ctx context.Context,
|
|
labelID int64,
|
|
values []string,
|
|
) error {
|
|
stmt := database.Builder.
|
|
Delete("label_values").
|
|
Where("label_value_label_id = ?", labelID).
|
|
Where(squirrel.Eq{"label_value_value": values})
|
|
|
|
sql, args, err := stmt.ToSql()
|
|
if err != nil {
|
|
return errors.Wrap(err, "Failed to convert query to sql")
|
|
}
|
|
|
|
db := dbtx.GetAccessor(ctx, s.db)
|
|
|
|
if _, err := db.ExecContext(ctx, sql, args...); err != nil {
|
|
return database.ProcessSQLErrorf(ctx, err, "Failed to delete label")
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// List returns a list of label values for a specified label.
|
|
func (s *labelValueStore) List(
|
|
ctx context.Context,
|
|
labelID int64,
|
|
opts *types.ListQueryFilter,
|
|
) ([]*types.LabelValue, error) {
|
|
stmt := database.Builder.
|
|
Select(`label_value_id, ` + labelValueColumns).
|
|
From("label_values")
|
|
|
|
stmt = stmt.Where("label_value_label_id = ?", labelID)
|
|
|
|
stmt = stmt.Limit(database.Limit(opts.Size))
|
|
stmt = stmt.Offset(database.Offset(opts.Page, opts.Size))
|
|
|
|
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 []*labelValue
|
|
if err = db.SelectContext(ctx, &dst, sql, args...); err != nil {
|
|
return nil, database.ProcessSQLErrorf(ctx, err, "Fail to list labels")
|
|
}
|
|
|
|
return mapSliceLabelValue(dst), nil
|
|
}
|
|
|
|
func (s *labelValueStore) ListInfosByLabelIDs(
|
|
ctx context.Context,
|
|
labelIDs []int64,
|
|
) (map[int64][]*types.LabelValueInfo, error) {
|
|
stmt := database.Builder.
|
|
Select(`
|
|
label_value_id
|
|
,label_value_label_id
|
|
,label_value_value
|
|
,label_value_color
|
|
`).
|
|
From("label_values").
|
|
Where(squirrel.Eq{"label_value_label_id": labelIDs}).
|
|
OrderBy("label_value_value")
|
|
|
|
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 []*labelValueInfo
|
|
if err = db.SelectContext(ctx, &dst, sql, args...); err != nil {
|
|
return nil, database.ProcessSQLErrorf(ctx, err, "Fail to list labels")
|
|
}
|
|
|
|
valueInfos := mapLabelValuInfos(dst)
|
|
labelValueMap := make(map[int64][]*types.LabelValueInfo)
|
|
for _, info := range valueInfos {
|
|
labelValueMap[*info.LabelID] = append(labelValueMap[*info.LabelID], info)
|
|
}
|
|
|
|
return labelValueMap, nil
|
|
}
|
|
|
|
func (s *labelValueStore) FindByLabelID(
|
|
ctx context.Context,
|
|
labelID int64,
|
|
value string,
|
|
) (*types.LabelValue, error) {
|
|
const sqlQuery = labelValueSelectBase + `
|
|
WHERE label_value_label_id = $1 AND LOWER(label_value_value) = LOWER($2)`
|
|
|
|
db := dbtx.GetAccessor(ctx, s.db)
|
|
|
|
var dst labelValue
|
|
if err := db.GetContext(ctx, &dst, sqlQuery, labelID, value); err != nil {
|
|
return nil, database.ProcessSQLErrorf(ctx, err, "Failed to find label")
|
|
}
|
|
|
|
return mapLabelValue(&dst), nil
|
|
}
|
|
|
|
func (s *labelValueStore) FindByID(ctx context.Context, id int64) (*types.LabelValue, error) {
|
|
const sqlQuery = labelValueSelectBase + `
|
|
WHERE label_value_id = $1`
|
|
|
|
db := dbtx.GetAccessor(ctx, s.db)
|
|
|
|
var dst labelValue
|
|
if err := db.GetContext(ctx, &dst, sqlQuery, id); err != nil {
|
|
return nil, database.ProcessSQLErrorf(ctx, err, "Failed to find label")
|
|
}
|
|
|
|
return mapLabelValue(&dst), nil
|
|
}
|
|
|
|
func mapLabelValue(lbl *labelValue) *types.LabelValue {
|
|
return &types.LabelValue{
|
|
ID: lbl.ID,
|
|
LabelID: lbl.LabelID,
|
|
Value: lbl.Value,
|
|
Color: lbl.Color,
|
|
Created: lbl.Created,
|
|
Updated: lbl.Updated,
|
|
CreatedBy: lbl.CreatedBy,
|
|
UpdatedBy: lbl.UpdatedBy,
|
|
}
|
|
}
|
|
|
|
func mapSliceLabelValue(dbLabelValues []*labelValue) []*types.LabelValue {
|
|
result := make([]*types.LabelValue, len(dbLabelValues))
|
|
|
|
for i, lbl := range dbLabelValues {
|
|
result[i] = mapLabelValue(lbl)
|
|
}
|
|
|
|
return result
|
|
}
|
|
|
|
func mapInternalLabelValue(lblVal *types.LabelValue) *labelValue {
|
|
return &labelValue{
|
|
ID: lblVal.ID,
|
|
LabelID: lblVal.LabelID,
|
|
Value: lblVal.Value,
|
|
Color: lblVal.Color,
|
|
Created: lblVal.Created,
|
|
Updated: lblVal.Updated,
|
|
CreatedBy: lblVal.CreatedBy,
|
|
UpdatedBy: lblVal.UpdatedBy,
|
|
}
|
|
}
|
|
|
|
func mapLabeValuelInfo(internal *labelValueInfo) *types.LabelValueInfo {
|
|
if !internal.ValueID.Valid {
|
|
return nil
|
|
}
|
|
return &types.LabelValueInfo{
|
|
ID: internal.ValueID.Ptr(),
|
|
LabelID: internal.LabelID.Ptr(),
|
|
Value: internal.Value.Ptr(),
|
|
Color: internal.ValueColor.Ptr(),
|
|
}
|
|
}
|
|
|
|
func mapLabelValuInfos(
|
|
dbLabels []*labelValueInfo,
|
|
) []*types.LabelValueInfo {
|
|
result := make([]*types.LabelValueInfo, len(dbLabels))
|
|
|
|
for i, lbl := range dbLabels {
|
|
result[i] = mapLabeValuelInfo(lbl)
|
|
}
|
|
|
|
return result
|
|
}
|