From f147be0b9975f5bdf3cc49b200628f69d008c155 Mon Sep 17 00:00:00 2001 From: Stephen Adamson Date: Sat, 11 Jul 2026 15:04:53 +0100 Subject: [PATCH] fix: add error logging to remaining nolint:errcheck scan sites for observability --- backend/handlers/admin/custom_services.go | 11 ++- backend/handlers/bookings/bookings.go | 15 ++-- backend/handlers/bookings/manage.go | 5 +- .../handlers/notifications/notifications.go | 5 +- backend/handlers/payments/giftcards.go | 22 ++--- backend/handlers/payments/handlers.go | 81 +++++++++++-------- backend/handlers/user/profile.go | 12 +-- 7 files changed, 90 insertions(+), 61 deletions(-) diff --git a/backend/handlers/admin/custom_services.go b/backend/handlers/admin/custom_services.go index 2278ebf..2cc9791 100644 --- a/backend/handlers/admin/custom_services.go +++ b/backend/handlers/admin/custom_services.go @@ -7,6 +7,7 @@ import ( "database/sql" "encoding/json" "errors" + "log" "log/slog" "net/http" "strconv" @@ -167,13 +168,15 @@ func GetCustomServices(w http.ResponseWriter, r *http.Request) { // so pgx does not return "conn busy" on the same transaction. if q != "" { var countTotal int64 -//nolint:errcheck // zero value is acceptable fallback on scan failure - _ = db.Conn.QueryRow(r.Context(), "SELECT COUNT(*) FROM custom_services WHERE name ILIKE $1 OR description ILIKE $1", "%"+q+"%").Scan(&countTotal) + if err := db.Conn.QueryRow(r.Context(), "SELECT COUNT(*) FROM custom_services WHERE name ILIKE $1 OR description ILIKE $1", "%"+q+"%").Scan(&countTotal); err != nil { + log.Printf("Failed to scan filtered custom services count: %v", err) + } total = countTotal } else { var countTotal int64 -//nolint:errcheck // zero value is acceptable fallback on scan failure - _ = db.Conn.QueryRow(r.Context(), "SELECT COUNT(*) FROM custom_services").Scan(&countTotal) + if err := db.Conn.QueryRow(r.Context(), "SELECT COUNT(*) FROM custom_services").Scan(&countTotal); err != nil { + log.Printf("Failed to scan custom services count: %v", err) + } total = countTotal } diff --git a/backend/handlers/bookings/bookings.go b/backend/handlers/bookings/bookings.go index 3ad12ff..258c860 100644 --- a/backend/handlers/bookings/bookings.go +++ b/backend/handlers/bookings/bookings.go @@ -2776,8 +2776,9 @@ func ProgressBookingHandler(w http.ResponseWriter, r *http.Request) { if bookingTotal > 0 { var userBookingCount int -//nolint:errcheck // zero value is acceptable fallback on scan failure - _ = tx.QueryRow(r.Context(), `SELECT COUNT(*) FROM bookings WHERE user_id = $1 AND status = 'completed'`, booking.User.ID).Scan(&userBookingCount) + if err := tx.QueryRow(r.Context(), `SELECT COUNT(*) FROM bookings WHERE user_id = $1 AND status = 'completed'`, booking.User.ID).Scan(&userBookingCount); err != nil { + log.Printf("Failed to scan user completed booking count: %v", err) + } var milestoneCampaignID string var milestonePercent float64 @@ -2817,8 +2818,9 @@ func ProgressBookingHandler(w http.ResponseWriter, r *http.Request) { } if !globalMilestoneApplied { var globalCount int -//nolint:errcheck // zero value is acceptable fallback on scan failure - _ = tx.QueryRow(r.Context(), `SELECT COUNT(*) FROM bookings WHERE status = 'completed'`).Scan(&globalCount) + if err := tx.QueryRow(r.Context(), `SELECT COUNT(*) FROM bookings WHERE status = 'completed'`).Scan(&globalCount); err != nil { + log.Printf("Failed to scan global completed booking count: %v", err) + } var hasInPersonPayment bool if err := tx.QueryRow(r.Context(), ` @@ -2863,8 +2865,9 @@ func ProgressBookingHandler(w http.ResponseWriter, r *http.Request) { } var firstVisitDate time.Time -//nolint:errcheck // zero value is acceptable fallback on scan failure - _ = tx.QueryRow(r.Context(), `SELECT MIN(start_time) FROM bookings WHERE user_id = $1 AND status = 'completed'`, booking.User.ID).Scan(&firstVisitDate) + if err := tx.QueryRow(r.Context(), `SELECT MIN(start_time) FROM bookings WHERE user_id = $1 AND status = 'completed'`, booking.User.ID).Scan(&firstVisitDate); err != nil { + log.Printf("Failed to scan first visit date: %v", err) + } if !firstVisitDate.IsZero() { annRows, err := tx.Query(r.Context(), ` SELECT id, discount_percent, milestone_value, milestone_unit FROM discount_campaigns diff --git a/backend/handlers/bookings/manage.go b/backend/handlers/bookings/manage.go index 920bee6..15a2d8e 100644 --- a/backend/handlers/bookings/manage.go +++ b/backend/handlers/bookings/manage.go @@ -1685,8 +1685,9 @@ func AdminListEditRequestsHandler(w http.ResponseWriter, r *http.Request) { var total int // Count query (no ORDER BY needed). -//nolint:errcheck // zero value is acceptable fallback on scan failure - _ = db.Conn.QueryRow(r.Context(), "SELECT COUNT(*) FROM booking_edit_requests").Scan(&total) + if err := db.Conn.QueryRow(r.Context(), "SELECT COUNT(*) FROM booking_edit_requests").Scan(&total); err != nil { + log.Printf("Failed to scan booking edit request count: %v", err) + } rows, err := db.Conn.Query(r.Context(), baseQuery, args...) if err != nil { diff --git a/backend/handlers/notifications/notifications.go b/backend/handlers/notifications/notifications.go index 91f7d45..9edaf69 100644 --- a/backend/handlers/notifications/notifications.go +++ b/backend/handlers/notifications/notifications.go @@ -126,8 +126,9 @@ func GetNotifications(w http.ResponseWriter, r *http.Request) { if !includeAcknowledged { countWhere += " WHERE an.acknowledged_at IS NULL" } -//nolint:errcheck // zero value is acceptable fallback on scan failure - _ = db.Conn.QueryRow(r.Context(), "SELECT COUNT(*) FROM admin_notifications an"+countWhere).Scan(&total) + if err := db.Conn.QueryRow(r.Context(), "SELECT COUNT(*) FROM admin_notifications an"+countWhere).Scan(&total); err != nil { + log.Printf("Failed to scan notification count: %v", err) + } // Query rows, err := db.Conn.Query(r.Context(), baseQuery, args...) diff --git a/backend/handlers/payments/giftcards.go b/backend/handlers/payments/giftcards.go index 004ec50..b67fb32 100644 --- a/backend/handlers/payments/giftcards.go +++ b/backend/handlers/payments/giftcards.go @@ -207,11 +207,13 @@ func GetGiftCards(w http.ResponseWriter, r *http.Request) { if searchTerm != "" { countArgs = append(countArgs, "%"+searchTerm+"%") } -//nolint:errcheck // zero value is acceptable fallback on scan failure - _ = db.Conn.QueryRow(ctx, "SELECT COUNT(*) FROM gift_cards "+whereSQL, countArgs...).Scan(&gcTotal) + if err := db.Conn.QueryRow(ctx, "SELECT COUNT(*) FROM gift_cards "+whereSQL, countArgs...).Scan(&gcTotal); err != nil { + log.Printf("Failed to scan filtered gift card count: %v", err) + } } else { -//nolint:errcheck // zero value is acceptable fallback on scan failure - _ = db.Conn.QueryRow(ctx, "SELECT COUNT(*) FROM gift_cards").Scan(&gcTotal) + if err := db.Conn.QueryRow(ctx, "SELECT COUNT(*) FROM gift_cards").Scan(&gcTotal); err != nil { + log.Printf("Failed to scan gift card count: %v", err) + } } gcRows, err := db.Conn.Query(ctx, gcListQuery, gcListArgs...) @@ -283,13 +285,15 @@ func GetGiftCards(w http.ResponseWriter, r *http.Request) { // Count query for user balances. if searchTerm != "" { -//nolint:errcheck // zero value is acceptable fallback on scan failure - _ = db.Conn.QueryRow(ctx, `SELECT COUNT(*) FROM user_giftcard_balances b + if err := db.Conn.QueryRow(ctx, `SELECT COUNT(*) FROM user_giftcard_balances b JOIN users u ON b.user_id = u.id - WHERE u.n_first_name ILIKE $1 OR u.n_last_name ILIKE $1 OR u.email ILIKE $1`, "%"+searchTerm+"%").Scan(&ubTotal) + WHERE u.n_first_name ILIKE $1 OR u.n_last_name ILIKE $1 OR u.email ILIKE $1`, "%"+searchTerm+"%").Scan(&ubTotal); err != nil { + log.Printf("Failed to scan filtered user balance count: %v", err) + } } else { -//nolint:errcheck // zero value is acceptable fallback on scan failure - _ = db.Conn.QueryRow(ctx, "SELECT COUNT(*) FROM user_giftcard_balances").Scan(&ubTotal) + if err := db.Conn.QueryRow(ctx, "SELECT COUNT(*) FROM user_giftcard_balances").Scan(&ubTotal); err != nil { + log.Printf("Failed to scan user balance count: %v", err) + } } for ubRows.Next() { diff --git a/backend/handlers/payments/handlers.go b/backend/handlers/payments/handlers.go index 3eee27c..8bb7aaa 100644 --- a/backend/handlers/payments/handlers.go +++ b/backend/handlers/payments/handlers.go @@ -165,8 +165,9 @@ func calculateDiscountPreview(ctx context.Context, bookingID string, userID stri ORDER BY discount_percent DESC LIMIT 1 `).Scan(&campaignID, &campaignPercent, &campaignName); err == nil && campaignID != "" { var exists int - //nolint:errcheck // zero value is acceptable fallback on scan failure - _ = db.Conn.QueryRow(ctx, `SELECT 1 FROM booking_discounts WHERE booking_id = $1 AND source_id = $2`, bookingID, campaignID).Scan(&exists) + if err := db.Conn.QueryRow(ctx, `SELECT 1 FROM booking_discounts WHERE booking_id = $1 AND source_id = $2`, bookingID, campaignID).Scan(&exists); err != nil { + log.Printf("Failed to scan campaign discount existence: %v", err) + } if exists == 0 { amount := roundTo2(bookingTotal * campaignPercent / 100) resp.Discounts = append(resp.Discounts, DiscountPreview{ @@ -180,8 +181,9 @@ func calculateDiscountPreview(ctx context.Context, bookingID string, userID stri } var userBookingCount int - //nolint:errcheck // zero value is acceptable fallback on scan failure - _ = db.Conn.QueryRow(ctx, `SELECT COUNT(*) FROM bookings WHERE user_id = $1 AND status = 'completed'`, userID).Scan(&userBookingCount) + if err := db.Conn.QueryRow(ctx, `SELECT COUNT(*) FROM bookings WHERE user_id = $1 AND status = 'completed'`, userID).Scan(&userBookingCount); err != nil { + log.Printf("Failed to scan user completed booking count: %v", err) + } var milestoneCampaignID string var milestonePercent float64 @@ -197,8 +199,9 @@ func calculateDiscountPreview(ctx context.Context, bookingID string, userID stri if milestoneCampaignID != "" { var exists int - //nolint:errcheck // zero value is acceptable fallback on scan failure - _ = db.Conn.QueryRow(ctx, `SELECT 1 FROM booking_discounts WHERE booking_id = $1 AND source_id = $2`, bookingID, milestoneCampaignID).Scan(&exists) + if err := db.Conn.QueryRow(ctx, `SELECT 1 FROM booking_discounts WHERE booking_id = $1 AND source_id = $2`, bookingID, milestoneCampaignID).Scan(&exists); err != nil { + log.Printf("Failed to scan milestone discount existence: %v", err) + } if exists == 0 { amount := roundTo2(bookingTotal * milestonePercent / 100) resp.Discounts = append(resp.Discounts, DiscountPreview{ @@ -212,8 +215,9 @@ func calculateDiscountPreview(ctx context.Context, bookingID string, userID stri } var firstVisitDate time.Time - //nolint:errcheck // zero value is acceptable fallback on scan failure - _ = db.Conn.QueryRow(ctx, `SELECT MIN(start_time) FROM bookings WHERE user_id = $1 AND status = 'completed'`, userID).Scan(&firstVisitDate) + if err := db.Conn.QueryRow(ctx, `SELECT MIN(start_time) FROM bookings WHERE user_id = $1 AND status = 'completed'`, userID).Scan(&firstVisitDate); err != nil { + log.Printf("Failed to scan first visit date: %v", err) + } if !firstVisitDate.IsZero() { type annCamp struct { id string @@ -238,9 +242,10 @@ func calculateDiscountPreview(ctx context.Context, bookingID string, userID stri annRows.Close() for _, c := range campaigns { - var exists int - //nolint:errcheck // zero value is acceptable fallback on scan failure - _ = db.Conn.QueryRow(ctx, `SELECT 1 FROM booking_discounts WHERE booking_id = $1 AND source_id = $2`, bookingID, c.id).Scan(&exists) + var exists int + if err := db.Conn.QueryRow(ctx, `SELECT 1 FROM booking_discounts WHERE booking_id = $1 AND source_id = $2`, bookingID, c.id).Scan(&exists); err != nil { + log.Printf("Failed to scan anniversary discount existence: %v", err) + } if exists > 0 { continue } @@ -276,8 +281,9 @@ func calculateDiscountPreview(ctx context.Context, bookingID string, userID stri LIMIT 1 `, userID).Scan(&rdID, &rdPercent); err == nil && rdID != "" { exists := 0 - //nolint:errcheck // zero value is acceptable fallback on scan failure - _ = db.Conn.QueryRow(ctx, `SELECT 1 FROM booking_discounts WHERE booking_id = $1 AND discount_source = 'referral' AND source_id = $2`, bookingID, rdID).Scan(&exists) + if err := db.Conn.QueryRow(ctx, `SELECT 1 FROM booking_discounts WHERE booking_id = $1 AND discount_source = 'referral' AND source_id = $2`, bookingID, rdID).Scan(&exists); err != nil { + log.Printf("Failed to scan referral discount existence: %v", err) + } if exists == 0 { amount := roundTo2(bookingTotal * rdPercent / 100) resp.Discounts = append(resp.Discounts, DiscountPreview{ @@ -1080,11 +1086,12 @@ func CreateBookingPayment(w http.ResponseWriter, r *http.Request) { // would create a credit balance or require a refund. func applyEligibleCampaignsAtPayment(ctx context.Context, q db.Querier, bookingID string, userID string) { var existingPayment int - //nolint:errcheck // zero value is acceptable fallback on scan failure - _ = q.QueryRow(ctx, ` + if err := q.QueryRow(ctx, ` SELECT COUNT(*) FROM payments WHERE booking_id = $1 AND status = 'completed' AND payment_method NOT IN ('discount', 'on_the_house') - `, bookingID).Scan(&existingPayment) + `, bookingID).Scan(&existingPayment); err != nil { + log.Printf("Failed to scan existing payment count: %v", err) + } // Only block if this is the 2nd+ real payment — the first payment should still // trigger discount application (existingPayment counts already-completed payments // visible within the transaction, including the just-inserted one). @@ -1113,8 +1120,9 @@ func applyEligibleCampaignsAtPayment(ctx context.Context, q db.Querier, bookingI ORDER BY discount_percent DESC LIMIT 1 `).Scan(&campaignID, &campaignPercent); err == nil && campaignID != "" { var exists int - //nolint:errcheck // zero value is acceptable fallback on scan failure - _ = q.QueryRow(ctx, `SELECT 1 FROM booking_discounts WHERE booking_id = $1 AND source_id = $2`, bookingID, campaignID).Scan(&exists) + if err := q.QueryRow(ctx, `SELECT 1 FROM booking_discounts WHERE booking_id = $1 AND source_id = $2`, bookingID, campaignID).Scan(&exists); err != nil { + log.Printf("Failed to scan time-based campaign discount existence: %v", err) + } if exists == 0 { discountAmount := roundTo2(bookingTotal * campaignPercent / 100) if _, err := q.Exec(ctx, ` @@ -1139,8 +1147,9 @@ func applyEligibleCampaignsAtPayment(ctx context.Context, q db.Querier, bookingI } var userBookingCount int - //nolint:errcheck // zero value is acceptable fallback on scan failure - _ = q.QueryRow(ctx, `SELECT COUNT(*) FROM bookings WHERE user_id = $1 AND status = 'completed'`, userID).Scan(&userBookingCount) + if err := q.QueryRow(ctx, `SELECT COUNT(*) FROM bookings WHERE user_id = $1 AND status = 'completed'`, userID).Scan(&userBookingCount); err != nil { + log.Printf("Failed to scan user booking count: %v", err) + } var milestoneCampaignID string var milestonePercent float64 @@ -1155,8 +1164,9 @@ func applyEligibleCampaignsAtPayment(ctx context.Context, q db.Querier, bookingI if milestoneCampaignID != "" { var exists int - //nolint:errcheck // zero value is acceptable fallback on scan failure - _ = q.QueryRow(ctx, `SELECT 1 FROM booking_discounts WHERE booking_id = $1 AND source_id = $2`, bookingID, milestoneCampaignID).Scan(&exists) + if err := q.QueryRow(ctx, `SELECT 1 FROM booking_discounts WHERE booking_id = $1 AND source_id = $2`, bookingID, milestoneCampaignID).Scan(&exists); err != nil { + log.Printf("Failed to scan milestone campaign discount existence: %v", err) + } if exists == 0 { discountAmount := roundTo2(bookingTotal * milestonePercent / 100) if _, err := q.Exec(ctx, ` @@ -1181,8 +1191,9 @@ func applyEligibleCampaignsAtPayment(ctx context.Context, q db.Querier, bookingI } var firstVisitDate time.Time - //nolint:errcheck // zero value is acceptable fallback on scan failure - _ = q.QueryRow(ctx, `SELECT MIN(start_time) FROM bookings WHERE user_id = $1 AND status = 'completed'`, userID).Scan(&firstVisitDate) + if err := q.QueryRow(ctx, `SELECT MIN(start_time) FROM bookings WHERE user_id = $1 AND status = 'completed'`, userID).Scan(&firstVisitDate); err != nil { + log.Printf("Failed to scan first visit date: %v", err) + } if !firstVisitDate.IsZero() { annRows, err := q.Query(ctx, ` SELECT id, discount_percent, milestone_value, milestone_unit FROM discount_campaigns @@ -1206,9 +1217,10 @@ func applyEligibleCampaignsAtPayment(ctx context.Context, q db.Querier, bookingI annRows.Close() for _, c := range campaigns { - var exists int - //nolint:errcheck // zero value is acceptable fallback on scan failure - _ = q.QueryRow(ctx, `SELECT 1 FROM booking_discounts WHERE booking_id = $1 AND source_id = $2`, bookingID, c.id).Scan(&exists) + var exists int + if err := q.QueryRow(ctx, `SELECT 1 FROM booking_discounts WHERE booking_id = $1 AND source_id = $2`, bookingID, c.id).Scan(&exists); err != nil { + log.Printf("Failed to scan anniversary discount existence: %v", err) + } if exists > 0 { continue } @@ -1256,8 +1268,9 @@ func applyEligibleCampaignsAtPayment(ctx context.Context, q db.Querier, bookingI SELECT payment_method FROM payments WHERE booking_id = $1 AND payment_method NOT IN ('discount', 'on_the_house') ORDER BY created_at ASC LIMIT 1 `, bookingID).Scan(&firstPaymentMethod); err == nil && firstPaymentMethod == "in_person_card" { var globalCount int - //nolint:errcheck // zero value is acceptable fallback on scan failure - _ = q.QueryRow(ctx, `SELECT COUNT(*) FROM bookings WHERE status = 'completed'`).Scan(&globalCount) + if err := q.QueryRow(ctx, `SELECT COUNT(*) FROM bookings WHERE status = 'completed'`).Scan(&globalCount); err != nil { + log.Printf("Failed to scan global completed booking count: %v", err) + } var globalCampaignID string var globalPercent float64 @@ -1274,8 +1287,9 @@ func applyEligibleCampaignsAtPayment(ctx context.Context, q db.Querier, bookingI if globalCampaignID != "" { var exists int - //nolint:errcheck // zero value is acceptable fallback on scan failure - _ = q.QueryRow(ctx, `SELECT 1 FROM booking_discounts WHERE booking_id = $1 AND source_id = $2`, bookingID, globalCampaignID).Scan(&exists) + if err := q.QueryRow(ctx, `SELECT 1 FROM booking_discounts WHERE booking_id = $1 AND source_id = $2`, bookingID, globalCampaignID).Scan(&exists); err != nil { + log.Printf("Failed to scan global campaign discount existence: %v", err) + } if exists == 0 { discountAmount := roundTo2(bookingTotal * globalPercent / 100) if _, err := q.Exec(ctx, ` @@ -1310,8 +1324,9 @@ func applyEligibleCampaignsAtPayment(ctx context.Context, q db.Querier, bookingI LIMIT 1 `, userID).Scan(&rdID, &rdPercent); err == nil && rdID != "" { exists := 0 - //nolint:errcheck // zero value is acceptable fallback on scan failure - _ = q.QueryRow(ctx, `SELECT 1 FROM booking_discounts WHERE booking_id = $1 AND discount_source = 'referral' AND source_id = $2`, bookingID, rdID).Scan(&exists) + if err := q.QueryRow(ctx, `SELECT 1 FROM booking_discounts WHERE booking_id = $1 AND discount_source = 'referral' AND source_id = $2`, bookingID, rdID).Scan(&exists); err != nil { + log.Printf("Failed to scan referral discount existence: %v", err) + } if exists == 0 { discountAmount := roundTo2(bookingTotal * rdPercent / 100) if _, err := q.Exec(ctx, ` diff --git a/backend/handlers/user/profile.go b/backend/handlers/user/profile.go index 0b14669..265ad61 100644 --- a/backend/handlers/user/profile.go +++ b/backend/handlers/user/profile.go @@ -565,13 +565,15 @@ func ListAdminUsersHandler(w http.ResponseWriter, r *http.Request) { // Run BEFORE the data query to avoid "conn busy" errors when routing // through a per-test transaction (pgx.Tx does not support concurrent queries). if searchTerm != "" { - //nolint:errcheck // zero value is acceptable fallback on scan failure - _ = db.Conn.QueryRow(r.Context(), `SELECT COUNT(DISTINCT u.id) FROM users u + if err := db.Conn.QueryRow(r.Context(), `SELECT COUNT(DISTINCT u.id) FROM users u LEFT JOIN bookings b ON u.id = b.user_id - WHERE u.fn ILIKE $1 OR u.email ILIKE $1 OR u.phone ILIKE $1`, "%"+searchTerm+"%").Scan(&total) + WHERE u.fn ILIKE $1 OR u.email ILIKE $1 OR u.phone ILIKE $1`, "%"+searchTerm+"%").Scan(&total); err != nil { + log.Printf("Failed to scan filtered user count: %v", err) + } } else { - //nolint:errcheck // zero value is acceptable fallback on scan failure - _ = db.Conn.QueryRow(r.Context(), "SELECT COUNT(*) FROM users").Scan(&total) + if err := db.Conn.QueryRow(r.Context(), "SELECT COUNT(*) FROM users").Scan(&total); err != nil { + log.Printf("Failed to scan user count: %v", err) + } } // Get users list