drone/types/rule.go

229 lines
5.7 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 types
import (
"bytes"
"encoding/json"
"fmt"
"github.com/harness/gitness/types/enum"
"gopkg.in/yaml.v3"
)
type Rule struct {
ID int64 `json:"-"`
Version int64 `json:"-"`
CreatedBy int64 `json:"-"`
Created int64 `json:"created"`
Updated int64 `json:"updated"`
RepoID *int64 `json:"-"`
SpaceID *int64 `json:"-"`
Identifier string `json:"identifier"`
Description string `json:"description"`
Type RuleType `json:"type"`
State enum.RuleState `json:"state"`
Pattern json.RawMessage `json:"pattern"`
Definition json.RawMessage `json:"definition"`
CreatedByInfo PrincipalInfo `json:"created_by"`
Users map[int64]*PrincipalInfo `json:"users"`
UserGroups map[int64]*UserGroupInfo `json:"user_groups"`
// scope 0 indicates repo; scope > 0 indicates space depth level
Scope int64 `json:"scope"`
}
// TODO [CODE-1363]: remove after identifier migration.
func (r Rule) MarshalJSON() ([]byte, error) {
// alias allows us to embed the original object while avoiding an infinite loop of marshaling.
type alias Rule
return json.Marshal(&struct {
alias
UID string `json:"uid"`
}{
alias: (alias)(r),
UID: r.Identifier,
})
}
func (r Rule) MarshalYAML() (interface{}, error) {
// yaml cannot marshal json.RawMessage
pattern := make(map[string]any)
err := yaml.Unmarshal(r.Pattern, pattern)
if err != nil {
return nil, err
}
definition := make(map[string]any)
err = yaml.Unmarshal(r.Definition, definition)
if err != nil {
return nil, err
}
return map[string]any{
"id": r.ID,
"created": r.Created,
"updated": r.Updated,
"created_by": r.CreatedBy,
"identifier": r.Identifier,
"description": r.Description,
"type": r.Type,
"state": r.State,
"pattern": pattern,
"definition": definition,
}, nil
}
// Clone makes deep copy of the rule object.
func (r Rule) Clone() Rule {
var repoID *int64
var spaceID *int64
if r.RepoID != nil {
id := *r.RepoID
repoID = &id
}
if r.SpaceID != nil {
id := *r.SpaceID
spaceID = &id
}
r.RepoID = repoID
r.SpaceID = spaceID
pattern := make(json.RawMessage, len(r.Pattern))
copy(pattern, r.Pattern)
r.Pattern = pattern
definition := make(json.RawMessage, len(r.Definition))
copy(definition, r.Definition)
r.Definition = definition
users := make(map[int64]*PrincipalInfo, len(r.Users))
for key, value := range r.Users {
cloned := *value
users[key] = &cloned
}
r.Users = users
return r
}
func (r *Rule) IsEqual(rule *Rule) bool {
return r.Identifier == rule.Identifier && r.State == rule.State &&
r.Description == rule.Description && bytes.Equal(r.Pattern, rule.Pattern) &&
bytes.Equal(r.Definition, rule.Definition)
}
type RuleType string
type RuleFilter struct {
ListQueryFilter
States []enum.RuleState
Sort enum.RuleSort `json:"sort"`
Order enum.Order `json:"order"`
}
// Violation represents a single violation.
type Violation struct {
Code string `json:"code"`
Message string `json:"message"`
Params []any `json:"params"`
}
// RuleViolations holds several violations of a rule.
type RuleViolations struct {
Rule RuleInfo `json:"rule"`
Bypassable bool `json:"bypassable"`
Bypassed bool `json:"bypassed"`
Violations []Violation `json:"violations"`
}
func (violations *RuleViolations) Add(code, message string) {
violations.Violations = append(violations.Violations, Violation{
Code: code,
Message: message,
Params: nil,
})
}
func (violations *RuleViolations) Addf(code, format string, params ...any) {
violations.Violations = append(violations.Violations, Violation{
Code: code,
Message: fmt.Sprintf(format, params...),
Params: params,
})
}
func (violations *RuleViolations) IsCritical() bool {
return violations.Rule.State == enum.RuleStateActive && len(violations.Violations) > 0 && !violations.Bypassed
}
func (violations *RuleViolations) IsBypassed() bool {
return violations.Rule.State == enum.RuleStateActive && len(violations.Violations) > 0 && violations.Bypassed
}
// RuleInfo holds basic info about a rule that is used to describe the rule in RuleViolations.
type RuleInfo struct {
SpacePath string `json:"space_path,omitempty"`
RepoPath string `json:"repo_path,omitempty"`
ID int64 `json:"-"`
Identifier string `json:"identifier"`
Type RuleType `json:"type"`
State enum.RuleState `json:"state"`
}
// TODO [CODE-1363]: remove after identifier migration.
func (r RuleInfo) MarshalJSON() ([]byte, error) {
// alias allows us to embed the original object while avoiding an infinite loop of marshaling.
type alias RuleInfo
return json.Marshal(&struct {
alias
UID string `json:"uid"`
}{
alias: (alias)(r),
UID: r.Identifier,
})
}
type RuleInfoInternal struct {
RuleInfo
Pattern json.RawMessage
Definition json.RawMessage
}
type RulesViolations struct {
Message string `json:"message"`
Violations []RuleViolations `json:"violations"`
}
type DryRunRulesOutput struct {
DryRunRules bool `json:"dry_run_rules,omitempty"`
RuleViolations []RuleViolations `json:"rule_violations,omitempty"`
}
type RuleParentInfo struct {
Type enum.RuleParent
ID int64
}