drone/app/services/label/label.go
Darko Draskovic 3a3c8e2c85 feat: [CODE-2854]: Add scope info to webhooks (#3050)
* Change space permissions to repo permissions for and add scope info to webhooks
2024-11-26 22:16:10 +00:00

360 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 label
import (
"context"
"fmt"
"sort"
"time"
"github.com/harness/gitness/errors"
"github.com/harness/gitness/store"
"github.com/harness/gitness/types"
)
const labelScopeRepo = int64(0)
func (s *Service) Define(
ctx context.Context,
principalID int64,
spaceID, repoID *int64,
in *types.DefineLabelInput,
) (*types.Label, error) {
scope := labelScopeRepo
if spaceID != nil {
var err error
scope, err = s.spaceStore.GetTreeLevel(ctx, *spaceID)
if err != nil {
return nil, fmt.Errorf("failed to get space tree level: %w", err)
}
}
label := newLabel(principalID, spaceID, repoID, scope, in)
if err := s.labelStore.Define(ctx, label); err != nil {
return nil, err
}
return label, nil
}
func (s *Service) Update(
ctx context.Context,
principalID int64,
spaceID, repoID *int64,
key string,
in *types.UpdateLabelInput,
) (*types.Label, error) {
label, err := s.labelStore.Find(ctx, spaceID, repoID, key)
if err != nil {
return nil, fmt.Errorf("failed to find repo label: %w", err)
}
return s.update(ctx, principalID, label, in)
}
func (s *Service) update(
ctx context.Context,
principalID int64,
label *types.Label,
in *types.UpdateLabelInput,
) (*types.Label, error) {
label, hasChanges := applyChanges(principalID, label, in)
if !hasChanges {
return label, nil
}
err := s.labelStore.Update(ctx, label)
if err != nil {
return nil, fmt.Errorf("failed to update label: %w", err)
}
return label, nil
}
//nolint:gocognit
func (s *Service) Save(
ctx context.Context,
principalID int64,
spaceID, repoID *int64,
in *types.SaveInput,
) (*types.LabelWithValues, error) {
var label *types.Label
var valuesToReturn []*types.LabelValue
var err error
err = s.tx.WithTx(ctx, func(ctx context.Context) error {
label, err = s.labelStore.FindByID(ctx, in.Label.ID)
if err != nil { //nolint:nestif
if !errors.Is(err, store.ErrResourceNotFound) {
return err
}
label, err = s.Define(ctx, principalID, spaceID, repoID, &in.Label.DefineLabelInput)
if err != nil {
return err
}
} else {
if err := checkLabelInScope(spaceID, repoID, label); err != nil {
return err
}
label, err = s.update(ctx, principalID, label, &types.UpdateLabelInput{
Key: &in.Label.Key,
Type: &in.Label.Type,
Description: &in.Label.Description,
Color: &in.Label.Color,
})
if err != nil {
return err
}
}
existingValues, err := s.labelValueStore.List(ctx, label.ID, &types.ListQueryFilter{})
if err != nil {
return err
}
existingValuesMap := make(map[int64]*types.LabelValue, len(existingValues))
for _, value := range existingValues {
existingValuesMap[value.ID] = value
}
var valuesToCreate []*types.SaveLabelValueInput
valuesToUpdate := make(map[int64]*types.SaveLabelValueInput)
var valuesToDelete []string
for _, value := range in.Values {
if _, ok := existingValuesMap[value.ID]; ok {
valuesToUpdate[value.ID] = value
} else {
valuesToCreate = append(valuesToCreate, value)
}
}
for _, value := range existingValues {
if _, ok := valuesToUpdate[value.ID]; !ok {
valuesToDelete = append(valuesToDelete, value.Value)
}
}
valuesToReturn = make([]*types.LabelValue, len(valuesToCreate)+len(valuesToUpdate))
for i, value := range valuesToCreate {
valuesToReturn[i] = newLabelValue(principalID, label.ID, &value.DefineValueInput)
if err = s.labelValueStore.Define(ctx, valuesToReturn[i]); err != nil {
if errors.Is(err, store.ErrDuplicate) {
return errors.Conflict("value %s already exists", valuesToReturn[i].Value)
}
return err
}
}
i := len(valuesToCreate)
for _, value := range valuesToUpdate {
if valuesToReturn[i], err = s.updateValue(ctx, principalID, existingValuesMap[value.ID], &types.UpdateValueInput{
Value: &value.Value,
Color: &value.Color,
}); err != nil {
return err
}
i++
}
if err = s.labelValueStore.DeleteMany(ctx, label.ID, valuesToDelete); err != nil {
return err
}
if label.ValueCount, err = s.labelStore.IncrementValueCount(
ctx, label.ID, len(valuesToCreate)-len(valuesToDelete)); err != nil {
return err
}
sort.Slice(valuesToReturn, func(i, j int) bool {
return valuesToReturn[i].Value < valuesToReturn[j].Value
})
return nil
})
if err != nil {
return nil, fmt.Errorf("failed to save label: %w", err)
}
return &types.LabelWithValues{
Label: *label,
Values: valuesToReturn,
}, nil
}
func (s *Service) Find(
ctx context.Context,
spaceID, repoID *int64,
key string,
) (*types.Label, error) {
return s.labelStore.Find(ctx, spaceID, repoID, key)
}
func (s *Service) FindByID(ctx context.Context, labelID int64) (*types.Label, error) {
return s.labelStore.FindByID(ctx, labelID)
}
func (s *Service) List(
ctx context.Context,
spaceID, repoID *int64,
filter *types.LabelFilter,
) ([]*types.Label, int64, error) {
if filter.Inherited {
return s.listInScopes(ctx, spaceID, repoID, filter)
}
return s.list(ctx, spaceID, repoID, filter)
}
func (s *Service) list(
ctx context.Context,
spaceID, repoID *int64,
filter *types.LabelFilter,
) ([]*types.Label, int64, error) {
if repoID != nil {
total, err := s.labelStore.CountInRepo(ctx, *repoID, filter)
if err != nil {
return nil, 0, err
}
labels, err := s.labelStore.List(ctx, nil, repoID, filter)
if err != nil {
return nil, 0, err
}
return labels, total, nil
}
count, err := s.labelStore.CountInSpace(ctx, *spaceID, filter)
if err != nil {
return nil, 0, err
}
labels, err := s.labelStore.List(ctx, spaceID, nil, filter)
if err != nil {
return nil, 0, err
}
return labels, count, nil
}
func (s *Service) listInScopes(
ctx context.Context,
spaceID, repoID *int64,
filter *types.LabelFilter,
) ([]*types.Label, int64, error) {
var spaceIDs []int64
var repoIDVal int64
var err error
if repoID != nil {
spaceIDs, err = s.spaceStore.GetAncestorIDs(ctx, *spaceID)
if err != nil {
return nil, 0, err
}
repoIDVal = *repoID
} else {
spaceIDs, err = s.spaceStore.GetAncestorIDs(ctx, *spaceID)
if err != nil {
return nil, 0, err
}
}
total, err := s.labelStore.CountInScopes(ctx, repoIDVal, spaceIDs, filter)
if err != nil {
return nil, 0, err
}
labels, err := s.labelStore.ListInScopes(ctx, repoIDVal, spaceIDs, filter)
if err != nil {
return nil, 0, err
}
return labels, total, nil
}
func (s *Service) Delete(
ctx context.Context,
spaceID, repoID *int64,
key string,
) error {
return s.labelStore.Delete(ctx, spaceID, repoID, key)
}
func newLabel(
principalID int64,
spaceID, repoID *int64,
scope int64,
in *types.DefineLabelInput,
) *types.Label {
now := time.Now().UnixMilli()
return &types.Label{
RepoID: repoID,
SpaceID: spaceID,
Scope: scope,
Key: in.Key,
Type: in.Type,
Description: in.Description,
Color: in.Color,
Created: now,
Updated: now,
CreatedBy: principalID,
UpdatedBy: principalID,
}
}
func applyChanges(principalID int64, label *types.Label, in *types.UpdateLabelInput) (*types.Label, bool) {
hasChanges := false
if label.UpdatedBy != principalID {
hasChanges = true
label.UpdatedBy = principalID
}
if in.Key != nil && label.Key != *in.Key {
hasChanges = true
label.Key = *in.Key
}
if in.Description != nil && label.Description != *in.Description {
hasChanges = true
label.Description = *in.Description
}
if in.Color != nil && label.Color != *in.Color {
hasChanges = true
label.Color = *in.Color
}
if in.Type != nil && label.Type != *in.Type {
hasChanges = true
label.Type = *in.Type
}
if hasChanges {
label.Updated = time.Now().UnixMilli()
}
return label, hasChanges
}
func checkLabelInScope(
spaceID, repoID *int64,
label *types.Label,
) error {
if (repoID != nil && (label.RepoID == nil || *label.RepoID != *repoID)) ||
(spaceID != nil && (label.SpaceID == nil || *label.SpaceID != *spaceID)) {
return errors.InvalidArgument("label is not defined in requested scope")
}
return nil
}