feat: ability to filter by project labels

This commit is contained in:
Ferdinand Mütsch 2021-12-26 17:23:37 +01:00
parent a279548c89
commit d80c1a4c4b
4 changed files with 66 additions and 22 deletions

View File

@ -104,7 +104,6 @@ func (f *Filters) Hash() string {
}
func (f *Filters) Match(h *Heartbeat) bool {
// TODO: labels?
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)) &&
@ -113,12 +112,12 @@ func (f *Filters) Match(h *Heartbeat) bool {
}
// WithAliases adds OR-conditions for every alias of a filter key as additional filter keys
func (f *Filters) WithAliases(resolver AliasReverseResolver) *Filters {
func (f *Filters) WithAliases(resolve AliasReverseResolver) *Filters {
if f.Project != nil {
updated := OrFilter(make([]string, 0, len(f.Project)))
for _, e := range f.Project {
updated = append(updated, e)
updated = append(updated, resolver(SummaryProject, e)...)
updated = append(updated, resolve(SummaryProject, e)...)
}
f.Project = updated
}
@ -126,7 +125,7 @@ func (f *Filters) WithAliases(resolver AliasReverseResolver) *Filters {
updated := OrFilter(make([]string, 0, len(f.OS)))
for _, e := range f.OS {
updated = append(updated, e)
updated = append(updated, resolver(SummaryOS, e)...)
updated = append(updated, resolve(SummaryOS, e)...)
}
f.OS = updated
}
@ -134,7 +133,7 @@ func (f *Filters) WithAliases(resolver AliasReverseResolver) *Filters {
updated := OrFilter(make([]string, 0, len(f.Language)))
for _, e := range f.Language {
updated = append(updated, e)
updated = append(updated, resolver(SummaryLanguage, e)...)
updated = append(updated, resolve(SummaryLanguage, e)...)
}
f.Language = updated
}
@ -142,7 +141,7 @@ func (f *Filters) WithAliases(resolver AliasReverseResolver) *Filters {
updated := OrFilter(make([]string, 0, len(f.Editor)))
for _, e := range f.Editor {
updated = append(updated, e)
updated = append(updated, resolver(SummaryEditor, e)...)
updated = append(updated, resolve(SummaryEditor, e)...)
}
f.Editor = updated
}
@ -150,9 +149,19 @@ func (f *Filters) WithAliases(resolver AliasReverseResolver) *Filters {
updated := OrFilter(make([]string, 0, len(f.Machine)))
for _, e := range f.Machine {
updated = append(updated, e)
updated = append(updated, resolver(SummaryMachine, e)...)
updated = append(updated, resolve(SummaryMachine, e)...)
}
f.Machine = updated
}
return f
}
func (f *Filters) WithProjectLabels(resolve ProjectLabelReverseResolver) *Filters {
if f.Label == nil || !f.Label.Exists() {
return f
}
for _, l := range f.Label {
f.Project = append(f.Project, resolve(l)...)
}
return f
}

View File

@ -1,5 +1,8 @@
package models
// ProjectLabelReverseResolver returns all projects for a given label
type ProjectLabelReverseResolver func(l string) []string
type ProjectLabel struct {
ID uint `json:"id" gorm:"primary_key"`
User *User `json:"-" gorm:"not null; constraint:OnUpdate:CASCADE,OnDelete:CASCADE"`

View File

@ -43,6 +43,7 @@ func (srv *ProjectLabelService) GetByUser(userId string) ([]*models.ProjectLabel
return labels, nil
}
// GetByUserGrouped returns lists of project labels, grouped by their project key
func (srv *ProjectLabelService) GetByUserGrouped(userId string) (map[string][]*models.ProjectLabel, error) {
labelsByProject := make(map[string][]*models.ProjectLabel)
userLabels, err := srv.GetByUser(userId)
@ -60,6 +61,7 @@ func (srv *ProjectLabelService) GetByUserGrouped(userId string) (map[string][]*m
return labelsByProject, nil
}
// GetByUserGroupedInverted returns lists of project labels, grouped by their label key
func (srv *ProjectLabelService) GetByUserGroupedInverted(userId string) (map[string][]*models.ProjectLabel, error) {
projectsByLabel := make(map[string][]*models.ProjectLabel)
userLabels, err := srv.GetByUser(userId)

View File

@ -62,23 +62,15 @@ func (srv *SummaryService) Aliased(from, to time.Time, user *models.User, f Summ
return cacheResult.(*models.Summary), nil
}
// Wrap alias resolution
resolve := func(t uint8, k string) string {
s, _ := srv.aliasService.GetAliasOrDefault(user.ID, t, k)
return s
}
resolveReverse := func(t uint8, k string) []string {
aliases, _ := srv.aliasService.GetByUserAndKeyAndType(user.ID, k, t)
aliasStrings := make([]string, 0, len(aliases))
for _, a := range aliases {
aliasStrings = append(aliasStrings, a.Value)
}
return aliasStrings
}
// Resolver functions
resolveAliases := srv.getAliasResolver(user)
resolveAliasesReverse := srv.getAliasReverseResolver(user)
resolveProjectLabelsReverse := srv.getProjectLabelsReverseResolver(user)
// Post-process filters
if filters != nil {
filters = filters.WithAliases(resolveReverse)
filters = filters.WithAliases(resolveAliasesReverse)
filters = filters.WithProjectLabels(resolveProjectLabelsReverse)
}
// Initialize alias resolver service
@ -93,7 +85,7 @@ func (srv *SummaryService) Aliased(from, to time.Time, user *models.User, f Summ
}
// Post-process summary and cache it
summary := s.WithResolvedAliases(resolve)
summary := s.WithResolvedAliases(resolveAliases)
summary = srv.withProjectLabels(summary)
summary.FillBy(models.SummaryProject, models.SummaryLabel) // first fill up labels from projects
summary.FillMissing() // then, full up types which are entirely missing
@ -419,3 +411,41 @@ func (srv *SummaryService) invalidateUserCache(userId string) {
}
}
}
func (srv *SummaryService) getAliasResolver(user *models.User) models.AliasResolver {
return func(t uint8, k string) string {
s, _ := srv.aliasService.GetAliasOrDefault(user.ID, t, k)
return s
}
}
func (srv *SummaryService) getAliasReverseResolver(user *models.User) models.AliasReverseResolver {
return func(t uint8, k string) []string {
aliases, err := srv.aliasService.GetByUserAndKeyAndType(user.ID, k, t)
if err != nil {
aliases = []*models.Alias{}
}
aliasStrings := make([]string, 0, len(aliases))
for _, a := range aliases {
aliasStrings = append(aliasStrings, a.Value)
}
return aliasStrings
}
}
func (srv *SummaryService) getProjectLabelsReverseResolver(user *models.User) models.ProjectLabelReverseResolver {
return func(k string) []string {
var labels []*models.ProjectLabel
allLabels, err := srv.projectLabelService.GetByUserGroupedInverted(user.ID)
if err == nil {
if l, ok := allLabels[k]; ok {
labels = l
}
}
projectStrings := make([]string, 0, len(labels))
for _, l := range labels {
projectStrings = append(projectStrings, l.ProjectKey)
}
return projectStrings
}
}