Skip to content

Commit 64d3373

Browse files
committed
refactor(data): remove interest model and related operations
- Remove interest model from model registry - Remove interest operations from data operation registry - Update user preference handling to manage interests within UserContentPreferences - Simplify interest-related permission checks and validations
1 parent b798ffd commit 64d3373

File tree

2 files changed

+92
-103
lines changed

2 files changed

+92
-103
lines changed

lib/src/registry/data_operation_registry.dart

Lines changed: 92 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -121,8 +121,6 @@ class DataOperationRegistry {
121121
c.read<DataRepository<RemoteConfig>>().read(id: id, userId: null),
122122
'dashboard_summary': (c, id) =>
123123
c.read<DashboardSummaryService>().getSummary(),
124-
'interest': (c, id) =>
125-
c.read<DataRepository<Interest>>().read(id: id, userId: null),
126124
'in_app_notification': (c, id) => c
127125
.read<DataRepository<InAppNotification>>()
128126
.read(id: id, userId: null),
@@ -170,13 +168,6 @@ class DataOperationRegistry {
170168
.read<DataRepository<Language>>()
171169
.readAll(userId: uid, filter: f, sort: s, pagination: p),
172170
'user': (c, uid, f, s, p) => c.read<DataRepository<User>>().readAll(
173-
userId: uid,
174-
filter: f,
175-
sort: s,
176-
pagination: p,
177-
),
178-
'interest': (c, uid, f, s, p) =>
179-
c.read<DataRepository<Interest>>().readAll(
180171
userId: uid,
181172
filter: f,
182173
sort: s,
@@ -277,30 +268,6 @@ class DataOperationRegistry {
277268
userId: null,
278269
);
279270
},
280-
'interest': (context, item, uid) async {
281-
_log.info('Executing custom creator for interest.');
282-
final authenticatedUser = context.read<User>();
283-
final interestToCreate = (item as Interest).copyWith(
284-
userId: authenticatedUser.id,
285-
);
286-
287-
// 1. Fetch current user preferences to get existing interests.
288-
final preferences = await context
289-
.read<DataRepository<UserContentPreferences>>()
290-
.read(id: authenticatedUser.id);
291-
292-
// 2. Check limits before creating.
293-
await context.read<UserPreferenceLimitService>().checkInterestLimits(
294-
user: authenticatedUser,
295-
interest: interestToCreate,
296-
existingInterests: preferences.interests,
297-
);
298-
299-
// 3. Proceed with creation.
300-
return context.read<DataRepository<Interest>>().create(
301-
item: interestToCreate,
302-
);
303-
},
304271
});
305272

306273
// --- Register Item Updaters ---
@@ -419,53 +386,111 @@ class DataOperationRegistry {
419386
'Executing custom updater for user_content_preferences ID: $id.',
420387
);
421388
final authenticatedUser = context.read<User>();
422-
final preferencesToUpdate = item as UserContentPreferences;
389+
final userPreferenceLimitService =
390+
context.read<UserPreferenceLimitService>();
391+
final userContentPreferencesRepository =
392+
context.read<DataRepository<UserContentPreferences>>();
423393

424-
// 1. Check limits before updating.
425-
await context
426-
.read<UserPreferenceLimitService>()
427-
.checkUserContentPreferencesLimits(
428-
user: authenticatedUser,
429-
updatedPreferences: preferencesToUpdate,
430-
);
394+
final preferencesToUpdate = item as UserContentPreferences;
431395

432-
// 2. Proceed with update.
433-
return context.read<DataRepository<UserContentPreferences>>().update(
396+
// 1. Fetch the current state of the user's preferences.
397+
final currentPreferences = await userContentPreferencesRepository.read(
434398
id: id,
435-
item: preferencesToUpdate,
436399
);
437-
},
438-
'remote_config': (c, id, item, uid) => c
439-
.read<DataRepository<RemoteConfig>>()
440-
.update(id: id, item: item as RemoteConfig, userId: uid),
441-
'interest': (context, id, item, uid) async {
442-
_log.info('Executing custom updater for interest ID: $id.');
443-
final authenticatedUser = context.read<User>();
444-
final interestToUpdate = item as Interest;
445400

446-
// 1. Fetch current user preferences to get existing interests.
447-
final preferences = await context
448-
.read<DataRepository<UserContentPreferences>>()
449-
.read(id: authenticatedUser.id);
401+
// 2. Detect changes in the interests list.
402+
final currentIds =
403+
currentPreferences.interests.map((i) => i.id).toSet();
404+
final updatedIds =
405+
preferencesToUpdate.interests.map((i) => i.id).toSet();
450406

451-
// Exclude the interest being updated from the list for limit checking.
452-
final otherInterests = preferences.interests
453-
.where((i) => i.id != id)
454-
.toList();
407+
final addedIds = updatedIds.difference(currentIds);
408+
final removedIds = currentIds.difference(updatedIds);
455409

456-
// 2. Check limits before updating.
457-
await context.read<UserPreferenceLimitService>().checkInterestLimits(
410+
// For simplicity and clear validation, enforce one change at a time.
411+
if (addedIds.length + removedIds.length > 1) {
412+
throw const BadRequestException(
413+
'Only one interest can be added or removed per request.',
414+
);
415+
}
416+
417+
// 3. Perform permission and limit checks based on the detected action.
418+
if (addedIds.isNotEmpty) {
419+
// --- Interest Added ---
420+
final addedInterestId = addedIds.first;
421+
_log.info(
422+
'Detected interest addition for user ${authenticatedUser.id}.',
423+
);
424+
425+
final addedInterest = preferencesToUpdate.interests
426+
.firstWhere((i) => i.id == addedInterestId);
427+
428+
// Check business logic limits.
429+
await userPreferenceLimitService.checkInterestLimits(
430+
user: authenticatedUser,
431+
interest: addedInterest,
432+
existingInterests: currentPreferences.interests,
433+
);
434+
} else if (removedIds.isNotEmpty) {
435+
// --- Interest Removed ---
436+
_log.info(
437+
'Detected interest removal for user ${authenticatedUser.id}.',
438+
);
439+
440+
} else {
441+
// --- Interest Potentially Updated ---
442+
// Check if any existing interest was modified.
443+
Interest? updatedInterest;
444+
for (final newInterest in preferencesToUpdate.interests) {
445+
// Find the corresponding interest in the old list.
446+
final oldInterest = currentPreferences.interests.firstWhere(
447+
(i) => i.id == newInterest.id,
448+
// This should not be hit if add/remove is handled, but as a
449+
// safeguard, we use the newInterest to avoid null issues.
450+
orElse: () => newInterest,
451+
);
452+
if (newInterest != oldInterest) {
453+
updatedInterest = newInterest;
454+
break; // Found the updated one, no need to continue loop.
455+
}
456+
}
457+
458+
if (updatedInterest != null) {
459+
_log.info(
460+
'Detected interest update for user ${authenticatedUser.id}.',
461+
);
462+
463+
// Check business logic limits.
464+
final otherInterests = currentPreferences.interests
465+
.where((i) => i.id != updatedInterest!.id)
466+
.toList();
467+
await userPreferenceLimitService.checkInterestLimits(
468+
user: authenticatedUser,
469+
interest: updatedInterest,
470+
existingInterests: otherInterests,
471+
);
472+
}
473+
}
474+
475+
// 4. Always validate general preference limits (followed items, etc.).
476+
await userPreferenceLimitService.checkUserContentPreferencesLimits(
458477
user: authenticatedUser,
459-
interest: interestToUpdate,
460-
existingInterests: otherInterests,
478+
updatedPreferences: preferencesToUpdate,
461479
);
462480

463-
// 3. Proceed with update.
464-
return context.read<DataRepository<Interest>>().update(
481+
// 5. If all checks pass, proceed with the update.
482+
_log.info(
483+
'All preference validations passed for user ${authenticatedUser.id}. '
484+
'Proceeding with update.',
485+
);
486+
return userContentPreferencesRepository.update(
465487
id: id,
466-
item: interestToUpdate,
488+
item: preferencesToUpdate,
467489
);
468490
},
491+
'remote_config': (c, id, item, uid) => c
492+
.read<DataRepository<RemoteConfig>>()
493+
.update(id: id, item: item as RemoteConfig, userId: uid),
469494
});
470495

471496
// --- Register Item Deleters ---
@@ -490,8 +515,6 @@ class DataOperationRegistry {
490515
'push_notification_device': (c, id, uid) => c
491516
.read<DataRepository<PushNotificationDevice>>()
492517
.delete(id: id, userId: uid),
493-
'interest': (c, id, uid) =>
494-
c.read<DataRepository<Interest>>().delete(id: id, userId: uid),
495518
'in_app_notification': (c, id, uid) => c
496519
.read<DataRepository<InAppNotification>>()
497520
.delete(id: id, userId: uid),

lib/src/registry/model_registry.dart

Lines changed: 0 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -463,40 +463,6 @@ final modelRegistry = <String, ModelConfig<dynamic>>{
463463
requiresOwnershipCheck: true,
464464
),
465465
),
466-
'interest': ModelConfig<Interest>(
467-
fromJson: Interest.fromJson,
468-
getId: (i) => i.id,
469-
getOwnerId: (dynamic item) => (item as Interest).userId,
470-
// Collection GET is admin-only to prevent listing all users' interests.
471-
getCollectionPermission: const ModelActionPermission(
472-
type: RequiredPermissionType.adminOnly,
473-
),
474-
// Item GET is unsupported. Interests are managed as part of the
475-
// UserContentPreferences object, not as individual top-level documents.
476-
getItemPermission: const ModelActionPermission(
477-
type: RequiredPermissionType.unsupported,
478-
),
479-
// POST is allowed for any authenticated user to create their own interest.
480-
// A custom creator in DataOperationRegistry will enforce role-based limits.
481-
postPermission: const ModelActionPermission(
482-
type: RequiredPermissionType.specificPermission,
483-
permission: Permissions.interestCreateOwned,
484-
requiresOwnershipCheck: false,
485-
),
486-
// PUT is allowed for any authenticated user to update their own interest.
487-
// A custom updater in DataOperationRegistry will enforce role-based limits.
488-
putPermission: const ModelActionPermission(
489-
type: RequiredPermissionType.specificPermission,
490-
permission: Permissions.interestUpdateOwned,
491-
requiresOwnershipCheck: true,
492-
),
493-
// DELETE is allowed for any authenticated user to delete their own interest.
494-
deletePermission: const ModelActionPermission(
495-
type: RequiredPermissionType.specificPermission,
496-
permission: Permissions.interestDeleteOwned,
497-
requiresOwnershipCheck: true,
498-
),
499-
),
500466
'in_app_notification': ModelConfig<InAppNotification>(
501467
fromJson: InAppNotification.fromJson,
502468
getId: (n) => n.id,

0 commit comments

Comments
 (0)