From beba7c8d2ececf17d56d32c5b1e435ff0bcd0b4e Mon Sep 17 00:00:00 2001 From: Christoph Kluge Date: Thu, 19 Sep 2024 15:21:32 +0200 Subject: [PATCH] fix tag count bug if names non-unique, set global as default scope if none entered --- internal/api/rest.go | 1 + internal/repository/tags.go | 23 ++++++++++++++++++----- internal/routerConfig/routes.go | 3 ++- 3 files changed, 21 insertions(+), 6 deletions(-) diff --git a/internal/api/rest.go b/internal/api/rest.go index eee65c7..6e90365 100644 --- a/internal/api/rest.go +++ b/internal/api/rest.go @@ -688,6 +688,7 @@ func (api *RestApi) editMeta(rw http.ResponseWriter, r *http.Request) { // @summary Adds one or more tags to a job // @tags Job add and modify // @description Adds tag(s) to a job specified by DB ID. Name and Type of Tag(s) can be chosen freely. +// @description Tag Scope for frontend visibility will default to "global" if none entered, other options: "admin" or specific username. // @description If tagged job is already finished: Tag will be written directly to respective archive files. // @accept json // @produce json diff --git a/internal/repository/tags.go b/internal/repository/tags.go index 2b7e5d5..dcdbd29 100644 --- a/internal/repository/tags.go +++ b/internal/repository/tags.go @@ -81,6 +81,12 @@ func (r *JobRepository) RemoveTag(ctx context.Context, job, tag int64) ([]*schem // CreateTag creates a new tag with the specified type and name and returns its database id. func (r *JobRepository) CreateTag(tagType string, tagName string, tagScope string) (tagId int64, err error) { + + // Default to "Global" scope if none defined + if tagScope == "" { + tagScope = "global" + } + q := sq.Insert("tag").Columns("tag_type", "tag_name", "tag_scope").Values(tagType, tagName, tagScope) res, err := q.RunWith(r.stmtCache).Exec() @@ -120,7 +126,7 @@ func (r *JobRepository) CountTags(ctx context.Context) (tags []schema.Tag, count user := GetUserFromContext(ctx) // Query and Count Jobs with attached Tags - q := sq.Select("t.tag_name, count(jt.tag_id)"). + q := sq.Select("t.tag_name, t.id, count(jt.tag_id)"). From("tag t"). LeftJoin("jobtag jt ON t.id = jt.tag_id"). GroupBy("t.tag_name") @@ -137,7 +143,7 @@ func (r *JobRepository) CountTags(ctx context.Context) (tags []schema.Tag, count // Handle Job Ownership if user != nil && user.HasAnyRole([]schema.Role{schema.RoleAdmin, schema.RoleSupport}) { // ADMIN || SUPPORT: Count all jobs - log.Debug("CountTags: User Admin or Support -> Count all Jobs for Tags") + // log.Debug("CountTags: User Admin or Support -> Count all Jobs for Tags") // Unchanged: Needs to be own case still, due to UserRole/NoRole compatibility handling in else case } else if user != nil && user.HasRole(schema.RoleManager) { // MANAGER: Count own jobs plus project's jobs // Build ("project1", "project2", ...) list of variable length directly in SQL string @@ -154,11 +160,13 @@ func (r *JobRepository) CountTags(ctx context.Context) (tags []schema.Tag, count counts = make(map[string]int) for rows.Next() { var tagName string + var tagId int var count int - if err = rows.Scan(&tagName, &count); err != nil { + if err = rows.Scan(&tagName, &tagId, &count); err != nil { return nil, nil, err } - counts[tagName] = count + // Use tagId as second Map-Key component to differentiate tags with identical names + counts[fmt.Sprint(tagName, tagId)] = count } err = rows.Err() @@ -169,6 +177,11 @@ func (r *JobRepository) CountTags(ctx context.Context) (tags []schema.Tag, count // If such a tag does not yet exist, it is created. func (r *JobRepository) AddTagOrCreate(ctx context.Context, jobId int64, tagType string, tagName string, tagScope string) (tagId int64, err error) { + // Default to "Global" scope if none defined + if tagScope == "" { + tagScope = "global" + } + writable, err := r.checkScopeAuth(ctx, "write", tagScope) if err != nil { return 0, err @@ -237,7 +250,7 @@ func (r *JobRepository) GetTags(ctx context.Context, job *int64) ([]*schema.Tag, return tags, nil } -// GetArchiveTags returns a list of all tags *regardless of scope* if job is nil or of the tags that the job with that database ID has. +// GetArchiveTags returns a list of all tags *regardless of scope* for archiving if job is nil or of the tags that the job with that database ID has. func (r *JobRepository) getArchiveTags(job *int64) ([]*schema.Tag, error) { q := sq.Select("id", "tag_type", "tag_name", "tag_scope").From("tag") if job != nil { diff --git a/internal/routerConfig/routes.go b/internal/routerConfig/routes.go index cde1562..eff4ade 100644 --- a/internal/routerConfig/routes.go +++ b/internal/routerConfig/routes.go @@ -133,12 +133,13 @@ func setupTaglistRoute(i InfoType, r *http.Request) InfoType { return i } + // Uses tag.ID as second Map-Key component to differentiate tags with identical names for _, tag := range tags { tagItem := map[string]interface{}{ "id": tag.ID, "name": tag.Name, "scope": tag.Scope, - "count": counts[tag.Name], + "count": counts[fmt.Sprint(tag.Name, tag.ID)], } tagMap[tag.Type] = append(tagMap[tag.Type], tagItem) }