11import 'package:core/core.dart' ;
22import 'package:data_repository/data_repository.dart' ;
3+ import 'package:flutter_news_app_api_server_full_source_code/src/helpers/set_equality_helper.dart' ;
34import 'package:flutter_news_app_api_server_full_source_code/src/services/user_preference_limit_service.dart' ;
45import 'package:logging/logging.dart' ;
56
@@ -22,95 +23,15 @@ class DefaultUserPreferenceLimitService implements UserPreferenceLimitService {
2223 // Assuming a fixed ID for the RemoteConfig document
2324 static const String _remoteConfigId = kRemoteConfigId;
2425
25- @override
26- Future <void > checkInterestLimits ({
27- required User user,
28- required Interest interest,
29- required List <Interest > existingInterests,
30- }) async {
31- _log.info ('Checking interest limits for user ${user .id }.' );
32- final remoteConfig = await _remoteConfigRepository.read (
33- id: _remoteConfigId,
34- );
35- final limits = remoteConfig.interestConfig.limits[user.appRole];
36-
37- if (limits == null ) {
38- _log.severe (
39- 'Interest limits not found for role ${user .appRole }. '
40- 'Denying request by default.' ,
41- );
42- throw const ForbiddenException ('Interest limits are not configured.' );
43- }
44-
45- // 1. Check total number of interests.
46- final newTotal = existingInterests.length + 1 ;
47- if (newTotal > limits.total) {
48- _log.warning (
49- 'User ${user .id } exceeded total interest limit: '
50- '${limits .total } (attempted $newTotal ).' ,
51- );
52- throw ForbiddenException (
53- 'You have reached your limit of ${limits .total } saved interests.' ,
54- );
55- }
56-
57- // 2. Check total number of pinned feed filters.
58- if (interest.isPinnedFeedFilter) {
59- final pinnedCount =
60- existingInterests.where ((i) => i.isPinnedFeedFilter).length + 1 ;
61- if (pinnedCount > limits.pinnedFeedFilters) {
62- _log.warning (
63- 'User ${user .id } exceeded pinned feed filter limit: '
64- '${limits .pinnedFeedFilters } (attempted $pinnedCount ).' ,
65- );
66- throw ForbiddenException (
67- 'You have reached your limit of ${limits .pinnedFeedFilters } '
68- 'pinned feed filters.' ,
69- );
70- }
71- }
72-
73- // 3. Check notification subscription limits for each type.
74- for (final deliveryType in interest.deliveryTypes) {
75- final notificationLimit = limits.notifications[deliveryType];
76- if (notificationLimit == null ) {
77- _log.severe (
78- 'Notification limit for type ${deliveryType .name } not found for '
79- 'role ${user .appRole }. Denying request by default.' ,
80- );
81- throw ForbiddenException (
82- 'Notification limits for ${deliveryType .name } are not configured.' ,
83- );
84- }
85-
86- final subscriptionCount =
87- existingInterests
88- .where ((i) => i.deliveryTypes.contains (deliveryType))
89- .length +
90- 1 ;
91-
92- if (subscriptionCount > notificationLimit) {
93- _log.warning (
94- 'User ${user .id } exceeded notification limit for '
95- '${deliveryType .name }: $notificationLimit '
96- '(attempted $subscriptionCount ).' ,
97- );
98- throw ForbiddenException (
99- 'You have reached your limit of $notificationLimit '
100- '${deliveryType .name } notification subscriptions.' ,
101- );
102- }
103- }
104-
105- _log.info ('Interest limits check passed for user ${user .id }.' );
106- }
107-
10826 @override
10927 Future <void > checkUserContentPreferencesLimits ({
11028 required User user,
11129 required UserContentPreferences updatedPreferences,
30+ required UserContentPreferences currentPreferences,
11231 }) async {
113- _log.info ('Checking user content preferences limits for user ${user .id }.' );
32+ _log.info (
33+ 'Checking all user content preferences limits for user ${user .id }.' ,
34+ );
11435 final remoteConfig = await _remoteConfigRepository.read (
11536 id: _remoteConfigId,
11637 );
@@ -121,7 +42,7 @@ class DefaultUserPreferenceLimitService implements UserPreferenceLimitService {
12142 limits,
12243 );
12344
124- // Check followed countries
45+ // --- 1. Check general preference limits ---
12546 if (updatedPreferences.followedCountries.length > followedItemsLimit) {
12647 _log.warning (
12748 'User ${user .id } exceeded followed countries limit: '
@@ -133,7 +54,6 @@ class DefaultUserPreferenceLimitService implements UserPreferenceLimitService {
13354 );
13455 }
13556
136- // Check followed sources
13757 if (updatedPreferences.followedSources.length > followedItemsLimit) {
13858 _log.warning (
13959 'User ${user .id } exceeded followed sources limit: '
@@ -145,7 +65,6 @@ class DefaultUserPreferenceLimitService implements UserPreferenceLimitService {
14565 );
14666 }
14767
148- // Check followed topics
14968 if (updatedPreferences.followedTopics.length > followedItemsLimit) {
15069 _log.warning (
15170 'User ${user .id } exceeded followed topics limit: '
@@ -157,7 +76,6 @@ class DefaultUserPreferenceLimitService implements UserPreferenceLimitService {
15776 );
15877 }
15978
160- // Check saved headlines
16179 if (updatedPreferences.savedHeadlines.length > savedHeadlinesLimit) {
16280 _log.warning (
16381 'User ${user .id } exceeded saved headlines limit: '
@@ -169,8 +87,104 @@ class DefaultUserPreferenceLimitService implements UserPreferenceLimitService {
16987 );
17088 }
17189
90+ // --- 2. Check interest-specific limits ---
91+ final interestLimits = remoteConfig.interestConfig.limits[user.appRole];
92+ if (interestLimits == null ) {
93+ _log.severe (
94+ 'Interest limits not found for role ${user .appRole }. '
95+ 'Denying request by default.' ,
96+ );
97+ throw const ForbiddenException ('Interest limits are not configured.' );
98+ }
99+
100+ // Check total number of interests.
101+ if (updatedPreferences.interests.length > interestLimits.total) {
102+ _log.warning (
103+ 'User ${user .id } exceeded total interest limit: '
104+ '${interestLimits .total } (attempted '
105+ '${updatedPreferences .interests .length }).' ,
106+ );
107+ throw ForbiddenException (
108+ 'You have reached your limit of ${interestLimits .total } saved interests.' ,
109+ );
110+ }
111+
112+ // Find the interest that was added or updated to check its specific limits.
113+ // This logic assumes only one interest is added or updated per request.
114+ final currentInterestIds = currentPreferences.interests
115+ .map ((i) => i.id)
116+ .toSet ();
117+ Interest ? changedInterest;
118+
119+ for (final updatedInterest in updatedPreferences.interests) {
120+ if (! currentInterestIds.contains (updatedInterest.id)) {
121+ // This is a newly added interest.
122+ changedInterest = updatedInterest;
123+ break ;
124+ } else {
125+ // This is a potentially updated interest. Find the original.
126+ final originalInterest = currentPreferences.interests.firstWhere (
127+ (i) => i.id == updatedInterest.id,
128+ );
129+ if (updatedInterest != originalInterest) {
130+ changedInterest = updatedInterest;
131+ break ;
132+ }
133+ }
134+ }
135+
136+ // If an interest was added or updated, check its specific limits.
137+ if (changedInterest != null ) {
138+ _log.info ('Checking limits for changed interest: ${changedInterest .id }' );
139+
140+ // Check total number of pinned feed filters.
141+ final pinnedCount = updatedPreferences.interests
142+ .where ((i) => i.isPinnedFeedFilter)
143+ .length;
144+ if (pinnedCount > interestLimits.pinnedFeedFilters) {
145+ _log.warning (
146+ 'User ${user .id } exceeded pinned feed filter limit: '
147+ '${interestLimits .pinnedFeedFilters } (attempted $pinnedCount ).' ,
148+ );
149+ throw ForbiddenException (
150+ 'You have reached your limit of ${interestLimits .pinnedFeedFilters } '
151+ 'pinned feed filters.' ,
152+ );
153+ }
154+
155+ // Check notification subscription limits for each type.
156+ for (final deliveryType in changedInterest.deliveryTypes) {
157+ final notificationLimit = interestLimits.notifications[deliveryType];
158+ if (notificationLimit == null ) {
159+ _log.severe (
160+ 'Notification limit for type ${deliveryType .name } not found for '
161+ 'role ${user .appRole }. Denying request by default.' ,
162+ );
163+ throw ForbiddenException (
164+ 'Notification limits for ${deliveryType .name } are not configured.' ,
165+ );
166+ }
167+
168+ final subscriptionCount = updatedPreferences.interests
169+ .where ((i) => i.deliveryTypes.contains (deliveryType))
170+ .length;
171+
172+ if (subscriptionCount > notificationLimit) {
173+ _log.warning (
174+ 'User ${user .id } exceeded notification limit for '
175+ '${deliveryType .name }: $notificationLimit '
176+ '(attempted $subscriptionCount ).' ,
177+ );
178+ throw ForbiddenException (
179+ 'You have reached your limit of $notificationLimit '
180+ '${deliveryType .name } notification subscriptions.' ,
181+ );
182+ }
183+ }
184+ }
185+
172186 _log.info (
173- 'User content preferences limits check passed for user ${user .id }.' ,
187+ 'All user content preferences limits check passed for user ${user .id }.' ,
174188 );
175189 }
176190
0 commit comments