appview/db: simplify db handlers for notifications #679

merged
opened by oppi.li targeting master from push-myrnwrtyvllw
Changed files
+125 -73
appview
db
models
notifications
notify
db
settings
-1
appview/db/artifact.go
···
)
rows, err := e.Query(query, args...)
-
if err != nil {
return nil, err
}
+81 -45
appview/db/notifications.go
···
"strings"
"time"
+
"github.com/bluesky-social/indigo/atproto/syntax"
"tangled.org/core/appview/models"
"tangled.org/core/appview/pagination"
)
-
func (d *DB) CreateNotification(ctx context.Context, notification *models.Notification) error {
+
func CreateNotification(e Execer, notification *models.Notification) error {
query := `
INSERT INTO notifications (recipient_did, actor_did, type, entity_type, entity_id, read, repo_id, issue_id, pull_id)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
`
-
result, err := d.DB.ExecContext(ctx, query,
+
result, err := e.Exec(query,
notification.RecipientDid,
notification.ActorDid,
string(notification.Type),
···
return count, nil
}
-
func (d *DB) MarkNotificationRead(ctx context.Context, notificationID int64, userDID string) error {
+
func MarkNotificationRead(e Execer, notificationID int64, userDID string) error {
idFilter := FilterEq("id", notificationID)
recipientFilter := FilterEq("recipient_did", userDID)
···
args := append(idFilter.Arg(), recipientFilter.Arg()...)
-
result, err := d.DB.ExecContext(ctx, query, args...)
+
result, err := e.Exec(query, args...)
if err != nil {
return fmt.Errorf("failed to mark notification as read: %w", err)
}
···
return nil
}
-
func (d *DB) MarkAllNotificationsRead(ctx context.Context, userDID string) error {
+
func MarkAllNotificationsRead(e Execer, userDID string) error {
recipientFilter := FilterEq("recipient_did", userDID)
readFilter := FilterEq("read", 0)
···
args := append(recipientFilter.Arg(), readFilter.Arg()...)
-
_, err := d.DB.ExecContext(ctx, query, args...)
+
_, err := e.Exec(query, args...)
if err != nil {
return fmt.Errorf("failed to mark all notifications as read: %w", err)
}
···
return nil
}
-
func (d *DB) DeleteNotification(ctx context.Context, notificationID int64, userDID string) error {
+
func DeleteNotification(e Execer, notificationID int64, userDID string) error {
idFilter := FilterEq("id", notificationID)
recipientFilter := FilterEq("recipient_did", userDID)
···
args := append(idFilter.Arg(), recipientFilter.Arg()...)
-
result, err := d.DB.ExecContext(ctx, query, args...)
+
result, err := e.Exec(query, args...)
if err != nil {
return fmt.Errorf("failed to delete notification: %w", err)
}
···
return nil
}
-
func (d *DB) GetNotificationPreferences(ctx context.Context, userDID string) (*models.NotificationPreferences, error) {
-
userFilter := FilterEq("user_did", userDID)
+
func GetNotificationPreference(e Execer, userDid string) (*models.NotificationPreferences, error) {
+
prefs, err := GetNotificationPreferences(e, FilterEq("user_did", userDid))
+
if err != nil {
+
return nil, err
+
}
+
+
p, ok := prefs[syntax.DID(userDid)]
+
if !ok {
+
return models.DefaultNotificationPreferences(syntax.DID(userDid)), nil
+
}
+
+
return p, nil
+
}
+
+
func GetNotificationPreferences(e Execer, filters ...filter) (map[syntax.DID]*models.NotificationPreferences, error) {
+
prefsMap := make(map[syntax.DID]*models.NotificationPreferences)
+
+
var conditions []string
+
var args []any
+
for _, filter := range filters {
+
conditions = append(conditions, filter.Condition())
+
args = append(args, filter.Arg()...)
+
}
+
+
whereClause := ""
+
if conditions != nil {
+
whereClause = " where " + strings.Join(conditions, " and ")
+
}
query := fmt.Sprintf(`
-
SELECT id, user_did, repo_starred, issue_created, issue_commented, pull_created,
-
pull_commented, followed, pull_merged, issue_closed, email_notifications
-
FROM notification_preferences
-
WHERE %s
-
`, userFilter.Condition())
-
-
var prefs models.NotificationPreferences
-
err := d.DB.QueryRowContext(ctx, query, userFilter.Arg()...).Scan(
-
&prefs.ID,
-
&prefs.UserDid,
-
&prefs.RepoStarred,
-
&prefs.IssueCreated,
-
&prefs.IssueCommented,
-
&prefs.PullCreated,
-
&prefs.PullCommented,
-
&prefs.Followed,
-
&prefs.PullMerged,
-
&prefs.IssueClosed,
-
&prefs.EmailNotifications,
-
)
+
select
+
id,
+
user_did,
+
repo_starred,
+
issue_created,
+
issue_commented,
+
pull_created,
+
pull_commented,
+
followed,
+
pull_merged,
+
issue_closed,
+
email_notifications
+
from
+
notification_preferences
+
%s
+
`, whereClause)
+
rows, err := e.Query(query, args...)
if err != nil {
-
if err == sql.ErrNoRows {
-
return &models.NotificationPreferences{
-
UserDid: userDID,
-
RepoStarred: true,
-
IssueCreated: true,
-
IssueCommented: true,
-
PullCreated: true,
-
PullCommented: true,
-
Followed: true,
-
PullMerged: true,
-
IssueClosed: true,
-
EmailNotifications: false,
-
}, nil
+
return nil, err
+
}
+
defer rows.Close()
+
+
for rows.Next() {
+
var prefs models.NotificationPreferences
+
if err := rows.Scan(
+
&prefs.ID,
+
&prefs.UserDid,
+
&prefs.RepoStarred,
+
&prefs.IssueCreated,
+
&prefs.IssueCommented,
+
&prefs.PullCreated,
+
&prefs.PullCommented,
+
&prefs.Followed,
+
&prefs.PullMerged,
+
&prefs.IssueClosed,
+
&prefs.EmailNotifications,
+
); err != nil {
+
return nil, err
}
-
return nil, fmt.Errorf("failed to get notification preferences: %w", err)
+
+
prefsMap[prefs.UserDid] = &prefs
+
}
+
+
if err := rows.Err(); err != nil {
+
return nil, err
}
-
return &prefs, nil
+
return prefsMap, nil
}
func (d *DB) UpdateNotificationPreferences(ctx context.Context, prefs *models.NotificationPreferences) error {
+17 -1
appview/models/notifications.go
···
import (
"time"
+
+
"github.com/bluesky-social/indigo/atproto/syntax"
)
type NotificationType string
···
type NotificationPreferences struct {
ID int64
-
UserDid string
+
UserDid syntax.DID
RepoStarred bool
IssueCreated bool
IssueCommented bool
···
IssueClosed bool
EmailNotifications bool
}
+
func DefaultNotificationPreferences(user syntax.DID) *NotificationPreferences {
+
return &NotificationPreferences{
+
UserDid: user,
+
RepoStarred: true,
+
IssueCreated: true,
+
IssueCommented: true,
+
PullCreated: true,
+
PullCommented: true,
+
Followed: true,
+
PullMerged: true,
+
IssueClosed: true,
+
EmailNotifications: false,
+
}
+
}
+4 -4
appview/notifications/notifications.go
···
return
}
-
err = n.db.MarkAllNotificationsRead(r.Context(), user.Did)
+
err = db.MarkAllNotificationsRead(n.db, user.Did)
if err != nil {
l.Error("failed to mark notifications as read", "err", err)
}
···
return
}
-
err = n.db.MarkNotificationRead(r.Context(), notificationID, userDid)
+
err = db.MarkNotificationRead(n.db, notificationID, userDid)
if err != nil {
http.Error(w, "Failed to mark notification as read", http.StatusInternalServerError)
return
···
func (n *Notifications) markAllRead(w http.ResponseWriter, r *http.Request) {
userDid := n.oauth.GetDid(r)
-
err := n.db.MarkAllNotificationsRead(r.Context(), userDid)
+
err := db.MarkAllNotificationsRead(n.db, userDid)
if err != nil {
http.Error(w, "Failed to mark all notifications as read", http.StatusInternalServerError)
return
···
return
}
-
err = n.db.DeleteNotification(r.Context(), notificationID, userDid)
+
err = db.DeleteNotification(n.db, notificationID, userDid)
if err != nil {
http.Error(w, "Failed to delete notification", http.StatusInternalServerError)
return
+20 -20
appview/notify/db/db.go
···
}
// check if user wants these notifications
-
prefs, err := n.db.GetNotificationPreferences(ctx, repo.Did)
+
prefs, err := db.GetNotificationPreference(n.db, repo.Did)
if err != nil {
log.Printf("NewStar: failed to get notification preferences for %s: %v", repo.Did, err)
return
···
EntityId: string(star.RepoAt),
RepoId: &repo.Id,
}
-
err = n.db.CreateNotification(ctx, notification)
+
err = db.CreateNotification(n.db, notification)
if err != nil {
log.Printf("NewStar: failed to create notification: %v", err)
return
···
return
}
-
prefs, err := n.db.GetNotificationPreferences(ctx, repo.Did)
+
prefs, err := db.GetNotificationPreference(n.db, repo.Did)
if err != nil {
log.Printf("NewIssue: failed to get notification preferences for %s: %v", repo.Did, err)
return
···
IssueId: &issue.Id,
}
-
err = n.db.CreateNotification(ctx, notification)
+
err = db.CreateNotification(n.db, notification)
if err != nil {
log.Printf("NewIssue: failed to create notification: %v", err)
return
···
// notify issue author (if not the commenter)
if issue.Did != comment.Did {
-
prefs, err := n.db.GetNotificationPreferences(ctx, issue.Did)
+
prefs, err := db.GetNotificationPreference(n.db, issue.Did)
if err == nil && prefs.IssueCommented {
recipients[issue.Did] = true
} else if err != nil {
···
// notify repo owner (if not the commenter and not already added)
if repo.Did != comment.Did && repo.Did != issue.Did {
-
prefs, err := n.db.GetNotificationPreferences(ctx, repo.Did)
+
prefs, err := db.GetNotificationPreference(n.db, repo.Did)
if err == nil && prefs.IssueCommented {
recipients[repo.Did] = true
} else if err != nil {
···
IssueId: &issue.Id,
}
-
err = n.db.CreateNotification(ctx, notification)
+
err = db.CreateNotification(n.db, notification)
if err != nil {
log.Printf("NewIssueComment: failed to create notification for %s: %v", recipientDid, err)
}
···
}
func (n *databaseNotifier) NewFollow(ctx context.Context, follow *models.Follow) {
-
prefs, err := n.db.GetNotificationPreferences(ctx, follow.SubjectDid)
+
prefs, err := db.GetNotificationPreference(n.db, follow.SubjectDid)
if err != nil {
log.Printf("NewFollow: failed to get notification preferences for %s: %v", follow.SubjectDid, err)
return
···
EntityId: follow.UserDid,
}
-
err = n.db.CreateNotification(ctx, notification)
+
err = db.CreateNotification(n.db, notification)
if err != nil {
log.Printf("NewFollow: failed to create notification: %v", err)
return
···
return
}
-
prefs, err := n.db.GetNotificationPreferences(ctx, repo.Did)
+
prefs, err := db.GetNotificationPreference(n.db, repo.Did)
if err != nil {
log.Printf("NewPull: failed to get notification preferences for %s: %v", repo.Did, err)
return
···
PullId: func() *int64 { id := int64(pull.ID); return &id }(),
}
-
err = n.db.CreateNotification(ctx, notification)
+
err = db.CreateNotification(n.db, notification)
if err != nil {
log.Printf("NewPull: failed to create notification: %v", err)
return
···
// notify pull request author (if not the commenter)
if pull.OwnerDid != comment.OwnerDid {
-
prefs, err := n.db.GetNotificationPreferences(ctx, pull.OwnerDid)
+
prefs, err := db.GetNotificationPreference(n.db, pull.OwnerDid)
if err == nil && prefs.PullCommented {
recipients[pull.OwnerDid] = true
} else if err != nil {
···
// notify repo owner (if not the commenter and not already added)
if repo.Did != comment.OwnerDid && repo.Did != pull.OwnerDid {
-
prefs, err := n.db.GetNotificationPreferences(ctx, repo.Did)
+
prefs, err := db.GetNotificationPreference(n.db, repo.Did)
if err == nil && prefs.PullCommented {
recipients[repo.Did] = true
} else if err != nil {
···
PullId: func() *int64 { id := int64(pull.ID); return &id }(),
}
-
err = n.db.CreateNotification(ctx, notification)
+
err = db.CreateNotification(n.db, notification)
if err != nil {
log.Printf("NewPullComment: failed to create notification for %s: %v", recipientDid, err)
}
···
}
// Check if user wants these notifications
-
prefs, err := n.db.GetNotificationPreferences(ctx, repo.Did)
+
prefs, err := db.GetNotificationPreference(n.db, repo.Did)
if err != nil {
log.Printf("NewIssueClosed: failed to get notification preferences for %s: %v", repo.Did, err)
return
···
IssueId: &issue.Id,
}
-
err = n.db.CreateNotification(ctx, notification)
+
err = db.CreateNotification(n.db, notification)
if err != nil {
log.Printf("NewIssueClosed: failed to create notification: %v", err)
return
···
}
// Check if user wants these notifications
-
prefs, err := n.db.GetNotificationPreferences(ctx, pull.OwnerDid)
+
prefs, err := db.GetNotificationPreference(n.db, pull.OwnerDid)
if err != nil {
log.Printf("NewPullMerged: failed to get notification preferences for %s: %v", pull.OwnerDid, err)
return
···
PullId: func() *int64 { id := int64(pull.ID); return &id }(),
}
-
err = n.db.CreateNotification(ctx, notification)
+
err = db.CreateNotification(n.db, notification)
if err != nil {
log.Printf("NewPullMerged: failed to create notification: %v", err)
return
···
}
// Check if user wants these notifications - reuse pull_merged preference for now
-
prefs, err := n.db.GetNotificationPreferences(ctx, pull.OwnerDid)
+
prefs, err := db.GetNotificationPreference(n.db, pull.OwnerDid)
if err != nil {
log.Printf("NewPullClosed: failed to get notification preferences for %s: %v", pull.OwnerDid, err)
return
···
PullId: func() *int64 { id := int64(pull.ID); return &id }(),
}
-
err = n.db.CreateNotification(ctx, notification)
+
err = db.CreateNotification(n.db, notification)
if err != nil {
log.Printf("NewPullClosed: failed to create notification: %v", err)
return
+3 -2
appview/settings/settings.go
···
"tangled.org/core/tid"
comatproto "github.com/bluesky-social/indigo/api/atproto"
+
"github.com/bluesky-social/indigo/atproto/syntax"
lexutil "github.com/bluesky-social/indigo/lex/util"
"github.com/gliderlabs/ssh"
"github.com/google/uuid"
···
user := s.OAuth.GetUser(r)
did := s.OAuth.GetDid(r)
-
prefs, err := s.Db.GetNotificationPreferences(r.Context(), did)
+
prefs, err := db.GetNotificationPreference(s.Db, did)
if err != nil {
log.Printf("failed to get notification preferences: %s", err)
s.Pages.Notice(w, "settings-notifications-error", "Unable to load notification preferences.")
···
did := s.OAuth.GetDid(r)
prefs := &models.NotificationPreferences{
-
UserDid: did,
+
UserDid: syntax.DID(did),
RepoStarred: r.FormValue("repo_starred") == "on",
IssueCreated: r.FormValue("issue_created") == "on",
IssueCommented: r.FormValue("issue_commented") == "on",