Browse Source

feat(filter): merge user and feed entry filter rules

Frédéric Guillot 9 months ago
parent
commit
9c05c3c493
2 changed files with 34 additions and 14 deletions
  1. 24 14
      internal/reader/filter/filter.go
  2. 10 0
      internal/reader/filter/filter_test.go

+ 24 - 14
internal/reader/filter/filter.go

@@ -21,14 +21,9 @@ const (
 )
 
 func IsBlockedEntry(feed *model.Feed, entry *model.Entry, user *model.User) bool {
-	if user.BlockFilterEntryRules != "" {
-		if matchesEntryFilterRules(user.BlockFilterEntryRules, entry, feed, filterActionBlock) {
-			return true
-		}
-	}
-
-	if feed.BlockFilterEntryRules != "" {
-		if matchesEntryFilterRules(feed.BlockFilterEntryRules, entry, feed, filterActionBlock) {
+	combinedRules := combineFilterRules(user.BlockFilterEntryRules, feed.BlockFilterEntryRules)
+	if combinedRules != "" {
+		if matchesEntryFilterRules(combinedRules, entry, feed, filterActionBlock) {
 			return true
 		}
 	}
@@ -41,12 +36,9 @@ func IsBlockedEntry(feed *model.Feed, entry *model.Entry, user *model.User) bool
 }
 
 func IsAllowedEntry(feed *model.Feed, entry *model.Entry, user *model.User) bool {
-	if user.KeepFilterEntryRules != "" {
-		return matchesEntryFilterRules(user.KeepFilterEntryRules, entry, feed, filterActionAllow)
-	}
-
-	if feed.KeepFilterEntryRules != "" {
-		return matchesEntryFilterRules(feed.KeepFilterEntryRules, entry, feed, filterActionAllow)
+	combinedRules := combineFilterRules(user.KeepFilterEntryRules, feed.KeepFilterEntryRules)
+	if combinedRules != "" {
+		return matchesEntryFilterRules(combinedRules, entry, feed, filterActionAllow)
 	}
 
 	if feed.KeeplistRules == "" {
@@ -56,6 +48,24 @@ func IsAllowedEntry(feed *model.Feed, entry *model.Entry, user *model.User) bool
 	return matchesEntryRegexRules(feed.KeeplistRules, entry, feed, filterActionAllow)
 }
 
+func combineFilterRules(userRules, feedRules string) string {
+	var combinedRules strings.Builder
+
+	userRules = strings.TrimSpace(userRules)
+	feedRules = strings.TrimSpace(feedRules)
+
+	if userRules != "" {
+		combinedRules.WriteString(userRules)
+	}
+	if feedRules != "" {
+		if combinedRules.Len() > 0 {
+			combinedRules.WriteString("\n")
+		}
+		combinedRules.WriteString(feedRules)
+	}
+	return combinedRules.String()
+}
+
 func matchesEntryFilterRules(rules string, entry *model.Entry, feed *model.Feed, filterAction filterActionType) bool {
 	for rule := range strings.SplitSeq(rules, "\n") {
 		if matchesRule(rule, entry) {

+ 10 - 0
internal/reader/filter/filter_test.go

@@ -42,6 +42,11 @@ func TestBlockingEntries(t *testing.T) {
 		{&model.Feed{ID: 1}, &model.Entry{Date: time.Date(2024, 3, 14, 0, 0, 0, 0, time.UTC)}, &model.User{BlockFilterEntryRules: "EntryDate=before:2024-03-15"}, true},
 		{&model.Feed{ID: 1}, &model.Entry{Date: time.Date(2024, 3, 14, 0, 0, 0, 0, time.UTC)}, &model.User{BlockFilterEntryRules: "UnknownRuleType=test"}, false},
 		{&model.Feed{ID: 1, BlockFilterEntryRules: "EntryURL=(?i)example\nEntryTitle=(?i)Test"}, &model.Entry{URL: "https://example.com", Title: "Some Example"}, &model.User{}, true},
+		// Test cases for merged user and feed BlockFilterEntryRules
+		{&model.Feed{ID: 1, BlockFilterEntryRules: "EntryURL=(?i)website"}, &model.Entry{URL: "https://example.com", Title: "Some Title"}, &model.User{BlockFilterEntryRules: "   EntryTitle=(?i)title   "}, true}, // User rule matches
+		{&model.Feed{ID: 1, BlockFilterEntryRules: "EntryURL=(?i)example"}, &model.Entry{URL: "https://example.com", Title: "Some Other"}, &model.User{BlockFilterEntryRules: "EntryTitle=(?i)title"}, true},       // Feed rule matches
+		{&model.Feed{ID: 1, BlockFilterEntryRules: "EntryURL=(?i)example"}, &model.Entry{URL: "https://different.com", Title: "Some Other"}, &model.User{BlockFilterEntryRules: "EntryTitle=(?i)title"}, false},    // Neither rule matches
+		{&model.Feed{ID: 1, BlockFilterEntryRules: "EntryURL=(?i)example"}, &model.Entry{URL: "https://example.com", Title: "Some Title"}, &model.User{BlockFilterEntryRules: "EntryTitle=(?i)title"}, true},       // Both rules would match
 	}
 
 	for _, tc := range scenarios {
@@ -95,6 +100,11 @@ func TestAllowEntries(t *testing.T) {
 		{&model.Feed{ID: 1}, &model.Entry{Date: time.Date(2024, 2, 28, 0, 0, 0, 0, time.UTC)}, &model.User{KeepFilterEntryRules: "EntryDate=abcd"}, false},                            // no colon in rule value
 		{&model.Feed{ID: 1}, &model.Entry{Date: time.Date(2024, 2, 28, 0, 0, 0, 0, time.UTC)}, &model.User{KeepFilterEntryRules: "EntryDate=unknown:2024-03-15"}, false},              // unknown rule type
 		{&model.Feed{ID: 1, KeepFilterEntryRules: "EntryURL=(?i)example\nEntryTitle=(?i)Test"}, &model.Entry{URL: "https://example.com", Title: "Some Example"}, &model.User{}, true},
+		// Test cases for merged user and feed KeepFilterEntryRules
+		{&model.Feed{ID: 1, KeepFilterEntryRules: "EntryURL=(?i)website"}, &model.Entry{URL: "https://example.com", Title: "Some Title"}, &model.User{KeepFilterEntryRules: "EntryTitle=(?i)title"}, true},    // User rule matches
+		{&model.Feed{ID: 1, KeepFilterEntryRules: "EntryURL=(?i)example"}, &model.Entry{URL: "https://example.com", Title: "Some Other"}, &model.User{KeepFilterEntryRules: "EntryTitle=(?i)title"}, true},    // Feed rule matches
+		{&model.Feed{ID: 1, KeepFilterEntryRules: "EntryURL=(?i)example"}, &model.Entry{URL: "https://different.com", Title: "Some Other"}, &model.User{KeepFilterEntryRules: "EntryTitle=(?i)title"}, false}, // Neither rule matches
+		{&model.Feed{ID: 1, KeepFilterEntryRules: "EntryURL=(?i)example"}, &model.Entry{URL: "https://example.com", Title: "Some Title"}, &model.User{KeepFilterEntryRules: "EntryTitle=(?i)title"}, true},    // Both rules would match
 	}
 
 	for _, tc := range scenarios {