Smart vs. Manual Collections
In etchblok-test-api, collections provide a way to group bookmarks either through explicit user action or automated rules. This distinction is managed by the CollectionType enumeration and implemented within the Collection class.
Collection Types
The CollectionType enum in app/models/collection.py defines the two operational modes for any collection:
class CollectionType(Enum):
"""The kind of collection."""
MANUAL = "manual"
SMART = "smart"
- Manual Collections: Act as standard folders or lists where the user explicitly adds or removes bookmarks.
- Smart Collections: Act as dynamic views that conceptually include bookmarks based on a defined
filter_rule.
Manual Collections
Manual collections rely on the bookmark_ids attribute, which is an ordered list of strings. Membership is managed via the add_bookmark and remove_bookmark methods.
When adding a bookmark, etchblok-test-api ensures that the collection is not a smart collection and that the bookmark is not already present:
def add_bookmark(self, bookmark_id: str) -> bool:
"""Add a bookmark to a manual collection."""
if self.is_smart or bookmark_id in self.bookmark_ids:
return False
self.bookmark_ids.append(bookmark_id)
return True
Manual collections also support explicit reordering of their contents via the reorder method, provided the new list contains the exact same set of IDs currently in the collection.
Smart Collections and Filter Rules
Smart collections are defined by a filter_rule string. This rule is used to automatically determine which bookmarks belong to the collection based on their content.
Filtering Logic
The filtering logic is implemented in the internal _apply_filter method. It performs a case-insensitive keyword match against both the title and description of a bookmark:
def _apply_filter(self, bookmarks: list) -> List[str]:
"""Evaluate the filter_rule against a list of bookmarks."""
if not self.filter_rule:
return []
keyword = self.filter_rule.lower()
return [b.id for b in bookmarks if keyword in b.title.lower() or keyword in b.description.lower()]
Design Constraints
A key design choice in etchblok-test-api is the strict separation of membership logic:
- No Manual Overrides: The
add_bookmarkmethod explicitly rejects additions to smart collections. Ifis_smartis true, the method returnsFalseimmediately. - Internal Automation: While the
Collectionmodel defines how to filter bookmarks, the_apply_filtermethod is currently marked as internal. In the current version of theBookmarkService, smart collections are created and persisted, but the service layer does not yet automatically invoke_apply_filterto populatebookmark_idsdynamically during retrieval.
API Integration
Collections are created via the BookmarkService.create_collection method, which accepts a dictionary typically sourced from a JSON request body in app/routes/collections.py.
Creating a Collection
To create a collection, the API expects a name and an optional type. If the type is smart, a filter_rule should also be provided.
# Example of creating a smart collection via the service layer
data = {
"name": "Python Resources",
"type": "smart",
"filter_rule": "python"
}
collection, error = bookmark_service.create_collection(data)
Adding Bookmarks
For manual collections, the BookmarkService.add_to_collection method facilitates membership:
def add_to_collection(self, collection_id: str, bookmark_id: str) -> bool:
"""Add a bookmark to a collection."""
collection = self._repo.get_collection(collection_id)
if not collection:
return False
if not collection.add_bookmark(bookmark_id):
return False
self._repo.save_collection(collection)
return True
This service method respects the underlying model's restriction: if a developer attempts to call add_to_collection on a smart collection, the collection.add_bookmark call will return False, and the operation will fail, maintaining the integrity of the rule-based system.