notifications

package
v0.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Feb 5, 2026 License: AGPL-3.0 Imports: 15 Imported by: 0

README

Notifications Service

The Notifications Service is a comprehensive service for handling all notification operations in the Lesser project's API alignment implementation. This service follows the established patterns from other services in the project and provides complete functionality for notification management.

Overview

This service provides the core functionality for Phase 2.7 of the API Alignment Implementation, handling notification creation, reading, clearing, listing, and real-time event emission for ActivityPub and application notifications.

Features

Core Operations
  • CreateNotification: Create new notifications with comprehensive validation and event emission
  • MarkAsRead: Mark individual notifications as read with event streaming
  • ClearNotifications: Clear notifications with multiple strategies (all, by type, specific IDs)
  • GetNotification: Retrieve single notifications with privacy checks
  • ListNotifications: Get paginated notification lists with extensive filtering
Event Streaming

The service emits real-time events for:

  • notification.created - When new notifications are created
  • notification.read - When notifications are marked as read
  • notification.cleared - When notifications are cleared

All events are published to the recipient's notification stream for real-time updates.

Notification Types Supported
  • mention - User mentions in posts
  • reblog - Post reblogs/boosts
  • favourite - Post likes/favorites
  • follow - New followers
  • follow_request - Follow requests (for protected accounts)
  • poll - Poll updates
  • status - General status updates
  • update - Content updates
  • admin.sign_up - Administrative sign-up notifications
  • admin.report - Administrative reports
Advanced Features
Notification Filtering
  • Filter by notification types (include/exclude)
  • Filter by read/unread status
  • Filter by actor (who triggered the notification)
  • Filter by target type (what the notification is about)
  • Support for grouped notifications
  • Date range filtering with pagination
Notification Grouping
  • Automatic grouping of similar notifications within time windows
  • Group key generation for consolidation
  • Group count tracking for UI display
Summary Statistics
  • Total notification counts
  • Unread notification counts
  • Counts by notification type
  • Last notification timestamp

Architecture

Dependencies
  • NotificationRepository: Data access for notification operations
  • AccountRepository: User account validation and lookups
  • Publisher: Real-time event streaming to WebSocket clients
  • Logger: Structured logging with zap
Command Pattern

The service uses command structs for all write operations:

  • CreateNotificationCommand - Create new notifications
  • MarkAsReadCommand - Mark notifications as read
  • ClearCommand - Clear notifications with various strategies
Query Pattern

Read operations use query structs:

  • GetNotificationQuery - Retrieve single notification
  • ListNotificationsQuery - List notifications with filtering and pagination
Result Pattern

All operations return result structs containing:

  • The primary data (notification, list of notifications)
  • Associated events that were emitted
  • Pagination information for lists
  • Summary statistics where applicable

Usage Examples

Creating a Mention Notification
service := NewService(notificationRepo, accountRepo, publisher, logger, "example.com")

cmd := &CreateNotificationCommand{
    UserID:     "alice",
    Type:       "mention", 
    ActorID:    "bob",
    ActorType:  "user",
    TargetID:   "status123",
    TargetType: "status",
    Title:      "You were mentioned",
    Body:       "Bob mentioned you in a post",
}

result, err := service.CreateNotification(ctx, cmd)
if err != nil {
    // Handle error
}

// Notification created and events emitted to alice's stream
Listing Unread Notifications
query := &ListNotificationsQuery{
    UserID:     "alice",
    OnlyUnread: true,
    Pagination: interfaces.PaginationOptions{
        Limit: 20,
    },
}

result, err := service.ListNotifications(ctx, query)
if err != nil {
    // Handle error  
}

// result.Notifications contains unread notifications
// result.Summary contains count statistics
Clearing Notifications by Type
cmd := &ClearCommand{
    UserID: "alice", 
    Types:  []string{"mention", "follow"},
}

result, err := service.ClearNotifications(ctx, cmd)
if err != nil {
    // Handle error
}

// All mention and follow notifications cleared
// Events emitted to alice's stream

Testing

The service includes comprehensive tests covering:

  • Successful operations for all methods
  • Validation error cases
  • Authorization and privacy checks
  • Repository error handling
  • Event emission verification
  • Mock implementations for all dependencies

Run tests with:

go test ./pkg/services/notifications/ -v

Integration

The Notifications Service follows the same patterns as other services in the Lesser project:

  • Uses repository interfaces for data access
  • Emits events through the streaming Publisher
  • Follows command/query/result patterns
  • Provides comprehensive error handling
  • Includes structured logging
  • Supports dependency injection

This service completes Phase 2 of the API Alignment Implementation and can be integrated with the API handlers, GraphQL resolvers, and federation components.

Documentation

Overview

Package notifications provides the Notifications Service for the Lesser project's API alignment. This service handles all notification operations including creation, reading, clearing, and notification preferences. It emits appropriate events for real-time streaming.

Index

Constants

This section is empty.

Variables

View Source
var (
	// General validation errors
	ErrValidationFailed = errors.ValidationFailedWithField("notification")
	ErrInvalidInput     = errors.NewValidationError("input", "invalid")

	// Notification operation errors
	ErrNotificationNotFound     = errors.NewAppError(errors.CodeNotFound, errors.CategoryBusiness, "notification not found")
	ErrNotificationAccessDenied = errors.AccessDeniedForResource("notification", "unknown")
	ErrNotificationCreateFailed = errors.FailedToCreate("notification", stdErrors.New("failed to create notification"))
	ErrNotificationUpdateFailed = errors.FailedToUpdate("notification", stdErrors.New("failed to update notification"))
	ErrNotificationClearFailed  = errors.ProcessingFailed("notification clearing", stdErrors.New("notification clearing failed"))
	ErrNotificationQueryFailed  = errors.FailedToQuery("notifications", stdErrors.New("failed to query notifications"))
	ErrNoClearCriteria          = errors.NewValidationError("clear_criteria", "no criteria specified")
	ErrNoClearMethodSpecified   = errors.NewValidationError("clear_method", "at least one criteria must be specified")
	ErrUnreadCountFailed        = errors.FailedToQuery("unread count", stdErrors.New("failed to get unread count"))
	ErrCountsByTypeFailed       = errors.FailedToQuery("counts by type", stdErrors.New("failed to get counts by type"))
)

Service-specific errors for the notifications package

Functions

This section is empty.

Types

type ClearCommand

type ClearCommand struct {
	UserID           string   `json:"user_id" validate:"required"`
	NotificationIDs  []string `json:"notification_ids"`   // Clear specific notifications
	Types            []string `json:"types"`              // Clear notifications of specific types
	ClearAll         bool     `json:"clear_all"`          // Clear all notifications
	OlderThanSeconds int64    `json:"older_than_seconds"` // Clear notifications older than N seconds
}

ClearCommand contains data needed to clear notifications

type ClearResult

type ClearResult struct {
	ClearedCount int64              `json:"cleared_count"`
	Events       []*streaming.Event `json:"events"`
}

ClearResult contains the results of a clear operation

type CreateNotificationCommand

type CreateNotificationCommand struct {
	UserID     string                 `json:"user_id" validate:"required"`  // Recipient user ID
	Type       string                 `json:"type" validate:"required"`     // Notification type
	ActorID    string                 `json:"actor_id" validate:"required"` // Who triggered the notification
	ActorType  string                 `json:"actor_type"`                   // Type of actor (user, remote_actor)
	TargetID   string                 `json:"target_id"`                    // What the notification is about
	TargetType string                 `json:"target_type"`                  // Type of target (status, user, account)
	Title      string                 `json:"title"`                        // Notification title
	Body       string                 `json:"body"`                         // Notification body
	Data       map[string]interface{} `json:"data"`                         // Additional data
	GroupKey   string                 `json:"group_key"`                    // Custom group key for consolidation
}

CreateNotificationCommand contains all data needed to create a new notification

type GetNotificationQuery

type GetNotificationQuery struct {
	NotificationID string `json:"notification_id" validate:"required"`
	UserID         string `json:"user_id" validate:"required"` // For privacy checks
}

GetNotificationQuery contains parameters for retrieving a single notification

type GroupedNotification

type GroupedNotification struct {
	ID                string                 `json:"id"`
	Type              string                 `json:"type"`
	GroupKey          string                 `json:"group_key"`
	Count             int                    `json:"count"`
	LatestCreatedAt   time.Time              `json:"latest_created_at"`
	EarliestCreatedAt time.Time              `json:"earliest_created_at"`
	IsRead            bool                   `json:"is_read"`
	SampleAccounts    []NotificationAccount  `json:"sample_accounts"`
	TargetStatus      *NotificationStatus    `json:"status,omitempty"`
	MostRecentNotif   *models.Notification   `json:"most_recent_notification"`
	AllNotifications  []*models.Notification `json:"all_notifications,omitempty"`
}

GroupedNotification represents a group of similar notifications

type GroupedNotificationsService

type GroupedNotificationsService struct {
	// contains filtered or unexported fields
}

GroupedNotificationsService provides advanced notification grouping functionality

func NewGroupedNotificationsService

func NewGroupedNotificationsService(logger *zap.Logger) *GroupedNotificationsService

NewGroupedNotificationsService creates a new grouped notifications service

func (*GroupedNotificationsService) GenerateGroupSummary

func (gns *GroupedNotificationsService) GenerateGroupSummary(
	group *GroupedNotification,
) string

GenerateGroupSummary generates a human-readable summary for a group

func (*GroupedNotificationsService) GroupNotifications

func (gns *GroupedNotificationsService) GroupNotifications(
	_ context.Context,
	notifications []*models.Notification,
	strategy *GroupingStrategy,
) ([]*GroupedNotification, error)

GroupNotifications groups similar notifications together

func (*GroupedNotificationsService) MarkGroupAsRead

func (gns *GroupedNotificationsService) MarkGroupAsRead(
	ctx context.Context,
	group *GroupedNotification,
	markReadFunc func(context.Context, string) error,
) error

MarkGroupAsRead marks all notifications in a group as read

type GroupingStrategy

type GroupingStrategy struct {
	TimeWindow    time.Duration `json:"time_window"`     // Group notifications within this time window
	MaxGroupSize  int           `json:"max_group_size"`  // Maximum notifications per group
	MinGroupSize  int           `json:"min_group_size"`  // Minimum to consider grouping
	SampleSize    int           `json:"sample_size"`     // Number of sample accounts to include
	GroupByType   bool          `json:"group_by_type"`   // Group by notification type
	GroupByTarget bool          `json:"group_by_target"` // Group by target object
}

GroupingStrategy defines how notifications should be grouped

func DefaultGroupingStrategy

func DefaultGroupingStrategy() *GroupingStrategy

DefaultGroupingStrategy returns the default grouping strategy

type ListNotificationsQuery

type ListNotificationsQuery struct {
	UserID       string                       `json:"user_id" validate:"required"`
	Types        []string                     `json:"types"`         // Filter by notification types
	ExcludeTypes []string                     `json:"exclude_types"` // Exclude specific types
	OnlyUnread   bool                         `json:"only_unread"`   // Only unread notifications
	IncludeRead  bool                         `json:"include_read"`  // Include read notifications (default: true)
	GroupedOnly  bool                         `json:"grouped_only"`  // Only grouped notifications
	ActorID      string                       `json:"actor_id"`      // Filter by specific actor
	TargetType   string                       `json:"target_type"`   // Filter by target type
	Pagination   interfaces.PaginationOptions `json:"pagination"`
}

ListNotificationsQuery contains parameters for listing notifications with filters

type MarkAsReadCommand

type MarkAsReadCommand struct {
	NotificationID string `json:"notification_id" validate:"required"`
	UserID         string `json:"user_id" validate:"required"`
}

MarkAsReadCommand contains data needed to mark a notification as read

type NotificationAccount

type NotificationAccount struct {
	ID          string    `json:"id"`
	Username    string    `json:"username"`
	DisplayName string    `json:"display_name"`
	Avatar      string    `json:"avatar"`
	IsBot       bool      `json:"bot"`
	CreatedAt   time.Time `json:"created_at"`
}

NotificationAccount represents an account in grouped notifications

type NotificationListResult

type NotificationListResult struct {
	Notifications []*models.Notification                            `json:"notifications"`
	Pagination    *interfaces.PaginatedResult[*models.Notification] `json:"pagination"`
	Summary       *NotificationSummary                              `json:"summary"`
	Events        []*streaming.Event                                `json:"events"`
}

NotificationListResult contains multiple notifications and pagination information

type NotificationResult

type NotificationResult struct {
	Notification *models.Notification `json:"notification"`
	Events       []*streaming.Event   `json:"events"`
}

NotificationResult contains a notification and associated events that were emitted

type NotificationStatus

type NotificationStatus struct {
	ID         string    `json:"id"`
	Content    string    `json:"content"`
	CreatedAt  time.Time `json:"created_at"`
	URL        string    `json:"url"`
	Visibility string    `json:"visibility"`
}

NotificationStatus represents a status in grouped notifications

type NotificationSummary

type NotificationSummary struct {
	TotalCount         int64            `json:"total_count"`
	UnreadCount        int64            `json:"unread_count"`
	CountsByType       map[string]int64 `json:"counts_by_type"`
	LastNotificationAt *time.Time       `json:"last_notification_at,omitempty"`
}

NotificationSummary provides summary statistics

type Service

type Service struct {
	// contains filtered or unexported fields
}

Service provides notification operations

func NewService

func NewService(
	notificationRepo interfaces.NotificationRepository,
	accountRepo interfaces.AccountRepository,
	publisher streaming.Publisher,
	logger *zap.Logger,
	domainName string,
	pushService pushQueue,
) *Service

NewService creates a new Notifications Service with the required dependencies

func (*Service) ClearNotifications

func (s *Service) ClearNotifications(ctx context.Context, cmd *ClearCommand) (*ClearResult, error)

ClearNotifications clears notifications based on the specified criteria and emits events

func (*Service) CreateNotification

func (s *Service) CreateNotification(ctx context.Context, cmd *CreateNotificationCommand) (*NotificationResult, error)

CreateNotification creates a new notification, stores it, and emits events

func (*Service) DispatchPushForNotification

func (s *Service) DispatchPushForNotification(ctx context.Context, notification *models.Notification)

DispatchPushForNotification replays the push queue logic for an already persisted notification.

func (*Service) GetNotification

func (s *Service) GetNotification(ctx context.Context, query *GetNotificationQuery) (*models.Notification, error)

GetNotification retrieves a single notification with privacy checks

func (*Service) ListNotifications

func (s *Service) ListNotifications(ctx context.Context, query *ListNotificationsQuery) (*NotificationListResult, error)

ListNotifications retrieves notifications based on filters and pagination

func (*Service) MarkAsRead

func (s *Service) MarkAsRead(ctx context.Context, cmd *MarkAsReadCommand) (*NotificationResult, error)

MarkAsRead marks a notification as read and emits events

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL