158 lines
4.1 KiB
Go
158 lines
4.1 KiB
Go
package reader
|
|
|
|
import (
|
|
"context"
|
|
"log/slog"
|
|
|
|
"github.com/google/uuid"
|
|
|
|
"borodyadka.dev/borodyadka/minireader/internal/minireader/dto"
|
|
"borodyadka.dev/borodyadka/minireader/internal/minireader/repositories"
|
|
"borodyadka.dev/borodyadka/minireader/internal/pkg/validator"
|
|
)
|
|
|
|
type feedRepository interface {
|
|
ListAllUserFeeds(ctx context.Context, user *dto.User) ([]dto.Feed, int, error)
|
|
FindOrCreate(ctx context.Context, input *dto.CreateFeed) (dto.Feed, bool, error)
|
|
Delete(ctx context.Context, id uuid.UUID) (bool, error)
|
|
}
|
|
|
|
type itemRepository interface{}
|
|
|
|
type subscriptionRepository interface {
|
|
Create(ctx context.Context, user *dto.User, feed uuid.UUID) error
|
|
Update(ctx context.Context, user *dto.User, feed uuid.UUID, input dto.UpdateFeed) (bool, error)
|
|
Delete(ctx context.Context, user *dto.User) (bool, error)
|
|
CountByFeed(ctx context.Context, feed uuid.UUID) (int, error)
|
|
}
|
|
|
|
type userRepository interface {
|
|
EnsureUser(ctx context.Context, user *dto.User) error
|
|
}
|
|
|
|
type fetcher interface {
|
|
ForceRefresh(ctx context.Context, feed dto.Feed) error
|
|
}
|
|
|
|
type Reader struct {
|
|
feeds feedRepository
|
|
subscriptions subscriptionRepository
|
|
items itemRepository
|
|
users userRepository
|
|
fetcher fetcher
|
|
|
|
logger *slog.Logger
|
|
}
|
|
|
|
func New(opts ...Option) *Reader {
|
|
reader := &Reader{}
|
|
|
|
for _, opt := range opts {
|
|
opt(reader)
|
|
}
|
|
|
|
return reader
|
|
}
|
|
|
|
func (r *Reader) EnsureUser(ctx context.Context, user *dto.User) error {
|
|
return r.users.EnsureUser(ctx, user)
|
|
}
|
|
|
|
func (r *Reader) ListAllUserFeeds(ctx context.Context, user *dto.User) ([]dto.Feed, int, error) {
|
|
return r.feeds.ListAllUserFeeds(ctx, user)
|
|
}
|
|
|
|
func (r *Reader) FindOrCreateFeed(ctx context.Context, input *dto.CreateFeed) (dto.Feed, error) {
|
|
logger := r.logger.With("method", "FindOrCreateFeed")
|
|
var feed dto.Feed
|
|
|
|
err := repositories.Transactional(ctx, func(ctx context.Context) (err error) {
|
|
var created bool
|
|
feed, created, err = r.feeds.FindOrCreate(ctx, input)
|
|
if err != nil {
|
|
logger.Error("find or create feed failed", "error", err)
|
|
return err
|
|
}
|
|
|
|
if created {
|
|
logger.Debug("feed created", "feed", feed.ID)
|
|
err := r.fetcher.ForceRefresh(ctx, feed)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
return nil
|
|
})
|
|
|
|
return feed, err
|
|
}
|
|
|
|
// CreateSubscription will create a new subscription to feed (if feed not exists, it will be created)
|
|
func (r *Reader) CreateSubscription(ctx context.Context, user *dto.User, input *dto.CreateFeed) (dto.Feed, error) {
|
|
logger := r.logger.With("method", "CreateSubscription")
|
|
var feed dto.Feed
|
|
|
|
if err := validator.Struct(input); err != nil {
|
|
return feed, err
|
|
}
|
|
|
|
err := repositories.Transactional(ctx, func(ctx context.Context) (err error) {
|
|
feed, err = r.FindOrCreateFeed(ctx, input)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
logger.Debug("create subscription", "feed", feed.ID)
|
|
return r.subscriptions.Create(ctx, user, feed.ID)
|
|
})
|
|
if err != nil {
|
|
return feed, err
|
|
}
|
|
|
|
return feed, nil
|
|
}
|
|
|
|
// UpdateSubscription will update subscription info (e.g. change title)
|
|
func (r *Reader) UpdateSubscription(ctx context.Context, user *dto.User, feed uuid.UUID, input dto.UpdateFeed) error {
|
|
logger := r.logger.With("method", "UpdateSubscription")
|
|
|
|
updated, err := r.subscriptions.Update(ctx, user, feed, input)
|
|
if !updated {
|
|
return dto.ErrNotFound
|
|
}
|
|
|
|
logger.Debug("subscription updated", "feed", feed)
|
|
|
|
return err
|
|
}
|
|
|
|
// DeleteSubscription will delete user subscription and remove feed if no active subscriptions left
|
|
func (r *Reader) DeleteSubscription(ctx context.Context, user *dto.User, feed uuid.UUID) error {
|
|
logger := r.logger.With("method", "DeleteSubscription")
|
|
|
|
return repositories.Transactional(ctx, func(ctx context.Context) error {
|
|
deleted, err := r.subscriptions.Delete(ctx, user)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if !deleted {
|
|
return dto.ErrNotFound
|
|
}
|
|
|
|
logger.Debug("subscription removed", "feed", feed)
|
|
|
|
subs, err := r.subscriptions.CountByFeed(ctx, feed)
|
|
if err != nil {
|
|
logger.Error("failed to count feed subscriptions", "error", err)
|
|
} else if subs == 0 {
|
|
_, err = r.feeds.Delete(ctx, feed)
|
|
if err != nil {
|
|
logger.Error("failed to delete feed", "error", err)
|
|
}
|
|
}
|
|
|
|
return nil
|
|
})
|
|
}
|