Changeset c296f76 in flexoentity
- Timestamp:
- 12/03/25 16:24:38 (6 weeks ago)
- Branches:
- master
- Children:
- 4459fa4
- Parents:
- 4f91fed
- Files:
-
- 7 added
- 5 edited
-
flexoentity/__init__.py (modified) (2 diffs)
-
flexoentity/composite_backend.py (added)
-
flexoentity/domain_manager.py (modified) (3 diffs)
-
flexoentity/entity_manager.py (added)
-
flexoentity/flexo_collection.py (modified) (2 diffs)
-
flexoentity/in_memory_backend.py (added)
-
flexoentity/json_file_backend.py (added)
-
flexoentity/persistance_backend.py (added)
-
flexoentity/sqlite_entity_backend.py (added)
-
flexoentity/typed_collection.py (added)
-
tests/conftest.py (modified) (2 diffs)
-
tests/test_domain.py (modified) (2 diffs)
Legend:
- Unmodified
- Added
- Removed
-
flexoentity/__init__.py
r4f91fed rc296f76 14 14 from .flexo_entity import FlexoEntity, EntityType, EntityState 15 15 from .flexo_collection import FlexoCollection 16 from .typed_collection import TypedCollection 17 from .in_memory_backend import InMemoryBackend 18 from .composite_backend import CompositeBackend 19 from .entity_manager import EntityManager 20 from .sqlite_entity_backend import SQLiteEntityBackend 16 21 from .domain import Domain 17 22 from .domain_manager import DomainManager, DuplicateDomainError … … 26 31 "EntityType", 27 32 "Domain", 33 "EntityManager", 34 "SQLiteEntityBackend", 35 "InMemoryBackend", 36 "CompositeBackend", 28 37 "DomainManager", 29 38 "DuplicateDomainError", 30 39 "EntityState", 31 40 "FlexoCollection", 41 "TypedCollection", 32 42 "FlexoSignature", 33 43 "EntityRegistry", -
flexoentity/domain_manager.py
r4f91fed rc296f76 1 # flexoentity/domain_manager.py2 3 1 from __future__ import annotations 2 import json 4 3 5 4 from typing import Iterable … … 8 7 from .domain import Domain 9 8 from .flexo_entity import EntityState 9 from .entity_manager import EntityManager 10 10 11 11 … … 22 22 23 23 24 class DomainManager :24 class DomainManager(EntityManager): 25 25 """ 26 Manager for Domain entities , implemented using composition.26 Manager for Domain entities using the new backend architecture. 27 27 28 - Uses FlexoCollection internally. 29 - Domains are keyed by domain.domain_id (explicit override). 30 - No inheritance from FlexoCollection. 31 - Responsible ONLY for domain-specific rules. 28 - Primary storage: backend.load_all() (InMemory, SQLite, JSON, ...) 29 - Secondary index: domain_id → Domain 30 - Domain-specific business rules included. 32 31 """ 33 32 34 def __init__(self, registry): 35 # store is a general-purpose collection 36 self._store = FlexoCollection() 33 ENTITY_CLASS = Domain 34 35 def __init__(self, backend, registry=None): 36 super().__init__(backend) 37 37 self._registry = registry 38 self._index_by_domain_id = {} 38 39 39 # --------------------------------------------------------------- 40 # Core API 41 # --------------------------------------------------------------- 40 self._rebuild_index() 42 41 43 def add(self, domain: Domain) -> None: 44 """ 45 Add a domain using domain.domain_id as key. 46 """ 47 if not isinstance(domain, Domain): 48 raise TypeError( 49 f"DomainManager accepts only Domain instances, got {type(domain)}" 50 ) 51 if domain.domain_id in self._store: 52 raise DuplicateDomainError(f"Domain '{domain.domain_id} already exists.") 53 self._store.add(domain, key=domain.domain_id) 42 # ------------------------------------------------------------ 43 # Index rebuild 44 # ------------------------------------------------------------ 54 45 55 def get(self, domain_id: str) -> Domain: 56 """Retrieve a domain or raise DomainNotFoundError.""" 57 if isinstance(domain_id, Domain): 58 raise TypeError("DomainManager.get() expects a domain_id string, not a Domain object.") 59 result = self._store.get(domain_id) 60 if result is None: 61 raise DomainNotFoundError(f"Domain '{domain_id}' not found.") 62 return result 46 def _rebuild_index(self): 47 """Rebuild domain_id lookup table from backend contents.""" 48 self._index_by_domain_id.clear() 49 for dom in self.backend.load_all(): 50 self._index_by_domain_id[dom.domain_id] = dom 63 51 64 def can_delete(self, domain_id): 52 # ------------------------------------------------------------ 53 # Overrides for CRUD 54 # ------------------------------------------------------------ 55 56 def add(self, domain: Domain): 57 # Domain-specific uniqueness rule 58 if domain.domain_id in self._index_by_domain_id: 59 raise DuplicateDomainError(f"Domain '{domain.domain_id}' already exists.") 60 61 super().add(domain) 62 self._index_by_domain_id[domain.domain_id] = domain 63 64 def update(self, domain: Domain): 65 super().update(domain) 66 self._index_by_domain_id[domain.domain_id] = domain 67 68 def delete(self, flexo_id: str): 69 dom = self.backend.load(flexo_id) 70 if dom is None: 71 return 72 73 domain_id = dom.domain_id 74 75 # Check domain deletion rules 76 if not self.can_delete(domain_id): 77 raise ValueError(f"Domain '{domain_id}' is still referenced.") 78 79 super().delete(flexo_id) 80 81 # Remove from index 82 self._index_by_domain_id.pop(domain_id, None) 83 84 # ------------------------------------------------------------ 85 # Domain-specific lookup 86 # ------------------------------------------------------------ 87 88 def get_by_domain_id(self, domain_id: str) -> Domain: 89 dom = self._index_by_domain_id.get(domain_id) 90 if not dom: 91 raise KeyError(f"Domain '{domain_id}' not found.") 92 return dom 93 94 def find_by_domain_id(self, domain_id: str): 95 return self._index_by_domain_id.get(domain_id) 96 97 def all_domain_ids(self): 98 return list(self._index_by_domain_id.keys()) 99 100 def all(self): 101 return list(self._index_by_domain_id.values()) 102 # ------------------------------------------------------------ 103 # Domain deletion rules 104 # ------------------------------------------------------------ 105 106 def can_delete(self, domain_id: str) -> bool: 107 if not self._registry: 108 return True 65 109 return not any(self._registry.entities_by_domain(domain_id)) 66 110 67 def delete(self, domain_id): 68 if not self.can_delete(domain_id): 69 raise ValueError(f"Domain {domain_id} is still referenced.") 70 self.remove(domain_id) 71 72 def remove(self, domain_id: str) -> None: 73 """Remove a domain (rarely used, mostly for tests).""" 74 if domain_id not in self._store: 75 raise DomainNotFoundError(domain_id) 76 self._store.remove(domain_id) 77 78 def all(self) -> list[Domain]: 79 """List of all domains.""" 80 return self._store.entities() 81 82 def all_domain_ids(self): 83 return self._store.ids() 84 85 def clear(self): 86 self._store.clear() 87 88 # --------------------------------------------------------------- 89 # Lifecycle / Approval Helpers 90 # --------------------------------------------------------------- 111 # ------------------------------------------------------------ 112 # Domain lifecycle helpers 113 # ------------------------------------------------------------ 91 114 92 115 def ensure_approved(self, domain_id: str) -> None: 93 """ 94 Used during entity.transition_to(). 95 Raises if the domain does not exist or is not APPROVED. 96 """ 97 dom = self.get(domain_id) 116 dom = self.get_by_domain_id(domain_id) 98 117 99 118 if dom.state != EntityState.APPROVED: 100 raise InvalidDomainError(119 raise ValueError( 101 120 f"Domain '{domain_id}' must be APPROVED before " 102 f"entities in this domain maybe promoted. "121 f"entities in this domain can be promoted. " 103 122 f"(Current state: {dom.state})" 104 123 ) 105 124 106 # --------------------------------------------------------------- 107 # Representations 108 # --------------------------------------------------------------- 125 def clear(self): 126 self.backend.clear() 109 127 110 def __ len__(self) -> int:111 return len(self._store)128 def __repr__(self): 129 return f"<DomainManager domains={self.all_domain_ids()}>" 112 130 113 def __contains__(self, domain_id: str) -> bool:114 return domain_id in self._store115 116 def __repr__(self) -> str:117 return f"<DomainManager domains={self._store.keys()}>" -
flexoentity/flexo_collection.py
r4f91fed rc296f76 11 11 - Backwards compatible API 12 12 """ 13 13 from abc import abstractmethod 14 14 from .flexo_entity import FlexoEntity 15 15 from .id_factory import FlexOID … … 115 115 # ------------------------------------------------------------------ 116 116 117 def to_dict_list(self):117 def serialize(self): 118 118 """Serialize all entities to list of dicts.""" 119 return [e.to_dict() for e in self. _items.values()]119 return [e.to_dict() for e in self.entities()] 120 120 121 121 @classmethod 122 def from_dict_list(cls, data): 122 @abstractmethod 123 def deserialize(cls, data): 123 124 """ 124 125 Deserialize a list of dicts into a FlexoCollection. 125 126 126 Uses FlexoEntity.from_dict() to dispatch to the correct subclass.127 Uses from_dict() to dispatch to the correct subclass. 127 128 """ 128 c = cls() 129 for d in data: 130 entity = FlexoEntity.from_dict(d) 131 c.add(entity) 132 return c 129 pass 133 130 134 131 # ------------------------------------------------------------------ -
tests/conftest.py
r4f91fed rc296f76 3 3 from pathlib import Path 4 4 from datetime import datetime 5 from flexoentity import Domain, FlexoSignature, DomainManager, EntityRegistry 5 from flexoentity import Domain, FlexoSignature, DomainManager, EntityRegistry, CompositeBackend 6 6 from flexoentity import get_signing_backend, CertificateReference 7 7 … … 30 30 @pytest.fixture 31 31 def sample_domain_manager(): 32 return DomainManager( EntityRegistry())32 return DomainManager(CompositeBackend(Domain), EntityRegistry()) 33 33 34 34 # ───────────────────────────────────────────────────────────── -
tests/test_domain.py
r4f91fed rc296f76 20 20 sample_domain_manager.add(sample_domain) 21 21 # Manager must know it now 22 assert sample_domain_manager.get ("PY_ARITHM") is sample_domain22 assert sample_domain_manager.get_by_domain_id("PY_ARITHM") is sample_domain 23 23 24 24 … … 38 38 def test_lookup_by_oid(sample_domain, sample_domain_manager): 39 39 sample_domain_manager.add(sample_domain) 40 found = sample_domain_manager.get (sample_domain.domain_id)40 found = sample_domain_manager.get_by_domain_id(sample_domain.domain_id) 41 41 assert found is sample_domain 42 43 42 44 43 # ---------------------------------------------------------------
Note:
See TracChangeset
for help on using the changeset viewer.
