Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 0 additions & 5 deletions alerter/src/internal/database/datastore.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,8 +70,3 @@ func (d *Datastore) Close() {
d.pool.Close()
}
}

// Pool returns the underlying connection pool
func (d *Datastore) Pool() *pgxpool.Pool {
return d.pool
}
10 changes: 0 additions & 10 deletions alerter/src/internal/database/datastore_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -208,15 +208,8 @@ func TestDatastoreCloseNilPool(t *testing.T) {

// TestDatastorePoolAccessor tests the Pool accessor method
func TestDatastorePoolAccessor(t *testing.T) {
ds := &Datastore{
pool: nil,
config: nil,
}

// Pool should return nil when no pool is set
if ds.Pool() != nil {
t.Errorf("Pool() should return nil for uninitialized datastore")
}
}

// TestNewDatastoreSuccess exercises the happy path through NewDatastore
Expand Down Expand Up @@ -251,9 +244,6 @@ func TestNewDatastoreSuccess(t *testing.T) {
t.Fatalf("NewDatastore: %v", err)
}
defer ds.Close()
if ds.Pool() == nil {
t.Errorf("expected non-nil pool from NewDatastore")
}
}

// TestNewDatastoreInvalidConfig exercises the parse-failure branch of
Expand Down
48 changes: 0 additions & 48 deletions alerter/src/internal/database/error_paths_integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,9 +82,6 @@ func TestQueriesReturnErrorOnClosedPool(t *testing.T) {
if _, err := ds.DeleteOldAnomalyCandidates(ctx, time.Now()); err == nil {
t.Errorf("DeleteOldAnomalyCandidates should error on closed pool")
}
if _, err := ds.GetProbeAvailability(ctx, 1, "x"); err == nil {
t.Errorf("GetProbeAvailability should error on closed pool")
}
if _, err := ds.GetEnabledBlackoutSchedules(ctx); err == nil {
t.Errorf("GetEnabledBlackoutSchedules should error on closed pool")
}
Expand Down Expand Up @@ -206,54 +203,15 @@ func TestNotificationQueriesReturnErrorOnClosedPool(t *testing.T) {
defer cleanup()

ctx := context.Background()
owner := "tester"
ch := &NotificationChannel{
OwnerUsername: &owner,
Enabled: true,
ChannelType: ChannelTypeWebhook,
Name: "x",
HTTPMethod: "POST",
Headers: map[string]string{},
SMTPPort: 587,
SMTPUseTLS: true,
ReminderEnabled: true,
ReminderIntervalHours: 1,
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
}
if _, err := ds.GetNotificationChannel(ctx, 1); err == nil {
t.Errorf("GetNotificationChannel should error on closed pool")
}
if _, err := ds.GetNotificationChannelsForConnection(ctx, 1); err == nil {
t.Errorf("GetNotificationChannelsForConnection should error on closed pool")
}
if err := ds.CreateNotificationChannel(ctx, ch); err == nil {
t.Errorf("CreateNotificationChannel should error on closed pool")
}
if err := ds.UpdateNotificationChannel(ctx, ch); err == nil {
t.Errorf("UpdateNotificationChannel should error on closed pool")
}
if err := ds.DeleteNotificationChannel(ctx, 1); err == nil {
t.Errorf("DeleteNotificationChannel should error on closed pool")
}
if _, err := ds.GetEmailRecipients(ctx, 1); err == nil {
t.Errorf("GetEmailRecipients should error on closed pool")
}
if err := ds.CreateEmailRecipient(ctx, &EmailRecipient{}); err == nil {
t.Errorf("CreateEmailRecipient should error on closed pool")
}
if err := ds.DeleteEmailRecipient(ctx, 1); err == nil {
t.Errorf("DeleteEmailRecipient should error on closed pool")
}
if err := ds.LinkConnectionToChannel(ctx, &ConnectionNotificationChannel{}); err == nil {
t.Errorf("LinkConnectionToChannel should error on closed pool")
}
if err := ds.UnlinkConnectionFromChannel(ctx, 1, 1); err == nil {
t.Errorf("UnlinkConnectionFromChannel should error on closed pool")
}
if _, err := ds.GetConnectionChannelLinks(ctx, 1); err == nil {
t.Errorf("GetConnectionChannelLinks should error on closed pool")
}
if err := ds.CreateNotificationHistory(ctx, &NotificationHistory{}); err == nil {
t.Errorf("CreateNotificationHistory should error on closed pool")
}
Expand All @@ -263,12 +221,6 @@ func TestNotificationQueriesReturnErrorOnClosedPool(t *testing.T) {
if _, err := ds.GetPendingNotifications(ctx); err == nil {
t.Errorf("GetPendingNotifications should error on closed pool")
}
if _, err := ds.GetNotificationHistoryForAlert(ctx, 1); err == nil {
t.Errorf("GetNotificationHistoryForAlert should error on closed pool")
}
if _, err := ds.GetReminderState(ctx, 1, 1); err == nil {
t.Errorf("GetReminderState should error on closed pool")
}
if err := ds.UpsertReminderState(ctx, &NotificationReminderState{}); err == nil {
t.Errorf("UpsertReminderState should error on closed pool")
}
Expand Down
193 changes: 0 additions & 193 deletions alerter/src/internal/database/notification_queries.go
Original file line number Diff line number Diff line change
Expand Up @@ -127,70 +127,6 @@ func (d *Datastore) GetNotificationChannelsForConnection(ctx context.Context, co
return channels, nil
}

// CreateNotificationChannel inserts a new notification channel
func (d *Datastore) CreateNotificationChannel(ctx context.Context, channel *NotificationChannel) error {
return d.pool.QueryRow(ctx, `
INSERT INTO notification_channels (
owner_username, owner_token, enabled, channel_type, name,
description, webhook_url_encrypted, endpoint_url, http_method,
headers_json, auth_type, auth_credentials_encrypted, smtp_host,
smtp_port, smtp_username, smtp_password_encrypted,
smtp_use_tls, from_address, from_name, template_alert_fire,
template_alert_clear, template_reminder, reminder_enabled,
reminder_interval_hours, is_estate_default, created_at, updated_at
) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15,
$16, $17, $18, $19, $20, $21, $22, $23, $24, $25, $26, $27)
RETURNING id
`, channel.OwnerUsername, channel.OwnerToken, channel.Enabled,
channel.ChannelType, channel.Name, channel.Description, channel.WebhookURL,
channel.EndpointURL, channel.HTTPMethod, channel.Headers, channel.AuthType,
channel.AuthCredentials, channel.SMTPHost, channel.SMTPPort, channel.SMTPUsername,
channel.SMTPPassword, channel.SMTPUseTLS, channel.FromAddress, channel.FromName,
channel.TemplateAlertFire, channel.TemplateAlertClear, channel.TemplateReminder,
channel.ReminderEnabled, channel.ReminderIntervalHours, channel.IsEstateDefault,
channel.CreatedAt, channel.UpdatedAt).Scan(&channel.ID)
}

// UpdateNotificationChannel updates an existing notification channel
func (d *Datastore) UpdateNotificationChannel(ctx context.Context, channel *NotificationChannel) error {
_, err := d.pool.Exec(ctx, `
UPDATE notification_channels
SET owner_username = $2, owner_token = $3, enabled = $4,
channel_type = $5, name = $6, description = $7,
webhook_url_encrypted = $8,
endpoint_url = $9, http_method = $10, headers_json = $11,
auth_type = $12, auth_credentials_encrypted = $13,
smtp_host = $14, smtp_port = $15,
smtp_username = $16, smtp_password_encrypted = $17,
smtp_use_tls = $18,
from_address = $19, from_name = $20, template_alert_fire = $21,
template_alert_clear = $22, template_reminder = $23,
reminder_enabled = $24, reminder_interval_hours = $25,
is_estate_default = $26, updated_at = $27
WHERE id = $1
`, channel.ID, channel.OwnerUsername, channel.OwnerToken,
channel.Enabled, channel.ChannelType, channel.Name, channel.Description,
channel.WebhookURL, channel.EndpointURL, channel.HTTPMethod, channel.Headers,
channel.AuthType, channel.AuthCredentials, channel.SMTPHost, channel.SMTPPort,
channel.SMTPUsername, channel.SMTPPassword, channel.SMTPUseTLS,
channel.FromAddress, channel.FromName, channel.TemplateAlertFire,
channel.TemplateAlertClear, channel.TemplateReminder, channel.ReminderEnabled,
channel.ReminderIntervalHours, channel.IsEstateDefault, channel.UpdatedAt)
if err != nil {
return fmt.Errorf("failed to update notification channel: %w", err)
}
return nil
}

// DeleteNotificationChannel deletes a notification channel
func (d *Datastore) DeleteNotificationChannel(ctx context.Context, id int64) error {
_, err := d.pool.Exec(ctx, `DELETE FROM notification_channels WHERE id = $1`, id)
if err != nil {
return fmt.Errorf("failed to delete notification channel: %w", err)
}
return nil
}

// GetEmailRecipients retrieves all enabled email recipients for a channel
func (d *Datastore) GetEmailRecipients(ctx context.Context, channelID int64) ([]*EmailRecipient, error) {
rows, err := d.pool.Query(ctx, `
Expand Down Expand Up @@ -222,81 +158,6 @@ func (d *Datastore) GetEmailRecipients(ctx context.Context, channelID int64) ([]
return recipients, nil
}

// CreateEmailRecipient inserts a new email recipient
func (d *Datastore) CreateEmailRecipient(ctx context.Context, recipient *EmailRecipient) error {
return d.pool.QueryRow(ctx, `
INSERT INTO email_recipients (channel_id, email_address, display_name, enabled, created_at)
VALUES ($1, $2, $3, $4, $5)
RETURNING id
`, recipient.ChannelID, recipient.EmailAddress, recipient.DisplayName,
recipient.Enabled, recipient.CreatedAt).Scan(&recipient.ID)
}

// DeleteEmailRecipient deletes an email recipient
func (d *Datastore) DeleteEmailRecipient(ctx context.Context, id int64) error {
_, err := d.pool.Exec(ctx, `DELETE FROM email_recipients WHERE id = $1`, id)
if err != nil {
return fmt.Errorf("failed to delete email recipient: %w", err)
}
return nil
}

// LinkConnectionToChannel creates a link between a connection and notification channel
func (d *Datastore) LinkConnectionToChannel(ctx context.Context, link *ConnectionNotificationChannel) error {
return d.pool.QueryRow(ctx, `
INSERT INTO connection_notification_channels (
connection_id, channel_id, enabled, reminder_enabled_override,
reminder_interval_hours_override, created_at
) VALUES ($1, $2, $3, $4, $5, $6)
RETURNING id
`, link.ConnectionID, link.ChannelID, link.Enabled, link.ReminderEnabledOverride,
link.ReminderIntervalHoursOverride, link.CreatedAt).Scan(&link.ID)
}

// UnlinkConnectionFromChannel removes the link between a connection and notification channel
func (d *Datastore) UnlinkConnectionFromChannel(ctx context.Context, connectionID int, channelID int64) error {
_, err := d.pool.Exec(ctx, `
DELETE FROM connection_notification_channels
WHERE connection_id = $1 AND channel_id = $2
`, connectionID, channelID)
if err != nil {
return fmt.Errorf("failed to unlink connection from channel: %w", err)
}
return nil
}

// GetConnectionChannelLinks retrieves all notification channel links for a connection
func (d *Datastore) GetConnectionChannelLinks(ctx context.Context, connectionID int) ([]*ConnectionNotificationChannel, error) {
rows, err := d.pool.Query(ctx, `
SELECT id, connection_id, channel_id, enabled, reminder_enabled_override,
reminder_interval_hours_override, created_at
FROM connection_notification_channels
WHERE connection_id = $1
ORDER BY id
`, connectionID)
if err != nil {
return nil, fmt.Errorf("failed to get connection channel links: %w", err)
}
defer rows.Close()

var links []*ConnectionNotificationChannel
for rows.Next() {
var link ConnectionNotificationChannel
err := rows.Scan(&link.ID, &link.ConnectionID, &link.ChannelID, &link.Enabled,
&link.ReminderEnabledOverride, &link.ReminderIntervalHoursOverride, &link.CreatedAt)
if err != nil {
return nil, fmt.Errorf("failed to scan connection channel link: %w", err)
}
links = append(links, &link)
}

if err := rows.Err(); err != nil {
return nil, fmt.Errorf("row iteration error: %w", err)
}

return links, nil
}

// CreateNotificationHistory inserts a new notification history record
func (d *Datastore) CreateNotificationHistory(ctx context.Context, history *NotificationHistory) error {
return d.pool.QueryRow(ctx, `
Expand Down Expand Up @@ -364,60 +225,6 @@ func (d *Datastore) GetPendingNotifications(ctx context.Context) ([]*Notificatio
return notifications, nil
}

// GetNotificationHistoryForAlert retrieves all notification history for an alert
func (d *Datastore) GetNotificationHistoryForAlert(ctx context.Context, alertID int64) ([]*NotificationHistory, error) {
rows, err := d.pool.Query(ctx, `
SELECT id, alert_id, channel_id, connection_id, notification_type, status,
payload_json, response_code, response_body, error_message,
attempt_count, max_attempts, next_retry_at, created_at, sent_at
FROM notification_history
WHERE alert_id = $1
ORDER BY created_at DESC
`, alertID)
if err != nil {
return nil, fmt.Errorf("failed to get notification history for alert: %w", err)
}
defer rows.Close()

var notifications []*NotificationHistory
for rows.Next() {
var n NotificationHistory
err := rows.Scan(&n.ID, &n.AlertID, &n.ChannelID, &n.ConnectionID,
&n.NotificationType, &n.Status, &n.PayloadJSON, &n.ResponseCode,
&n.ResponseBody, &n.ErrorMessage, &n.AttemptCount, &n.MaxAttempts,
&n.NextRetryAt, &n.CreatedAt, &n.SentAt)
if err != nil {
return nil, fmt.Errorf("failed to scan notification history: %w", err)
}
notifications = append(notifications, &n)
}

if err := rows.Err(); err != nil {
return nil, fmt.Errorf("row iteration error: %w", err)
}

return notifications, nil
}

// GetReminderState retrieves the reminder state for an alert and channel
func (d *Datastore) GetReminderState(ctx context.Context, alertID int64, channelID int64) (*NotificationReminderState, error) {
var state NotificationReminderState
err := d.pool.QueryRow(ctx, `
SELECT id, alert_id, channel_id, last_reminder_at, reminder_count
FROM notification_reminder_state
WHERE alert_id = $1 AND channel_id = $2
`, alertID, channelID).Scan(&state.ID, &state.AlertID, &state.ChannelID,
&state.LastReminderAt, &state.ReminderCount)

if err != nil {
if err == pgx.ErrNoRows {
return nil, nil
}
return nil, fmt.Errorf("failed to get reminder state: %w", err)
}
return &state, nil
}

// UpsertReminderState inserts or updates a reminder state record
func (d *Datastore) UpsertReminderState(ctx context.Context, state *NotificationReminderState) error {
err := d.pool.QueryRow(ctx, `
Expand Down
Loading
Loading