minireader/internal/minireader/reader/reader.go

158 lines
4.1 KiB
Go
Raw Normal View History

2023-10-25 14:49:11 +00:00
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
})
}