2020-11-01 14:50:59 +03:00
|
|
|
package models
|
|
|
|
|
2021-12-26 19:02:14 +03:00
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"github.com/emvi/logbuch"
|
|
|
|
"github.com/mitchellh/hashstructure/v2"
|
|
|
|
)
|
|
|
|
|
2020-11-01 14:50:59 +03:00
|
|
|
type Filters struct {
|
2021-12-26 19:02:14 +03:00
|
|
|
Project OrFilter
|
|
|
|
OS OrFilter
|
|
|
|
Language OrFilter
|
|
|
|
Editor OrFilter
|
|
|
|
Machine OrFilter
|
|
|
|
Label OrFilter
|
2022-01-02 15:39:20 +03:00
|
|
|
Branch OrFilter
|
2023-03-22 22:45:27 +03:00
|
|
|
Entity OrFilter
|
2021-12-26 19:02:14 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
type OrFilter []string
|
|
|
|
|
|
|
|
func (f OrFilter) Exists() bool {
|
|
|
|
return len(f) > 0 && f[0] != ""
|
|
|
|
}
|
|
|
|
|
|
|
|
func (f OrFilter) MatchAny(search string) bool {
|
|
|
|
for _, s := range f {
|
|
|
|
if s == search {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false
|
2020-11-01 14:50:59 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
type FilterElement struct {
|
2021-12-26 19:02:14 +03:00
|
|
|
entity uint8
|
|
|
|
filter OrFilter
|
2020-11-01 14:50:59 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
func NewFiltersWith(entity uint8, key string) *Filters {
|
2021-12-26 19:02:14 +03:00
|
|
|
return NewFilterWithMultiple(entity, []string{key})
|
|
|
|
}
|
|
|
|
|
|
|
|
func NewFilterWithMultiple(entity uint8, keys []string) *Filters {
|
|
|
|
filters := &Filters{}
|
|
|
|
return filters.WithMultiple(entity, keys)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (f *Filters) With(entity uint8, key string) *Filters {
|
|
|
|
return f.WithMultiple(entity, []string{key})
|
|
|
|
}
|
|
|
|
|
|
|
|
func (f *Filters) WithMultiple(entity uint8, keys []string) *Filters {
|
2020-11-01 14:50:59 +03:00
|
|
|
switch entity {
|
|
|
|
case SummaryProject:
|
2021-12-26 19:02:14 +03:00
|
|
|
f.Project = append(f.Project, keys...)
|
2020-11-01 14:50:59 +03:00
|
|
|
case SummaryOS:
|
2021-12-26 19:02:14 +03:00
|
|
|
f.OS = append(f.OS, keys...)
|
2020-11-01 14:50:59 +03:00
|
|
|
case SummaryLanguage:
|
2021-12-26 19:02:14 +03:00
|
|
|
f.Language = append(f.Language, keys...)
|
2020-11-01 14:50:59 +03:00
|
|
|
case SummaryEditor:
|
2021-12-26 19:02:14 +03:00
|
|
|
f.Editor = append(f.Editor, keys...)
|
2020-11-01 14:50:59 +03:00
|
|
|
case SummaryMachine:
|
2021-12-26 19:02:14 +03:00
|
|
|
f.Machine = append(f.Machine, keys...)
|
2021-06-11 21:59:34 +03:00
|
|
|
case SummaryLabel:
|
2021-12-26 19:02:14 +03:00
|
|
|
f.Label = append(f.Label, keys...)
|
2022-01-02 15:39:20 +03:00
|
|
|
case SummaryBranch:
|
|
|
|
f.Branch = append(f.Branch, keys...)
|
2023-03-22 22:45:27 +03:00
|
|
|
case SummaryEntity:
|
|
|
|
f.Entity = append(f.Entity, keys...)
|
2020-11-01 14:50:59 +03:00
|
|
|
}
|
2021-12-26 19:02:14 +03:00
|
|
|
return f
|
2020-11-01 14:50:59 +03:00
|
|
|
}
|
|
|
|
|
2021-12-26 19:02:14 +03:00
|
|
|
func (f *Filters) One() (bool, uint8, OrFilter) {
|
|
|
|
if f.Project != nil && f.Project.Exists() {
|
2020-11-01 14:50:59 +03:00
|
|
|
return true, SummaryProject, f.Project
|
2021-12-26 19:02:14 +03:00
|
|
|
} else if f.OS != nil && f.OS.Exists() {
|
2020-11-01 14:50:59 +03:00
|
|
|
return true, SummaryOS, f.OS
|
2021-12-26 19:02:14 +03:00
|
|
|
} else if f.Language != nil && f.Language.Exists() {
|
2020-11-01 14:50:59 +03:00
|
|
|
return true, SummaryLanguage, f.Language
|
2021-12-26 19:02:14 +03:00
|
|
|
} else if f.Editor != nil && f.Editor.Exists() {
|
2020-11-01 14:50:59 +03:00
|
|
|
return true, SummaryEditor, f.Editor
|
2021-12-26 19:02:14 +03:00
|
|
|
} else if f.Machine != nil && f.Machine.Exists() {
|
2020-11-01 14:50:59 +03:00
|
|
|
return true, SummaryMachine, f.Machine
|
2021-12-26 19:02:14 +03:00
|
|
|
} else if f.Label != nil && f.Label.Exists() {
|
2021-06-11 21:59:34 +03:00
|
|
|
return true, SummaryLabel, f.Label
|
2022-01-02 15:39:20 +03:00
|
|
|
} else if f.Branch != nil && f.Branch.Exists() {
|
|
|
|
return true, SummaryBranch, f.Branch
|
2023-03-22 22:45:27 +03:00
|
|
|
} else if f.Entity != nil && f.Entity.Exists() {
|
|
|
|
return true, SummaryEntity, f.Entity
|
2020-11-01 14:50:59 +03:00
|
|
|
}
|
2021-12-26 19:02:14 +03:00
|
|
|
return false, 0, OrFilter{}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (f *Filters) OneOrEmpty() FilterElement {
|
|
|
|
if ok, t, of := f.One(); ok {
|
|
|
|
return FilterElement{entity: t, filter: of}
|
|
|
|
}
|
2022-03-13 10:17:50 +03:00
|
|
|
return FilterElement{entity: SummaryUnknown, filter: []string{}}
|
2021-12-26 19:02:14 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
func (f *Filters) IsEmpty() bool {
|
|
|
|
nonEmpty, _, _ := f.One()
|
|
|
|
return !nonEmpty
|
|
|
|
}
|
|
|
|
|
2022-03-13 10:17:50 +03:00
|
|
|
func (f *Filters) Count() int {
|
|
|
|
var count int
|
2023-03-22 22:45:27 +03:00
|
|
|
for i := SummaryProject; i <= SummaryEntity; i++ {
|
2022-03-13 10:17:50 +03:00
|
|
|
count += f.CountByEntity(i)
|
|
|
|
}
|
|
|
|
return count
|
|
|
|
}
|
|
|
|
|
|
|
|
func (f *Filters) CountByEntity(entity uint8) int {
|
|
|
|
return len(*f.ResolveEntity(entity))
|
|
|
|
}
|
|
|
|
|
|
|
|
func (f *Filters) EntityCount() int {
|
|
|
|
var count int
|
2023-03-22 22:45:27 +03:00
|
|
|
for i := SummaryProject; i <= SummaryEntity; i++ {
|
2022-03-13 10:17:50 +03:00
|
|
|
if c := f.CountByEntity(i); c > 0 {
|
|
|
|
count++
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return count
|
|
|
|
}
|
|
|
|
|
|
|
|
func (f *Filters) ResolveEntity(entityId uint8) *OrFilter {
|
|
|
|
switch entityId {
|
|
|
|
case SummaryProject:
|
|
|
|
return &f.Project
|
|
|
|
case SummaryLanguage:
|
|
|
|
return &f.Language
|
|
|
|
case SummaryEditor:
|
|
|
|
return &f.Editor
|
|
|
|
case SummaryOS:
|
|
|
|
return &f.OS
|
|
|
|
case SummaryMachine:
|
|
|
|
return &f.Machine
|
|
|
|
case SummaryLabel:
|
|
|
|
return &f.Label
|
|
|
|
case SummaryBranch:
|
|
|
|
return &f.Branch
|
2023-03-22 22:45:27 +03:00
|
|
|
case SummaryEntity:
|
|
|
|
return &f.Entity
|
2022-03-13 10:17:50 +03:00
|
|
|
default:
|
|
|
|
return &OrFilter{}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-12-26 19:02:14 +03:00
|
|
|
func (f *Filters) Hash() string {
|
|
|
|
hash, err := hashstructure.Hash(f, hashstructure.FormatV2, nil)
|
|
|
|
if err != nil {
|
2022-02-17 14:20:22 +03:00
|
|
|
logbuch.Error("CRITICAL ERROR: failed to hash struct - %v", err)
|
2021-12-26 19:02:14 +03:00
|
|
|
}
|
|
|
|
return fmt.Sprintf("%x", hash) // "uint64 values with high bit set are not supported"
|
|
|
|
}
|
|
|
|
|
|
|
|
func (f *Filters) Match(h *Heartbeat) bool {
|
|
|
|
return (f.Project == nil || f.Project.MatchAny(h.Project)) &&
|
|
|
|
(f.OS == nil || f.OS.MatchAny(h.OperatingSystem)) &&
|
|
|
|
(f.Language == nil || f.Language.MatchAny(h.Language)) &&
|
|
|
|
(f.Editor == nil || f.Editor.MatchAny(h.Editor)) &&
|
|
|
|
(f.Machine == nil || f.Machine.MatchAny(h.Machine))
|
|
|
|
}
|
|
|
|
|
|
|
|
// WithAliases adds OR-conditions for every alias of a filter key as additional filter keys
|
2021-12-26 19:23:37 +03:00
|
|
|
func (f *Filters) WithAliases(resolve AliasReverseResolver) *Filters {
|
2021-12-26 19:02:14 +03:00
|
|
|
if f.Project != nil {
|
|
|
|
updated := OrFilter(make([]string, 0, len(f.Project)))
|
|
|
|
for _, e := range f.Project {
|
|
|
|
updated = append(updated, e)
|
2021-12-26 19:23:37 +03:00
|
|
|
updated = append(updated, resolve(SummaryProject, e)...)
|
2021-12-26 19:02:14 +03:00
|
|
|
}
|
|
|
|
f.Project = updated
|
|
|
|
}
|
|
|
|
if f.OS != nil {
|
|
|
|
updated := OrFilter(make([]string, 0, len(f.OS)))
|
|
|
|
for _, e := range f.OS {
|
|
|
|
updated = append(updated, e)
|
2021-12-26 19:23:37 +03:00
|
|
|
updated = append(updated, resolve(SummaryOS, e)...)
|
2021-12-26 19:02:14 +03:00
|
|
|
}
|
|
|
|
f.OS = updated
|
|
|
|
}
|
|
|
|
if f.Language != nil {
|
|
|
|
updated := OrFilter(make([]string, 0, len(f.Language)))
|
|
|
|
for _, e := range f.Language {
|
|
|
|
updated = append(updated, e)
|
2021-12-26 19:23:37 +03:00
|
|
|
updated = append(updated, resolve(SummaryLanguage, e)...)
|
2021-12-26 19:02:14 +03:00
|
|
|
}
|
|
|
|
f.Language = updated
|
|
|
|
}
|
|
|
|
if f.Editor != nil {
|
|
|
|
updated := OrFilter(make([]string, 0, len(f.Editor)))
|
|
|
|
for _, e := range f.Editor {
|
|
|
|
updated = append(updated, e)
|
2021-12-26 19:23:37 +03:00
|
|
|
updated = append(updated, resolve(SummaryEditor, e)...)
|
2021-12-26 19:02:14 +03:00
|
|
|
}
|
|
|
|
f.Editor = updated
|
|
|
|
}
|
|
|
|
if f.Machine != nil {
|
|
|
|
updated := OrFilter(make([]string, 0, len(f.Machine)))
|
|
|
|
for _, e := range f.Machine {
|
|
|
|
updated = append(updated, e)
|
2021-12-26 19:23:37 +03:00
|
|
|
updated = append(updated, resolve(SummaryMachine, e)...)
|
2021-12-26 19:02:14 +03:00
|
|
|
}
|
|
|
|
f.Machine = updated
|
|
|
|
}
|
2022-01-02 15:39:20 +03:00
|
|
|
if f.Branch != nil {
|
|
|
|
updated := OrFilter(make([]string, 0, len(f.Branch)))
|
|
|
|
for _, e := range f.Branch {
|
|
|
|
updated = append(updated, e)
|
|
|
|
updated = append(updated, resolve(SummaryBranch, e)...)
|
|
|
|
}
|
|
|
|
f.Branch = updated
|
|
|
|
}
|
2023-03-22 22:45:27 +03:00
|
|
|
// no aliases for entites / files
|
2021-12-26 19:02:14 +03:00
|
|
|
return f
|
2020-11-01 14:50:59 +03:00
|
|
|
}
|
2021-12-26 19:23:37 +03:00
|
|
|
|
|
|
|
func (f *Filters) WithProjectLabels(resolve ProjectLabelReverseResolver) *Filters {
|
|
|
|
if f.Label == nil || !f.Label.Exists() {
|
|
|
|
return f
|
|
|
|
}
|
|
|
|
for _, l := range f.Label {
|
2021-12-26 20:09:05 +03:00
|
|
|
f.WithMultiple(SummaryProject, resolve(l))
|
2021-12-26 19:23:37 +03:00
|
|
|
}
|
|
|
|
return f
|
|
|
|
}
|
2023-03-22 22:45:27 +03:00
|
|
|
|
|
|
|
func (f *Filters) IsProjectDetails() bool {
|
|
|
|
return f != nil && f.Project != nil && f.Project.Exists()
|
|
|
|
}
|