mirror of
https://github.com/shankar0123/certctl.git
synced 2026-06-11 13:08:58 +00:00
Complete V1 scaffold
This commit is contained in:
@@ -0,0 +1,162 @@
|
||||
package postgres
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/shankar0123/certctl/internal/domain"
|
||||
"github.com/shankar0123/certctl/internal/repository"
|
||||
)
|
||||
|
||||
// NotificationRepository implements repository.NotificationRepository
|
||||
type NotificationRepository struct {
|
||||
db *sql.DB
|
||||
}
|
||||
|
||||
// NewNotificationRepository creates a new NotificationRepository
|
||||
func NewNotificationRepository(db *sql.DB) *NotificationRepository {
|
||||
return &NotificationRepository{db: db}
|
||||
}
|
||||
|
||||
// Create stores a new notification
|
||||
func (r *NotificationRepository) Create(ctx context.Context, notif *domain.NotificationEvent) error {
|
||||
if notif.ID == "" {
|
||||
notif.ID = uuid.New().String()
|
||||
}
|
||||
|
||||
err := r.db.QueryRowContext(ctx, `
|
||||
INSERT INTO notifications (
|
||||
id, type, certificate_id, channel, recipient, message, sent_at, status, error, created_at
|
||||
) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)
|
||||
RETURNING id
|
||||
`, notif.ID, notif.Type, notif.CertificateID, notif.Channel, notif.Recipient,
|
||||
notif.Message, notif.SentAt, notif.Status, notif.Error, notif.CreatedAt).Scan(¬if.ID)
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create notification: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// List returns notifications matching the filter criteria
|
||||
func (r *NotificationRepository) List(ctx context.Context, filter *repository.NotificationFilter) ([]*domain.NotificationEvent, error) {
|
||||
if filter == nil {
|
||||
filter = &repository.NotificationFilter{}
|
||||
}
|
||||
|
||||
// Set defaults
|
||||
if filter.Page < 1 {
|
||||
filter.Page = 1
|
||||
}
|
||||
if filter.PerPage == 0 || filter.PerPage > 500 {
|
||||
filter.PerPage = 50
|
||||
}
|
||||
|
||||
// Build WHERE clause
|
||||
var whereConditions []string
|
||||
var args []interface{}
|
||||
argCount := 1
|
||||
|
||||
if filter.CertificateID != "" {
|
||||
whereConditions = append(whereConditions, fmt.Sprintf("certificate_id = $%d", argCount))
|
||||
args = append(args, filter.CertificateID)
|
||||
argCount++
|
||||
}
|
||||
if filter.Status != "" {
|
||||
whereConditions = append(whereConditions, fmt.Sprintf("status = $%d", argCount))
|
||||
args = append(args, filter.Status)
|
||||
argCount++
|
||||
}
|
||||
if filter.Channel != "" {
|
||||
whereConditions = append(whereConditions, fmt.Sprintf("channel = $%d", argCount))
|
||||
args = append(args, filter.Channel)
|
||||
argCount++
|
||||
}
|
||||
|
||||
whereClause := ""
|
||||
if len(whereConditions) > 0 {
|
||||
whereClause = "WHERE " + strings.Join(whereConditions, " AND ")
|
||||
}
|
||||
|
||||
// Get total count
|
||||
countQuery := fmt.Sprintf("SELECT COUNT(*) FROM notifications %s", whereClause)
|
||||
var total int
|
||||
if err := r.db.QueryRowContext(ctx, countQuery, args...).Scan(&total); err != nil {
|
||||
return nil, fmt.Errorf("failed to count notifications: %w", err)
|
||||
}
|
||||
|
||||
// Get paginated results
|
||||
offset := (filter.Page - 1) * filter.PerPage
|
||||
query := fmt.Sprintf(`
|
||||
SELECT id, type, certificate_id, channel, recipient, message, sent_at, status, error, created_at
|
||||
FROM notifications
|
||||
%s
|
||||
ORDER BY created_at DESC
|
||||
LIMIT $%d OFFSET $%d
|
||||
`, whereClause, argCount, argCount+1)
|
||||
|
||||
args = append(args, filter.PerPage, offset)
|
||||
|
||||
rows, err := r.db.QueryContext(ctx, query, args...)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to query notifications: %w", err)
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var notifs []*domain.NotificationEvent
|
||||
for rows.Next() {
|
||||
notif, err := scanNotification(rows)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
notifs = append(notifs, notif)
|
||||
}
|
||||
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, fmt.Errorf("error iterating notification rows: %w", err)
|
||||
}
|
||||
|
||||
return notifs, nil
|
||||
}
|
||||
|
||||
// UpdateStatus updates a notification's delivery status
|
||||
func (r *NotificationRepository) UpdateStatus(ctx context.Context, id string, status string, sentAt time.Time) error {
|
||||
result, err := r.db.ExecContext(ctx, `
|
||||
UPDATE notifications SET status = $1, sent_at = $2 WHERE id = $3
|
||||
`, status, sentAt, id)
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to update notification status: %w", err)
|
||||
}
|
||||
|
||||
rows, err := result.RowsAffected()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get rows affected: %w", err)
|
||||
}
|
||||
|
||||
if rows == 0 {
|
||||
return fmt.Errorf("notification not found")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// scanNotification scans a notification from a row or rows
|
||||
func scanNotification(scanner interface {
|
||||
Scan(...interface{}) error
|
||||
}) (*domain.NotificationEvent, error) {
|
||||
var notif domain.NotificationEvent
|
||||
err := scanner.Scan(¬if.ID, ¬if.Type, ¬if.CertificateID, ¬if.Channel,
|
||||
¬if.Recipient, ¬if.Message, ¬if.SentAt, ¬if.Status, ¬if.Error, ¬if.CreatedAt)
|
||||
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to scan notification: %w", err)
|
||||
}
|
||||
|
||||
return ¬if, nil
|
||||
}
|
||||
Reference in New Issue
Block a user