Source code for pynenc_mongo.util.mongo_collections

# mongo_collection_specs.py
"""
Collection specifications for all MongoDB-based Pynenc components.
Centralized location for all collection definitions and indexes.
"""

import hashlib
import re
from dataclasses import dataclass, field

from pymongo import IndexModel

from pynenc_mongo.conf.config_mongo import ConfigMongo
from pynenc_mongo.util.mongo_client import PynencMongoClient, RetryableCollection


[docs] def sanitize_collection_prefix(app_id: str, max_prefix_len: int = 40) -> str: """ Sanitize an app_id for safe use as a MongoDB collection name prefix. Replaces any character that is not alphanumeric or underscore with an underscore, prepends an underscore if the result starts with a digit, then truncates to fit within ``max_prefix_len`` (including an 8-character deterministic hash suffix). The hash is always computed from the full original app_id, so different app_ids that truncate to the same string still produce distinct prefixes. :param str app_id: The application identifier to sanitize :param int max_prefix_len: Maximum length of the returned prefix (default 40) :return: A string safe for use in MongoDB collection names """ sanitized = re.sub(r"[^a-zA-Z0-9_]", "_", app_id) if sanitized and sanitized[0].isdigit(): sanitized = f"_{sanitized}" sanitized = sanitized or "_default" hash_suffix = hashlib.sha256(app_id.encode()).hexdigest()[:8] # Reserve space for underscore + hash_suffix max_name_len = max_prefix_len - len(hash_suffix) - 1 sanitized = sanitized[:max_name_len] return f"{sanitized}_{hash_suffix}"
[docs] @dataclass class CollectionSpec: """Specification for a collection with its indexes""" name: str indexes: list[IndexModel] = field(default_factory=list)
[docs] class MongoCollections: """Abstract base class for MongoDB collections with app_id-based prefix enforcement.""" def __init__(self, conf: ConfigMongo, prefix: str, app_id: str): self.conf = conf self.prefix = prefix.rstrip("_") + "_" # Normalize component prefix self._app_prefix = sanitize_collection_prefix(app_id)
[docs] def _prefixed_name(self, base_name: str) -> str: """Return the collection name prefixed with the sanitized app_id.""" return f"{self._app_prefix}_{base_name}"
[docs] def instantiate_retriable_coll( self, spec: "CollectionSpec" ) -> "RetryableCollection": """ Instantiate a RetryableCollection for the given CollectionSpec. Prepends the sanitized app_id prefix to the collection name so that different apps sharing the same database are fully isolated. :param spec: Specification for the collection :return: RetryableCollection instance """ prefixed_spec = CollectionSpec( name=self._prefixed_name(spec.name), indexes=spec.indexes, ) client = PynencMongoClient.get_instance(self.conf) return client.get_collection(prefixed_spec)
[docs] def purge_all(self) -> None: """Purge all collections belonging to this app.""" for attr_name in dir(self): if attr_name.startswith("_"): continue col = getattr(self, attr_name) if isinstance(col, RetryableCollection): col.delete_many({})