mirror of
https://github.com/ClusterCockpit/cc-backend
synced 2025-11-26 03:23:07 +01:00
Add documentation to repository package
This commit is contained in:
@@ -2,6 +2,63 @@
|
|||||||
// All rights reserved. This file is part of cc-backend.
|
// All rights reserved. This file is part of cc-backend.
|
||||||
// Use of this source code is governed by a MIT-style
|
// Use of this source code is governed by a MIT-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// Package repository provides the data access layer for cc-backend using the repository pattern.
|
||||||
|
//
|
||||||
|
// The repository pattern abstracts database operations and provides a clean interface for
|
||||||
|
// data access. Each major entity (Job, User, Node, Tag) has its own repository with CRUD
|
||||||
|
// operations and specialized queries.
|
||||||
|
//
|
||||||
|
// # Database Connection
|
||||||
|
//
|
||||||
|
// Initialize the database connection before using any repository:
|
||||||
|
//
|
||||||
|
// repository.Connect("sqlite3", "./var/job.db")
|
||||||
|
// // or for MySQL:
|
||||||
|
// repository.Connect("mysql", "user:password@tcp(localhost:3306)/dbname")
|
||||||
|
//
|
||||||
|
// # Configuration
|
||||||
|
//
|
||||||
|
// Optional: Configure repository settings before initialization:
|
||||||
|
//
|
||||||
|
// repository.SetConfig(&repository.RepositoryConfig{
|
||||||
|
// CacheSize: 2 * 1024 * 1024, // 2MB cache
|
||||||
|
// MaxOpenConnections: 8, // Connection pool size
|
||||||
|
// MinRunningJobDuration: 300, // Filter threshold
|
||||||
|
// })
|
||||||
|
//
|
||||||
|
// If not configured, sensible defaults are used automatically.
|
||||||
|
//
|
||||||
|
// # Repositories
|
||||||
|
//
|
||||||
|
// - JobRepository: Job lifecycle management and querying
|
||||||
|
// - UserRepository: User management and authentication
|
||||||
|
// - NodeRepository: Cluster node state tracking
|
||||||
|
// - Tags: Job tagging and categorization
|
||||||
|
//
|
||||||
|
// # Caching
|
||||||
|
//
|
||||||
|
// Repositories use LRU caching to improve performance. Cache keys are constructed
|
||||||
|
// as "type:id" (e.g., "metadata:123"). Cache is automatically invalidated on
|
||||||
|
// mutations to maintain consistency.
|
||||||
|
//
|
||||||
|
// # Transaction Support
|
||||||
|
//
|
||||||
|
// For batch operations, use transactions:
|
||||||
|
//
|
||||||
|
// t, err := jobRepo.TransactionInit()
|
||||||
|
// if err != nil {
|
||||||
|
// return err
|
||||||
|
// }
|
||||||
|
// defer t.Rollback() // Rollback if not committed
|
||||||
|
//
|
||||||
|
// // Perform operations...
|
||||||
|
// jobRepo.TransactionAdd(t, query, args...)
|
||||||
|
//
|
||||||
|
// // Commit when done
|
||||||
|
// if err := t.Commit(); err != nil {
|
||||||
|
// return err
|
||||||
|
// }
|
||||||
package repository
|
package repository
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
package repository
|
package repository
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
@@ -14,65 +15,32 @@ import (
|
|||||||
sq "github.com/Masterminds/squirrel"
|
sq "github.com/Masterminds/squirrel"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Add the tag with id `tagId` to the job with the database id `jobId`.
|
// AddTag adds the tag with id `tagId` to the job with the database id `jobId`.
|
||||||
|
// Requires user authentication for security checks.
|
||||||
func (r *JobRepository) AddTag(user *schema.User, job int64, tag int64) ([]*schema.Tag, error) {
|
func (r *JobRepository) AddTag(user *schema.User, job int64, tag int64) ([]*schema.Tag, error) {
|
||||||
j, err := r.FindByIdWithUser(user, job)
|
j, err := r.FindByIdWithUser(user, job)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
cclog.Warn("Error while finding job by id")
|
cclog.Warnf("Error finding job %d for user %s: %v", job, user.Username, err)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
q := sq.Insert("jobtag").Columns("job_id", "tag_id").Values(job, tag)
|
return r.addJobTag(job, tag, j, func() ([]*schema.Tag, error) {
|
||||||
|
return r.GetTags(user, &job)
|
||||||
if _, err := q.RunWith(r.stmtCache).Exec(); err != nil {
|
})
|
||||||
s, _, _ := q.ToSql()
|
|
||||||
cclog.Errorf("Error adding tag with %s: %v", s, err)
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
tags, err := r.GetTags(user, &job)
|
|
||||||
if err != nil {
|
|
||||||
cclog.Warn("Error while getting tags for job")
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
archiveTags, err := r.getArchiveTags(&job)
|
|
||||||
if err != nil {
|
|
||||||
cclog.Warn("Error while getting tags for job")
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return tags, archive.UpdateTags(j, archiveTags)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AddTagDirect adds a tag without user security checks.
|
||||||
|
// Use only for internal/admin operations.
|
||||||
func (r *JobRepository) AddTagDirect(job int64, tag int64) ([]*schema.Tag, error) {
|
func (r *JobRepository) AddTagDirect(job int64, tag int64) ([]*schema.Tag, error) {
|
||||||
j, err := r.FindByIdDirect(job)
|
j, err := r.FindByIdDirect(job)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
cclog.Warn("Error while finding job by id")
|
cclog.Warnf("Error finding job %d: %v", job, err)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
q := sq.Insert("jobtag").Columns("job_id", "tag_id").Values(job, tag)
|
return r.addJobTag(job, tag, j, func() ([]*schema.Tag, error) {
|
||||||
|
return r.GetTagsDirect(&job)
|
||||||
if _, err := q.RunWith(r.stmtCache).Exec(); err != nil {
|
})
|
||||||
s, _, _ := q.ToSql()
|
|
||||||
cclog.Errorf("Error adding tag with %s: %v", s, err)
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
tags, err := r.GetTagsDirect(&job)
|
|
||||||
if err != nil {
|
|
||||||
cclog.Warn("Error while getting tags for job")
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
archiveTags, err := r.getArchiveTags(&job)
|
|
||||||
if err != nil {
|
|
||||||
cclog.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.
|
// Removes a tag from a job by tag id.
|
||||||
@@ -260,15 +228,18 @@ func (r *JobRepository) CountTags(user *schema.User) (tags []schema.Tag, counts
|
|||||||
LeftJoin("jobtag jt ON t.id = jt.tag_id").
|
LeftJoin("jobtag jt ON t.id = jt.tag_id").
|
||||||
GroupBy("t.tag_name")
|
GroupBy("t.tag_name")
|
||||||
|
|
||||||
// Handle Scope Filtering
|
// Build scope list for filtering
|
||||||
scopeList := "\"global\""
|
var scopeBuilder strings.Builder
|
||||||
|
scopeBuilder.WriteString(`"global"`)
|
||||||
if user != nil {
|
if user != nil {
|
||||||
scopeList += ",\"" + user.Username + "\""
|
scopeBuilder.WriteString(`,"`)
|
||||||
|
scopeBuilder.WriteString(user.Username)
|
||||||
|
scopeBuilder.WriteString(`"`)
|
||||||
|
if user.HasAnyRole([]schema.Role{schema.RoleAdmin, schema.RoleSupport}) {
|
||||||
|
scopeBuilder.WriteString(`,"admin"`)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if user.HasAnyRole([]schema.Role{schema.RoleAdmin, schema.RoleSupport}) {
|
q = q.Where("t.tag_scope IN (" + scopeBuilder.String() + ")")
|
||||||
scopeList += ",\"admin\""
|
|
||||||
}
|
|
||||||
q = q.Where("t.tag_scope IN (" + scopeList + ")")
|
|
||||||
|
|
||||||
// Handle Job Ownership
|
// Handle Job Ownership
|
||||||
if user != nil && user.HasAnyRole([]schema.Role{schema.RoleAdmin, schema.RoleSupport}) { // ADMIN || SUPPORT: Count all jobs
|
if user != nil && user.HasAnyRole([]schema.Role{schema.RoleAdmin, schema.RoleSupport}) { // ADMIN || SUPPORT: Count all jobs
|
||||||
@@ -302,6 +273,41 @@ func (r *JobRepository) CountTags(user *schema.User) (tags []schema.Tag, counts
|
|||||||
return tags, counts, err
|
return tags, counts, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrTagNotFound = errors.New("the tag does not exist")
|
||||||
|
ErrJobNotOwned = errors.New("user is not owner of job")
|
||||||
|
ErrTagNoAccess = errors.New("user not permitted to use that tag")
|
||||||
|
ErrTagPrivateScope = errors.New("tag is private to another user")
|
||||||
|
ErrTagAdminScope = errors.New("tag requires admin privileges")
|
||||||
|
ErrTagsIncompatScopes = errors.New("combining admin and non-admin scoped tags not allowed")
|
||||||
|
)
|
||||||
|
|
||||||
|
// addJobTag is a helper function that inserts a job-tag association and updates the archive.
|
||||||
|
// Returns the updated tag list for the job.
|
||||||
|
func (r *JobRepository) addJobTag(jobId int64, tagId int64, job *schema.Job, getTags func() ([]*schema.Tag, error)) ([]*schema.Tag, error) {
|
||||||
|
q := sq.Insert("jobtag").Columns("job_id", "tag_id").Values(jobId, tagId)
|
||||||
|
|
||||||
|
if _, err := q.RunWith(r.stmtCache).Exec(); err != nil {
|
||||||
|
s, _, _ := q.ToSql()
|
||||||
|
cclog.Errorf("Error adding tag with %s: %v", s, err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
tags, err := getTags()
|
||||||
|
if err != nil {
|
||||||
|
cclog.Warnf("Error getting tags for job %d: %v", jobId, err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
archiveTags, err := r.getArchiveTags(&jobId)
|
||||||
|
if err != nil {
|
||||||
|
cclog.Warnf("Error getting archive tags for job %d: %v", jobId, err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return tags, archive.UpdateTags(job, archiveTags)
|
||||||
|
}
|
||||||
|
|
||||||
// AddTagOrCreate adds the tag with the specified type and name to the job with the database id `jobId`.
|
// AddTagOrCreate adds the tag with the specified type and name to the job with the database id `jobId`.
|
||||||
// If such a tag does not yet exist, it is created.
|
// If such a tag does not yet exist, it is created.
|
||||||
func (r *JobRepository) AddTagOrCreate(user *schema.User, jobId int64, tagType string, tagName string, tagScope string) (tagId int64, err error) {
|
func (r *JobRepository) AddTagOrCreate(user *schema.User, jobId int64, tagType string, tagName string, tagScope string) (tagId int64, err error) {
|
||||||
|
|||||||
Reference in New Issue
Block a user