From 7abdd0545e5a2ad7e1906411f38c1397185e8ef9 Mon Sep 17 00:00:00 2001 From: Jan Eitzinger Date: Fri, 16 May 2025 07:24:24 +0200 Subject: [PATCH] Add api for tag handling within cc-backend --- internal/graph/schema.resolvers.go | 37 +++++++------- internal/repository/tags.go | 78 +++++++++++++++++++++++++++++- internal/tagger/detectApp.go | 6 +-- internal/tagger/detectApp_test.go | 2 +- 4 files changed, 98 insertions(+), 25 deletions(-) diff --git a/internal/graph/schema.resolvers.go b/internal/graph/schema.resolvers.go index f3fc389..7e52b3d 100644 --- a/internal/graph/schema.resolvers.go +++ b/internal/graph/schema.resolvers.go @@ -143,7 +143,7 @@ func (r *mutationResolver) CreateTag(ctx context.Context, typeArg string, name s return &schema.Tag{ID: id, Type: typeArg, Name: name, Scope: scope}, nil } else { log.Warnf("Not authorized to create tag with scope: %s", scope) - return nil, fmt.Errorf("Not authorized to create tag with scope: %s", scope) + return nil, fmt.Errorf("not authorized to create tag with scope: %s", scope) } } @@ -179,7 +179,7 @@ func (r *mutationResolver) AddTagsToJob(ctx context.Context, job string, tagIds _, _, tscope, exists := r.Repo.TagInfo(tid) if !exists { log.Warnf("Tag does not exist (ID): %d", tid) - return nil, fmt.Errorf("Tag does not exist (ID): %d", tid) + return nil, fmt.Errorf("tag does not exist (ID): %d", tid) } // Test Access: Admins && Admin Tag OR Support/Admin and Global Tag OR Everyone && Private Tag @@ -193,7 +193,7 @@ func (r *mutationResolver) AddTagsToJob(ctx context.Context, job string, tagIds } } else { log.Warnf("Not authorized to add tag: %d", tid) - return nil, fmt.Errorf("Not authorized to add tag: %d", tid) + return nil, fmt.Errorf("not authorized to add tag: %d", tid) } } @@ -226,7 +226,7 @@ func (r *mutationResolver) RemoveTagsFromJob(ctx context.Context, job string, ta _, _, tscope, exists := r.Repo.TagInfo(tid) if !exists { log.Warnf("Tag does not exist (ID): %d", tid) - return nil, fmt.Errorf("Tag does not exist (ID): %d", tid) + return nil, fmt.Errorf("tag does not exist (ID): %d", tid) } // Test Access: Admins && Admin Tag OR Support/Admin and Global Tag OR Everyone && Private Tag @@ -240,7 +240,7 @@ func (r *mutationResolver) RemoveTagsFromJob(ctx context.Context, job string, ta } } else { log.Warnf("Not authorized to remove tag: %d", tid) - return nil, fmt.Errorf("Not authorized to remove tag: %d", tid) + return nil, fmt.Errorf("not authorized to remove tag: %d", tid) } } @@ -269,7 +269,7 @@ func (r *mutationResolver) RemoveTagFromList(ctx context.Context, tagIds []strin _, _, tscope, exists := r.Repo.TagInfo(tid) if !exists { log.Warnf("Tag does not exist (ID): %d", tid) - return nil, fmt.Errorf("Tag does not exist (ID): %d", tid) + return nil, fmt.Errorf("tag does not exist (ID): %d", tid) } // Test Access: Admins && Admin Tag OR Everyone && Private Tag @@ -283,7 +283,7 @@ func (r *mutationResolver) RemoveTagFromList(ctx context.Context, tagIds []strin } } else { log.Warnf("Not authorized to remove tag: %d", tid) - return nil, fmt.Errorf("Not authorized to remove tag: %d", tid) + return nil, fmt.Errorf("not authorized to remove tag: %d", tid) } } return tags, nil @@ -499,10 +499,7 @@ func (r *queryResolver) Jobs(ctx context.Context, filter []*model.JobFilter, pag return nil, err } - hasNextPage := false - if len(nextJobs) == 1 { - hasNextPage = true - } + hasNextPage := len(nextJobs) == 1 return &model.JobResultList{Items: jobs, Count: &count, HasNextPage: &hasNextPage}, nil } @@ -513,8 +510,8 @@ func (r *queryResolver) JobsStatistics(ctx context.Context, filter []*model.JobF var stats []*model.JobsStatistics // Top Level Defaults - var defaultDurationBins string = "1h" - var defaultMetricBins int = 10 + defaultDurationBins := "1h" + defaultMetricBins := 10 if requireField(ctx, "totalJobs") || requireField(ctx, "totalWalltime") || requireField(ctx, "totalNodes") || requireField(ctx, "totalCores") || requireField(ctx, "totalAccs") || requireField(ctx, "totalNodeHours") || requireField(ctx, "totalCoreHours") || requireField(ctx, "totalAccHours") { @@ -779,9 +776,11 @@ func (r *Resolver) Query() generated.QueryResolver { return &queryResolver{r} } // SubCluster returns generated.SubClusterResolver implementation. func (r *Resolver) SubCluster() generated.SubClusterResolver { return &subClusterResolver{r} } -type clusterResolver struct{ *Resolver } -type jobResolver struct{ *Resolver } -type metricValueResolver struct{ *Resolver } -type mutationResolver struct{ *Resolver } -type queryResolver struct{ *Resolver } -type subClusterResolver struct{ *Resolver } +type ( + clusterResolver struct{ *Resolver } + jobResolver struct{ *Resolver } + metricValueResolver struct{ *Resolver } + mutationResolver struct{ *Resolver } + queryResolver struct{ *Resolver } + subClusterResolver struct{ *Resolver } +) diff --git a/internal/repository/tags.go b/internal/repository/tags.go index d07c4d2..a9416c4 100644 --- a/internal/repository/tags.go +++ b/internal/repository/tags.go @@ -45,6 +45,36 @@ func (r *JobRepository) AddTag(user *schema.User, job int64, tag int64) ([]*sche return tags, archive.UpdateTags(j, archiveTags) } +func (r *JobRepository) AddTagDirect(job int64, tag int64) ([]*schema.Tag, error) { + j, err := r.FindByIdDirect(job) + if err != nil { + log.Warn("Error while finding job by id") + return nil, err + } + + q := sq.Insert("jobtag").Columns("job_id", "tag_id").Values(job, tag) + + if _, err := q.RunWith(r.stmtCache).Exec(); err != nil { + s, _, _ := q.ToSql() + log.Errorf("Error adding tag with %s: %v", s, err) + return nil, err + } + + tags, err := r.GetTagsDirect(&job) + if err != nil { + log.Warn("Error while getting tags for job") + return nil, err + } + + archiveTags, err := r.getArchiveTags(&job) + if err != nil { + log.Warn("Error while getting tags for job") + return nil, err + } + + return tags, archive.UpdateTags(j, archiveTags) +} + // Removes a tag from a job by tag id func (r *JobRepository) RemoveTag(user *schema.User, job, tag int64) ([]*schema.Tag, error) { j, err := r.FindByIdWithUser(user, job) @@ -82,7 +112,7 @@ func (r *JobRepository) RemoveJobTagByRequest(user *schema.User, job int64, tagT tagID, exists := r.TagId(tagType, tagName, tagScope) if !exists { log.Warnf("Tag does not exist (name, type, scope): %s, %s, %s", tagName, tagType, tagScope) - return nil, fmt.Errorf("Tag does not exist (name, type, scope): %s, %s, %s", tagName, tagType, tagScope) + return nil, fmt.Errorf("tag does not exist (name, type, scope): %s, %s, %s", tagName, tagType, tagScope) } // Get Job @@ -122,7 +152,7 @@ func (r *JobRepository) RemoveTagByRequest(tagType string, tagName string, tagSc tagID, exists := r.TagId(tagType, tagName, tagScope) if !exists { log.Warnf("Tag does not exist (name, type, scope): %s, %s, %s", tagName, tagType, tagScope) - return fmt.Errorf("Tag does not exist (name, type, scope): %s, %s, %s", tagName, tagType, tagScope) + return fmt.Errorf("tag does not exist (name, type, scope): %s, %s, %s", tagName, tagType, tagScope) } // Handle Delete JobTagTable @@ -291,6 +321,24 @@ func (r *JobRepository) AddTagOrCreate(user *schema.User, jobId int64, tagType s return tagId, nil } +func (r *JobRepository) AddTagOrCreateDirect(jobId int64, tagType string, tagName string) (tagId int64, err error) { + tagScope := "global" + + tagId, exists := r.TagId(tagType, tagName, tagScope) + if !exists { + tagId, err = r.CreateTag(tagType, tagName, tagScope) + if err != nil { + return 0, err + } + } + + if _, err := r.AddTagDirect(jobId, tagId); err != nil { + return 0, err + } + + return tagId, nil +} + func (r *JobRepository) HasTag(jobId int64, tagType string, tagName string) bool { var id int64 q := sq.Select("id").From("tag").Join("jobtag ON jobtag.tag_id = tag.id"). @@ -359,6 +407,32 @@ func (r *JobRepository) GetTags(user *schema.User, job *int64) ([]*schema.Tag, e return tags, nil } +func (r *JobRepository) GetTagsDirect(job *int64) ([]*schema.Tag, error) { + q := sq.Select("id", "tag_type", "tag_name", "tag_scope").From("tag") + if job != nil { + q = q.Join("jobtag ON jobtag.tag_id = tag.id").Where("jobtag.job_id = ?", *job) + } + + rows, err := q.RunWith(r.stmtCache).Query() + if err != nil { + s, _, _ := q.ToSql() + log.Errorf("Error get tags with %s: %v", s, err) + return nil, err + } + + tags := make([]*schema.Tag, 0) + for rows.Next() { + tag := &schema.Tag{} + if err := rows.Scan(&tag.ID, &tag.Type, &tag.Name, &tag.Scope); err != nil { + log.Warn("Error while scanning rows") + return nil, err + } + tags = append(tags, tag) + } + + return tags, nil +} + // 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") diff --git a/internal/tagger/detectApp.go b/internal/tagger/detectApp.go index 714fd27..339e398 100644 --- a/internal/tagger/detectApp.go +++ b/internal/tagger/detectApp.go @@ -1,5 +1,5 @@ -// Copyright (C) 2023 NHR@FAU, University Erlangen-Nuremberg. -// All rights reserved. +// Copyright (C) NHR@FAU, University Erlangen-Nuremberg. +// All rights reserved. This file is part of cc-backend. // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. package tagger @@ -72,7 +72,7 @@ func (t *AppTagger) Match(job *schema.Job) { for _, s := range a.strings { if strings.Contains(jobscript, s) { if !r.HasTag(id, tagType, tag) { - r.AddTagOrCreate(id, tagType, tag) + r.AddTagOrCreateDirect(id, tagType, tag) break out } } diff --git a/internal/tagger/detectApp_test.go b/internal/tagger/detectApp_test.go index 54a8dfd..8978e35 100644 --- a/internal/tagger/detectApp_test.go +++ b/internal/tagger/detectApp_test.go @@ -43,7 +43,7 @@ func TestRegister(t *testing.T) { func TestMatch(t *testing.T) { r := setup(t) - job, err := r.FindById(5) + job, err := r.FindByIdDirect(5) noErr(t, err) var tagger AppTagger