mirror of
https://github.com/btouchard/ackify-ce.git
synced 2026-02-08 23:08:58 -06:00
fix: properly handle NULL UserName in database operations
Add scanSignature helper to convert sql.NullString to string type. Update Create method to insert NULL for empty UserName values. Fix integration tests to work with string type instead of pointer.
This commit is contained in:
@@ -18,6 +18,37 @@ func NewSignatureRepository(db *sql.DB) *SignatureRepository {
|
||||
return &SignatureRepository{db: db}
|
||||
}
|
||||
|
||||
// scanSignature scans a row into a Signature, handling NULL values properly
|
||||
func scanSignature(scanner interface {
|
||||
Scan(dest ...interface{}) error
|
||||
}, signature *models.Signature) error {
|
||||
var userName sql.NullString
|
||||
err := scanner.Scan(
|
||||
&signature.ID,
|
||||
&signature.DocID,
|
||||
&signature.UserSub,
|
||||
&signature.UserEmail,
|
||||
&userName,
|
||||
&signature.SignedAtUTC,
|
||||
&signature.PayloadHash,
|
||||
&signature.Signature,
|
||||
&signature.Nonce,
|
||||
&signature.CreatedAt,
|
||||
&signature.Referer,
|
||||
&signature.PrevHash,
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Convert sql.NullString to string (empty string if NULL)
|
||||
if userName.Valid {
|
||||
signature.UserName = userName.String
|
||||
} else {
|
||||
signature.UserName = ""
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *SignatureRepository) Create(ctx context.Context, signature *models.Signature) error {
|
||||
query := `
|
||||
INSERT INTO signatures (doc_id, user_sub, user_email, user_name, signed_at, payload_hash, signature, nonce, referer, prev_hash)
|
||||
@@ -25,12 +56,18 @@ func (r *SignatureRepository) Create(ctx context.Context, signature *models.Sign
|
||||
RETURNING id, created_at
|
||||
`
|
||||
|
||||
// Convert empty string to NULL for user_name
|
||||
var userName sql.NullString
|
||||
if signature.UserName != "" {
|
||||
userName = sql.NullString{String: signature.UserName, Valid: true}
|
||||
}
|
||||
|
||||
err := r.db.QueryRowContext(
|
||||
ctx, query,
|
||||
signature.DocID,
|
||||
signature.UserSub,
|
||||
signature.UserEmail,
|
||||
signature.UserName,
|
||||
userName,
|
||||
signature.SignedAtUTC,
|
||||
signature.PayloadHash,
|
||||
signature.Signature,
|
||||
@@ -49,25 +86,12 @@ func (r *SignatureRepository) Create(ctx context.Context, signature *models.Sign
|
||||
func (r *SignatureRepository) GetByDocAndUser(ctx context.Context, docID, userSub string) (*models.Signature, error) {
|
||||
query := `
|
||||
SELECT id, doc_id, user_sub, user_email, user_name, signed_at, payload_hash, signature, nonce, created_at, referer, prev_hash
|
||||
FROM signatures
|
||||
FROM signatures
|
||||
WHERE doc_id = $1 AND user_sub = $2
|
||||
`
|
||||
|
||||
signature := &models.Signature{}
|
||||
err := r.db.QueryRowContext(ctx, query, docID, userSub).Scan(
|
||||
&signature.ID,
|
||||
&signature.DocID,
|
||||
&signature.UserSub,
|
||||
&signature.UserEmail,
|
||||
&signature.UserName,
|
||||
&signature.SignedAtUTC,
|
||||
&signature.PayloadHash,
|
||||
&signature.Signature,
|
||||
&signature.Nonce,
|
||||
&signature.CreatedAt,
|
||||
&signature.Referer,
|
||||
&signature.PrevHash,
|
||||
)
|
||||
err := scanSignature(r.db.QueryRowContext(ctx, query, docID, userSub), signature)
|
||||
|
||||
if err != nil {
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
@@ -98,21 +122,7 @@ func (r *SignatureRepository) GetByDoc(ctx context.Context, docID string) ([]*mo
|
||||
var signatures []*models.Signature
|
||||
for rows.Next() {
|
||||
signature := &models.Signature{}
|
||||
err := rows.Scan(
|
||||
&signature.ID,
|
||||
&signature.DocID,
|
||||
&signature.UserSub,
|
||||
&signature.UserEmail,
|
||||
&signature.UserName,
|
||||
&signature.SignedAtUTC,
|
||||
&signature.PayloadHash,
|
||||
&signature.Signature,
|
||||
&signature.Nonce,
|
||||
&signature.CreatedAt,
|
||||
&signature.Referer,
|
||||
&signature.PrevHash,
|
||||
)
|
||||
if err != nil {
|
||||
if err := scanSignature(rows, signature); err != nil {
|
||||
continue
|
||||
}
|
||||
signatures = append(signatures, signature)
|
||||
@@ -140,21 +150,7 @@ func (r *SignatureRepository) GetByUser(ctx context.Context, userSub string) ([]
|
||||
var signatures []*models.Signature
|
||||
for rows.Next() {
|
||||
signature := &models.Signature{}
|
||||
err := rows.Scan(
|
||||
&signature.ID,
|
||||
&signature.DocID,
|
||||
&signature.UserSub,
|
||||
&signature.UserEmail,
|
||||
&signature.UserName,
|
||||
&signature.SignedAtUTC,
|
||||
&signature.PayloadHash,
|
||||
&signature.Signature,
|
||||
&signature.Nonce,
|
||||
&signature.CreatedAt,
|
||||
&signature.Referer,
|
||||
&signature.PrevHash,
|
||||
)
|
||||
if err != nil {
|
||||
if err := scanSignature(rows, signature); err != nil {
|
||||
continue
|
||||
}
|
||||
signatures = append(signatures, signature)
|
||||
@@ -195,26 +191,13 @@ func (r *SignatureRepository) CheckUserSignatureStatus(ctx context.Context, docI
|
||||
func (r *SignatureRepository) GetLastSignature(ctx context.Context) (*models.Signature, error) {
|
||||
query := `
|
||||
SELECT id, doc_id, user_sub, user_email, user_name, signed_at, payload_hash, signature, nonce, created_at, referer, prev_hash
|
||||
FROM signatures
|
||||
ORDER BY id DESC
|
||||
FROM signatures
|
||||
ORDER BY id DESC
|
||||
LIMIT 1
|
||||
`
|
||||
|
||||
signature := &models.Signature{}
|
||||
err := r.db.QueryRowContext(ctx, query).Scan(
|
||||
&signature.ID,
|
||||
&signature.DocID,
|
||||
&signature.UserSub,
|
||||
&signature.UserEmail,
|
||||
&signature.UserName,
|
||||
&signature.SignedAtUTC,
|
||||
&signature.PayloadHash,
|
||||
&signature.Signature,
|
||||
&signature.Nonce,
|
||||
&signature.CreatedAt,
|
||||
&signature.Referer,
|
||||
&signature.PrevHash,
|
||||
)
|
||||
err := scanSignature(r.db.QueryRowContext(ctx, query), signature)
|
||||
|
||||
if err != nil {
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
@@ -243,21 +226,7 @@ func (r *SignatureRepository) GetAllSignaturesOrdered(ctx context.Context) ([]*m
|
||||
var signatures []*models.Signature
|
||||
for rows.Next() {
|
||||
signature := &models.Signature{}
|
||||
err := rows.Scan(
|
||||
&signature.ID,
|
||||
&signature.DocID,
|
||||
&signature.UserSub,
|
||||
&signature.UserEmail,
|
||||
&signature.UserName,
|
||||
&signature.SignedAtUTC,
|
||||
&signature.PayloadHash,
|
||||
&signature.Signature,
|
||||
&signature.Nonce,
|
||||
&signature.CreatedAt,
|
||||
&signature.Referer,
|
||||
&signature.PrevHash,
|
||||
)
|
||||
if err != nil {
|
||||
if err := scanSignature(rows, signature); err != nil {
|
||||
continue
|
||||
}
|
||||
signatures = append(signatures, signature)
|
||||
|
||||
@@ -62,7 +62,7 @@ func TestRepository_DatabaseConstraints_Integration(t *testing.T) {
|
||||
{
|
||||
name: "valid signature with nulls",
|
||||
modifyFn: func(s *models.Signature) {
|
||||
s.UserName = nil
|
||||
s.UserName = ""
|
||||
s.Referer = nil
|
||||
s.PrevHash = nil
|
||||
},
|
||||
@@ -403,7 +403,7 @@ func TestRepository_EdgeCases_Integration(t *testing.T) {
|
||||
// Test with empty strings for nullable fields
|
||||
sig := factory.CreateValidSignature()
|
||||
emptyString := ""
|
||||
sig.UserName = &emptyString
|
||||
sig.UserName = emptyString
|
||||
sig.Referer = &emptyString
|
||||
sig.PrevHash = &emptyString
|
||||
|
||||
@@ -418,7 +418,7 @@ func TestRepository_EdgeCases_Integration(t *testing.T) {
|
||||
}
|
||||
|
||||
// Verify empty strings are preserved (not converted to NULL)
|
||||
if result.UserName == nil || *result.UserName != "" {
|
||||
if result.UserName != "" {
|
||||
t.Error("Empty string UserName not preserved")
|
||||
}
|
||||
if result.Referer == nil || *result.Referer != "" {
|
||||
|
||||
Reference in New Issue
Block a user