A community based topic aggregation platform built on atproto
at main 2.7 kB view raw
1package communityFeeds 2 3import ( 4 "Coves/internal/core/communities" 5 "context" 6 "fmt" 7) 8 9type feedService struct { 10 repo Repository 11 communityService communities.Service 12} 13 14// NewCommunityFeedService creates a new feed service 15func NewCommunityFeedService( 16 repo Repository, 17 communityService communities.Service, 18) Service { 19 return &feedService{ 20 repo: repo, 21 communityService: communityService, 22 } 23} 24 25// GetCommunityFeed retrieves posts from a community with sorting 26func (s *feedService) GetCommunityFeed(ctx context.Context, req GetCommunityFeedRequest) (*FeedResponse, error) { 27 // 1. Validate request 28 if err := s.validateRequest(&req); err != nil { 29 return nil, err 30 } 31 32 // 2. Resolve community identifier (handle or DID) to DID 33 communityDID, err := s.communityService.ResolveCommunityIdentifier(ctx, req.Community) 34 if err != nil { 35 if communities.IsNotFound(err) { 36 return nil, ErrCommunityNotFound 37 } 38 if communities.IsValidationError(err) { 39 return nil, NewValidationError("community", err.Error()) 40 } 41 return nil, fmt.Errorf("failed to resolve community identifier: %w", err) 42 } 43 44 // 3. Update request with resolved DID 45 req.Community = communityDID 46 47 // 4. Fetch feed from repository (hydrated posts) 48 feedPosts, cursor, err := s.repo.GetCommunityFeed(ctx, req) 49 if err != nil { 50 return nil, fmt.Errorf("failed to get community feed: %w", err) 51 } 52 53 // 5. Return feed response 54 return &FeedResponse{ 55 Feed: feedPosts, 56 Cursor: cursor, 57 }, nil 58} 59 60// validateRequest validates the feed request parameters 61func (s *feedService) validateRequest(req *GetCommunityFeedRequest) error { 62 // Validate community identifier 63 if req.Community == "" { 64 return NewValidationError("community", "community parameter is required") 65 } 66 67 // Validate and set defaults for sort 68 if req.Sort == "" { 69 req.Sort = "hot" 70 } 71 validSorts := map[string]bool{"hot": true, "top": true, "new": true} 72 if !validSorts[req.Sort] { 73 return NewValidationError("sort", "sort must be one of: hot, top, new") 74 } 75 76 // Validate and set defaults for limit 77 if req.Limit <= 0 { 78 req.Limit = 15 79 } 80 if req.Limit > 50 { 81 return NewValidationError("limit", "limit must not exceed 50") 82 } 83 84 // Validate and set defaults for timeframe (only used with top sort) 85 if req.Sort == "top" && req.Timeframe == "" { 86 req.Timeframe = "day" 87 } 88 validTimeframes := map[string]bool{ 89 "hour": true, "day": true, "week": true, 90 "month": true, "year": true, "all": true, 91 } 92 if req.Timeframe != "" && !validTimeframes[req.Timeframe] { 93 return NewValidationError("timeframe", "timeframe must be one of: hour, day, week, month, year, all") 94 } 95 96 return nil 97}