103 lines
2.4 KiB
Go
103 lines
2.4 KiB
Go
package db
|
|
|
|
import (
|
|
"context"
|
|
|
|
"github.com/pkg/errors"
|
|
"github.com/uptrace/bun"
|
|
)
|
|
|
|
type deleter[T any] struct {
|
|
tx bun.Tx
|
|
q *bun.DeleteQuery
|
|
resourceID any // Store ID before deletion for audit
|
|
audit *AuditMeta
|
|
auditInfo *AuditInfo
|
|
}
|
|
|
|
type systemType interface {
|
|
isSystem() bool
|
|
}
|
|
|
|
func DeleteItem[T any](tx bun.Tx) *deleter[T] {
|
|
return &deleter[T]{
|
|
tx: tx,
|
|
q: tx.NewDelete().
|
|
Model((*T)(nil)),
|
|
}
|
|
}
|
|
|
|
func (d *deleter[T]) Where(query string, args ...any) *deleter[T] {
|
|
d.q = d.q.Where(query, args...)
|
|
// Try to capture resource ID from WHERE clause if it's a simple "id = ?" pattern
|
|
if query == "id = ?" && len(args) > 0 {
|
|
d.resourceID = args[0]
|
|
}
|
|
return d
|
|
}
|
|
|
|
// WithAudit enables audit logging for this delete operation
|
|
// If the provided *AuditInfo is nil, will use reflection to automatically work out the details
|
|
func (d *deleter[T]) WithAudit(meta *AuditMeta, info *AuditInfo) *deleter[T] {
|
|
d.audit = meta
|
|
d.auditInfo = info
|
|
return d
|
|
}
|
|
|
|
func (d *deleter[T]) Delete(ctx context.Context) error {
|
|
result, err := d.q.Exec(ctx)
|
|
if err != nil {
|
|
return errors.Wrap(err, "bun.DeleteQuery.Exec")
|
|
}
|
|
rows, err := result.RowsAffected()
|
|
if err != nil {
|
|
return errors.Wrap(err, "result.RowsAffected")
|
|
}
|
|
if rows == 0 {
|
|
resource := extractResourceType(extractTableName[T]())
|
|
return BadRequestNotFound(resource, "id", d.resourceID)
|
|
}
|
|
|
|
// Handle audit logging if enabled
|
|
if d.audit != nil {
|
|
if d.auditInfo == nil {
|
|
tableName := extractTableName[T]()
|
|
resourceType := extractResourceType(tableName)
|
|
action := buildAction(resourceType, "delete")
|
|
|
|
d.auditInfo = &AuditInfo{
|
|
Action: action,
|
|
ResourceType: resourceType,
|
|
ResourceID: d.resourceID,
|
|
Details: nil, // Delete doesn't need details
|
|
}
|
|
}
|
|
|
|
err = LogSuccess(ctx, d.tx, d.audit, d.auditInfo)
|
|
if err != nil {
|
|
return errors.Wrap(err, "LogSuccess")
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func DeleteByID[T any](tx bun.Tx, id int) *deleter[T] {
|
|
return DeleteItem[T](tx).Where("id = ?", id)
|
|
}
|
|
|
|
func DeleteWithProtection[T systemType](ctx context.Context, tx bun.Tx, id int, audit *AuditMeta) error {
|
|
deleter := DeleteByID[T](tx, id)
|
|
item, err := GetByID[T](tx, id).Get(ctx)
|
|
if err != nil {
|
|
return errors.Wrap(err, "GetByID")
|
|
}
|
|
if (*item).isSystem() {
|
|
return errors.New("record is system protected")
|
|
}
|
|
if audit != nil {
|
|
deleter = deleter.WithAudit(audit, nil)
|
|
}
|
|
return deleter.Delete(ctx)
|
|
}
|