mirror of
https://github.com/GlueOps/autoglue.git
synced 2026-02-13 04:40:05 +01:00
fix: refactor labels to use common entries between model and dto
This commit is contained in:
@@ -36,6 +36,7 @@ func NewRuntime() *Runtime {
|
||||
&models.Server{},
|
||||
&models.Taint{},
|
||||
&models.Label{},
|
||||
&models.Annotation{},
|
||||
)
|
||||
if err != nil {
|
||||
log.Fatalf("Error initializing database: %v", err)
|
||||
|
||||
14
internal/common/audit.go
Normal file
14
internal/common/audit.go
Normal file
@@ -0,0 +1,14 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
type AuditFields struct {
|
||||
ID uuid.UUID `json:"id" gorm:"type:uuid;default:gen_random_uuid()"`
|
||||
OrganizationID uuid.UUID `json:"organization_id" gorm:"type:uuid;index"`
|
||||
CreatedAt time.Time `json:"created_at,omitempty" gorm:"column:created_at;not null;default:now()"`
|
||||
UpdatedAt time.Time `json:"updated_at,omitempty" gorm:"autoUpdateTime;column:updated_at;not null;default:now()"`
|
||||
}
|
||||
21
internal/handlers/annotations.go
Normal file
21
internal/handlers/annotations.go
Normal file
@@ -0,0 +1,21 @@
|
||||
package handlers
|
||||
|
||||
// ListAnnotations godoc
|
||||
// @ID ListAnnotations
|
||||
// @Summary List annotations (org scoped)
|
||||
// @Description Returns annotations for the organization in X-Org-ID. Filters: `name`, `value`, and `q` (name contains). Add `include=node_pools` to include linked node pools.
|
||||
// @Tags Annotations
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param X-Org-ID header string false "Organization UUID"
|
||||
// @Param name query string false "Exact name"
|
||||
// @Param value query string false "Exact value"
|
||||
// @Param q query string false "name contains (case-insensitive)"
|
||||
// @Success 200 {array} annotationResponse
|
||||
// @Failure 401 {string} string "Unauthorized"
|
||||
// @Failure 403 {string} string "organization required"
|
||||
// @Failure 500 {string} string "failed to list annotations"
|
||||
// @Router /api/v1/annotations [get]
|
||||
// @Security BearerAuth
|
||||
// @Security OrgKeyAuth
|
||||
// @Security OrgSecretAuth
|
||||
19
internal/handlers/dto/annotations.go
Normal file
19
internal/handlers/dto/annotations.go
Normal file
@@ -0,0 +1,19 @@
|
||||
package dto
|
||||
|
||||
import "github.com/glueops/autoglue/internal/common"
|
||||
|
||||
type AnnotationResponse struct {
|
||||
common.AuditFields
|
||||
Key string `json:"key"`
|
||||
Value string `json:"value"`
|
||||
}
|
||||
|
||||
type CreateAnnotationRequest struct {
|
||||
Key string `json:"key"`
|
||||
Value string `json:"value"`
|
||||
}
|
||||
|
||||
type UpdateAnnotationRequest struct {
|
||||
Key *string `json:"key,omitempty"`
|
||||
Value *string `json:"value,omitempty"`
|
||||
}
|
||||
@@ -1,14 +1,13 @@
|
||||
package dto
|
||||
|
||||
import "github.com/google/uuid"
|
||||
import (
|
||||
"github.com/glueops/autoglue/internal/common"
|
||||
)
|
||||
|
||||
type LabelResponse struct {
|
||||
ID uuid.UUID `json:"id"`
|
||||
OrganizationID uuid.UUID `json:"organization_id"`
|
||||
Key string `json:"key"`
|
||||
Value string `json:"value"`
|
||||
CreatedAt string `json:"created_at,omitempty"`
|
||||
UpdatedAt string `json:"updated_at,omitempty"`
|
||||
common.AuditFields
|
||||
Key string `json:"key"`
|
||||
Value string `json:"value"`
|
||||
}
|
||||
|
||||
type CreateLabelRequest struct {
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/glueops/autoglue/internal/api/httpmiddleware"
|
||||
"github.com/glueops/autoglue/internal/common"
|
||||
"github.com/glueops/autoglue/internal/handlers/dto"
|
||||
"github.com/glueops/autoglue/internal/models"
|
||||
"github.com/glueops/autoglue/internal/utils"
|
||||
@@ -53,20 +54,12 @@ func ListLabels(db *gorm.DB) http.HandlerFunc {
|
||||
if needle := strings.TrimSpace(r.URL.Query().Get("q")); needle != "" {
|
||||
q = q.Where(`key ILIKE ?`, "%"+needle+"%")
|
||||
}
|
||||
var rows []models.Label
|
||||
if err := q.Order("created_at DESC").Find(&rows).Error; err != nil {
|
||||
var out []dto.LabelResponse
|
||||
if err := q.Model(&models.Label{}).Order("created_at DESC").Scan(&out).Error; err != nil {
|
||||
utils.WriteError(w, http.StatusInternalServerError, "db_error", "db error")
|
||||
return
|
||||
}
|
||||
|
||||
out := make([]dto.LabelResponse, 0, len(rows))
|
||||
for _, row := range rows {
|
||||
out = append(out, dto.LabelResponse{
|
||||
Key: row.Key,
|
||||
Value: row.Value,
|
||||
ID: row.ID,
|
||||
})
|
||||
}
|
||||
utils.WriteJSON(w, http.StatusOK, out)
|
||||
}
|
||||
}
|
||||
@@ -104,17 +97,14 @@ func GetLabel(db *gorm.DB) http.HandlerFunc {
|
||||
return
|
||||
}
|
||||
|
||||
var row models.Label
|
||||
if err := db.Where("id = ? AND organization_id = ?", id, orgID).First(&row).Error; err != nil {
|
||||
utils.WriteError(w, http.StatusNotFound, "label_not_found", "label not found")
|
||||
return
|
||||
var out dto.LabelResponse
|
||||
if err := db.Model(&models.Label{}).Where("id = ? AND organization_id = ?", id, orgID).Limit(1).Scan(&out).Error; err != nil {
|
||||
if out.ID == uuid.Nil {
|
||||
utils.WriteError(w, http.StatusNotFound, "label_not_found", "label not found")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
out := dto.LabelResponse{
|
||||
Key: row.Key,
|
||||
Value: row.Value,
|
||||
ID: row.ID,
|
||||
}
|
||||
utils.WriteJSON(w, http.StatusOK, out)
|
||||
}
|
||||
}
|
||||
@@ -160,9 +150,9 @@ func CreateLabel(db *gorm.DB) http.HandlerFunc {
|
||||
}
|
||||
|
||||
l := models.Label{
|
||||
OrganizationID: orgID,
|
||||
Key: req.Key,
|
||||
Value: req.Value,
|
||||
AuditFields: common.AuditFields{OrganizationID: orgID},
|
||||
Key: req.Key,
|
||||
Value: req.Value,
|
||||
}
|
||||
if err := db.Create(&l).Error; err != nil {
|
||||
utils.WriteError(w, http.StatusInternalServerError, "db_error", "db error")
|
||||
@@ -170,14 +160,15 @@ func CreateLabel(db *gorm.DB) http.HandlerFunc {
|
||||
}
|
||||
|
||||
out := dto.LabelResponse{
|
||||
ID: l.ID,
|
||||
Key: l.Key,
|
||||
Value: l.Value,
|
||||
AuditFields: l.AuditFields,
|
||||
Key: l.Key,
|
||||
Value: l.Value,
|
||||
}
|
||||
utils.WriteJSON(w, http.StatusCreated, out)
|
||||
}
|
||||
}
|
||||
|
||||
// UpdateLabel godoc
|
||||
// UpdateLabel godoc
|
||||
// @ID UpdateLabel
|
||||
// @Summary Update label (org scoped)
|
||||
@@ -228,22 +219,22 @@ func UpdateLabel(db *gorm.DB) http.HandlerFunc {
|
||||
return
|
||||
}
|
||||
|
||||
next := l
|
||||
if req.Key != nil {
|
||||
next.Key = strings.TrimSpace(*req.Key)
|
||||
l.Key = strings.TrimSpace(*req.Key)
|
||||
}
|
||||
if req.Value != nil {
|
||||
next.Value = strings.TrimSpace(*req.Value)
|
||||
l.Value = strings.TrimSpace(*req.Value)
|
||||
}
|
||||
|
||||
if err := db.Save(&next).Error; err != nil {
|
||||
if err := db.Save(&l).Error; err != nil {
|
||||
utils.WriteError(w, http.StatusInternalServerError, "db_error", "db error")
|
||||
return
|
||||
}
|
||||
|
||||
out := dto.LabelResponse{
|
||||
ID: next.ID,
|
||||
Key: next.Key,
|
||||
Value: next.Value,
|
||||
AuditFields: l.AuditFields,
|
||||
Key: l.Key,
|
||||
Value: l.Value,
|
||||
}
|
||||
utils.WriteJSON(w, http.StatusOK, out)
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ type userAPIKeyOut struct {
|
||||
// ListUserAPIKeys godoc
|
||||
// @ID ListUserAPIKeys
|
||||
// @Summary List my API keys
|
||||
// @Tags Me / API Keys
|
||||
// @Tags MeAPIKeys
|
||||
// @Produce json
|
||||
// @Success 200 {array} userAPIKeyOut
|
||||
// @Router /me/api-keys [get]
|
||||
@@ -67,7 +67,7 @@ type createUserKeyRequest struct {
|
||||
// @ID CreateUserAPIKey
|
||||
// @Summary Create a new user API key
|
||||
// @Description Returns the plaintext key once. Store it securely on the client side.
|
||||
// @Tags Me / API Keys
|
||||
// @Tags MeAPIKeys
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param body body createUserKeyRequest true "Key options"
|
||||
@@ -120,7 +120,7 @@ func CreateUserAPIKey(db *gorm.DB) http.HandlerFunc {
|
||||
// DeleteUserAPIKey godoc
|
||||
// @ID DeleteUserAPIKey
|
||||
// @Summary Delete a user API key
|
||||
// @Tags Me / API Keys
|
||||
// @Tags MeAPIKeys
|
||||
// @Produce json
|
||||
// @Param id path string true "Key ID (UUID)"
|
||||
// @Success 204 "No Content"
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"errors"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/glueops/autoglue/internal/api/httpmiddleware"
|
||||
"github.com/glueops/autoglue/internal/handlers/dto"
|
||||
@@ -63,10 +64,13 @@ func ListTaints(db *gorm.DB) http.HandlerFunc {
|
||||
out := make([]dto.TaintResponse, 0, len(rows))
|
||||
for _, row := range rows {
|
||||
out = append(out, dto.TaintResponse{
|
||||
ID: row.ID,
|
||||
Key: row.Key,
|
||||
Value: row.Value,
|
||||
Effect: row.Effect,
|
||||
ID: row.ID,
|
||||
Key: row.Key,
|
||||
Value: row.Value,
|
||||
Effect: row.Effect,
|
||||
OrganizationID: row.OrganizationID,
|
||||
CreatedAt: row.CreatedAt.UTC().Format(time.RFC3339),
|
||||
UpdatedAt: row.UpdatedAt.UTC().Format(time.RFC3339),
|
||||
})
|
||||
}
|
||||
utils.WriteJSON(w, http.StatusOK, out)
|
||||
@@ -115,10 +119,13 @@ func GetTaint(db *gorm.DB) http.HandlerFunc {
|
||||
return
|
||||
}
|
||||
out := dto.TaintResponse{
|
||||
ID: row.ID,
|
||||
Key: row.Key,
|
||||
Value: row.Value,
|
||||
Effect: row.Effect,
|
||||
ID: row.ID,
|
||||
Key: row.Key,
|
||||
Value: row.Value,
|
||||
Effect: row.Effect,
|
||||
OrganizationID: row.OrganizationID,
|
||||
CreatedAt: row.CreatedAt.UTC().Format(time.RFC3339),
|
||||
UpdatedAt: row.UpdatedAt.UTC().Format(time.RFC3339),
|
||||
}
|
||||
utils.WriteJSON(w, http.StatusOK, out)
|
||||
}
|
||||
@@ -182,10 +189,13 @@ func CreateTaint(db *gorm.DB) http.HandlerFunc {
|
||||
}
|
||||
|
||||
out := dto.TaintResponse{
|
||||
ID: t.ID,
|
||||
Key: t.Key,
|
||||
Value: t.Value,
|
||||
Effect: t.Effect,
|
||||
ID: t.ID,
|
||||
Key: t.Key,
|
||||
Value: t.Value,
|
||||
Effect: t.Effect,
|
||||
OrganizationID: t.OrganizationID,
|
||||
CreatedAt: t.CreatedAt.UTC().Format(time.RFC3339),
|
||||
UpdatedAt: t.UpdatedAt.UTC().Format(time.RFC3339),
|
||||
}
|
||||
utils.WriteJSON(w, http.StatusCreated, out)
|
||||
}
|
||||
@@ -268,10 +278,13 @@ func UpdateTaint(db *gorm.DB) http.HandlerFunc {
|
||||
}
|
||||
|
||||
out := dto.TaintResponse{
|
||||
ID: next.ID,
|
||||
Key: next.Key,
|
||||
Value: next.Value,
|
||||
Effect: next.Effect,
|
||||
ID: next.ID,
|
||||
Key: next.Key,
|
||||
Value: next.Value,
|
||||
Effect: next.Effect,
|
||||
OrganizationID: next.OrganizationID,
|
||||
CreatedAt: next.CreatedAt.UTC().Format(time.RFC3339),
|
||||
UpdatedAt: next.UpdatedAt.UTC().Format(time.RFC3339),
|
||||
}
|
||||
utils.WriteJSON(w, http.StatusOK, out)
|
||||
}
|
||||
|
||||
12
internal/models/annotation.go
Normal file
12
internal/models/annotation.go
Normal file
@@ -0,0 +1,12 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"github.com/glueops/autoglue/internal/common"
|
||||
)
|
||||
|
||||
type Annotation struct {
|
||||
common.AuditFields `gorm:"embedded"`
|
||||
Organization Organization `gorm:"foreignKey:OrganizationID;constraint:OnDelete:CASCADE" json:"organization"`
|
||||
Key string `gorm:"not null" json:"key"`
|
||||
Value string `gorm:"not null" json:"value"`
|
||||
}
|
||||
@@ -1,18 +1,13 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/glueops/autoglue/internal/common"
|
||||
)
|
||||
|
||||
type Label struct {
|
||||
ID uuid.UUID `gorm:"type:uuid;default:gen_random_uuid();primaryKey" json:"id"`
|
||||
OrganizationID uuid.UUID `gorm:"type:uuid;not null" json:"organization_id"`
|
||||
Organization Organization `gorm:"foreignKey:OrganizationID;constraint:OnDelete:CASCADE" json:"organization"`
|
||||
Key string `gorm:"not null" json:"key"`
|
||||
Value string `gorm:"not null" json:"value"`
|
||||
NodePools []NodePool `gorm:"many2many:node_labels;constraint:OnDelete:CASCADE" json:"servers,omitempty"`
|
||||
CreatedAt time.Time `gorm:"column:created_at;not null;default:now()" json:"created_at"`
|
||||
UpdatedAt time.Time `gorm:"autoUpdateTime;column:updated_at;not null;default:now()" json:"updated_at"`
|
||||
common.AuditFields
|
||||
Organization Organization `gorm:"foreignKey:OrganizationID;constraint:OnDelete:CASCADE" json:"organization"`
|
||||
Key string `gorm:"not null" json:"key"`
|
||||
Value string `gorm:"not null" json:"value"`
|
||||
NodePools []NodePool `gorm:"many2many:node_labels;constraint:OnDelete:CASCADE" json:"servers,omitempty"`
|
||||
}
|
||||
|
||||
@@ -12,9 +12,9 @@ type NodePool struct {
|
||||
Organization Organization `gorm:"foreignKey:OrganizationID;constraint:OnDelete:CASCADE" json:"organization"`
|
||||
Name string `gorm:"not null" json:"name"`
|
||||
Servers []Server `gorm:"many2many:node_servers;constraint:OnDelete:CASCADE" json:"servers,omitempty"`
|
||||
//Annotations []Annotation `gorm:"many2many:node_annotations;constraint:OnDelete:CASCADE" json:"annotations,omitempty"`
|
||||
Labels []Label `gorm:"many2many:node_labels;constraint:OnDelete:CASCADE" json:"labels,omitempty"`
|
||||
Taints []Taint `gorm:"many2many:node_taints;constraint:OnDelete:CASCADE" json:"taints,omitempty"`
|
||||
Annotations []Annotation `gorm:"many2many:node_annotations;constraint:OnDelete:CASCADE" json:"annotations,omitempty"`
|
||||
Labels []Label `gorm:"many2many:node_labels;constraint:OnDelete:CASCADE" json:"labels,omitempty"`
|
||||
Taints []Taint `gorm:"many2many:node_taints;constraint:OnDelete:CASCADE" json:"taints,omitempty"`
|
||||
//Clusters []Cluster `gorm:"many2many:cluster_node_pools;constraint:OnDelete:CASCADE" json:"clusters,omitempty"`
|
||||
Topology string `gorm:"not null" json:"topology,omitempty"` // stacked or external
|
||||
Role string `gorm:"not null" json:"role,omitempty"` // master, worker, or etcd (etcd only if topology = external
|
||||
|
||||
Reference in New Issue
Block a user