package storage import ( "context" "database/sql" "encoding/json" "errors" "fmt" "time" ) func (s *Store) UpdateOwnDisplayNameAudited(ctx context.Context, userID, displayName string, audit AppendAuditInput) error { return s.UpdateOwnDisplayNameAuditedAt(ctx, userID, displayName, audit, s.now()) } func (s *Store) UpdateOwnDisplayNameAuditedAt(ctx context.Context, userID, displayName string, audit AppendAuditInput, now time.Time) error { const operation = "update own display name" if err := validateDisplayName(displayName); err != nil { return invalid(operation, err) } fields := map[string]any{} if audit.Detail != "" { if err := json.Unmarshal([]byte(audit.Detail), &fields); err != nil || fields == nil { return invalid(operation, fmt.Errorf("audit detail is not a JSON object")) } } return s.auditedAt(ctx, operation, audit.Actor, now, func(tx *sql.Tx, at time.Time) error { var username, current string var disabled int err := tx.QueryRowContext(ctx, `SELECT username, display_name, disabled FROM users WHERE id = ?`, userID).Scan(&username, ¤t, &disabled) switch { case errors.Is(err, sql.ErrNoRows): return fmt.Errorf("%s: %w", operation, ErrNotFound) case err != nil: return classify(operation, err) case disabled != 0: return fmt.Errorf("%s: %w: the user is disabled", operation, ErrNotFound) } if _, err := tx.ExecContext(ctx, `UPDATE users SET display_name = ? WHERE id = ?`, displayName, userID); err != nil { return classify(operation, err) } fields["username"] = username fields["display_name_changed"] = current != displayName return writeAudit(ctx, tx, operation, audit.Actor, audit.Action, audit.Subject, at, fields) }) }