API reference¶
Generated from the apogee-core source with mkdocstrings. Every symbol below is exported from apogee_core, so it is part of the supported public surface.
Other¶
ApiResponse
¶
success
staticmethod
¶
success(data: T) -> SuccessResponse[T]
paged
staticmethod
¶
paged(result) -> PagedResponse
error
staticmethod
¶
error(code: str, message: str) -> ErrorResponse
validation_error
staticmethod
¶
validation_error(fields: list[dict]) -> ErrorResponse
BaseController
¶
Bases: ABC
Base class for HTTP controllers. Provides common helpers.
success
staticmethod
¶
success(data: Any) -> SuccessResponse
paged
staticmethod
¶
paged(result: PagedResult) -> PagedResponse
parse_page_params
staticmethod
¶
Extract page and per_page from query params with defaults.
Source code in apogee_core/presentation/http/controllers/base_controller.py
BaseEntity
dataclass
¶
BaseEntity(id: UUID = uuid4(), created_at: datetime = (lambda: now(utc))(), updated_at: datetime = (lambda: now(utc))())
BaseMongoCommandRepository
¶
Bases: Generic[T]
Subclasses must define:
_collection_name(str)_to_document(entity: T) -> dictfor serialisation_to_entity(doc: dict) -> Tfor hydration_id_of(entity: T) -> UUIDto extract the primary key
Source code in apogee_core/infrastructure/persistence/mongo/base_mongo_command_repository.py
BaseMongoQueryRepository
¶
Bases: Generic[T]
Generic Mongo query repo. Subclasses must define:
_collection_name(str)ALLOWED_FILTER_FIELDS,ALLOWED_SORT_FIELDS,SEARCHABLE_FIELDS_to_entity(doc: dict) -> Tfor hydration
Source code in apogee_core/infrastructure/persistence/mongo/base_mongo_query_repository.py
def __init__(self, database: Any) -> None:
# ``database`` is a motor.motor_asyncio.AsyncIOMotorDatabase. Kept as
# ``Any`` to avoid an import-time dependency on motor; we only touch
# async methods that exist on motor's API.
self._db = database
self._collection = database[self._collection_name]
ALLOWED_FILTER_FIELDS
class-attribute
instance-attribute
¶
ALLOWED_SORT_FIELDS
class-attribute
instance-attribute
¶
SEARCHABLE_FIELDS
class-attribute
instance-attribute
¶
get_by_id
async
¶
count
async
¶
find_all
async
¶
find_all(query: QueryFilter | None = None) -> PagedResult[T]
Source code in apogee_core/infrastructure/persistence/mongo/base_mongo_query_repository.py
async def find_all(self, query: QueryFilter | None = None) -> PagedResult[T]:
q = query or QueryFilter()
mongo_filter = query_filter_to_mongo(q, self.ALLOWED_FILTER_FIELDS | self.SEARCHABLE_FIELDS)
total = int(await self._collection.count_documents(mongo_filter))
skip = (q.page - 1) * q.per_page
cursor = self._collection.find(mongo_filter)
sort_spec = query_sort_to_mongo(q, self.ALLOWED_SORT_FIELDS)
if sort_spec:
cursor = cursor.sort(sort_spec)
cursor = cursor.skip(skip).limit(q.per_page)
if q.fields:
projection = {field: 1 for field in q.fields if field in self.ALLOWED_FILTER_FIELDS}
if projection:
cursor = cursor.projection(projection)
docs = await cursor.to_list(length=q.per_page)
return PagedResult[T](
data=[self._to_entity(d) for d in docs],
total=total,
page=q.page,
per_page=q.per_page,
)
BaseSQLCommandRepository
¶
Bases: Generic[T, M]
BaseSQLCommandRepository — generic CRUD mixin for SQLAlchemy async.
create
async
¶
Source code in apogee_core/infrastructure/persistence/sql/base_sql_command_repository.py
update
async
¶
Source code in apogee_core/infrastructure/persistence/sql/base_sql_command_repository.py
async def update(self, entity: T) -> T:
async with self._session_factory() as session:
result = await session.execute(
select(self._model_class).where(self._model_class.id == str(entity.id))
)
model = result.scalar_one_or_none()
if not model:
raise ValueError(f"{self._model_class.__name__} not found")
for key, value in self._to_model(entity).__dict__.items():
if key.startswith("_"):
continue
setattr(model, key, value)
await session.commit()
await session.refresh(model)
return self._to_entity(model)
delete
async
¶
Source code in apogee_core/infrastructure/persistence/sql/base_sql_command_repository.py
async def delete(self, entity_id: UUID) -> bool:
async with self._session_factory() as session:
result = await session.execute(
select(self._model_class).where(self._model_class.id == str(entity_id))
)
model = result.scalar_one_or_none()
if model:
await session.delete(model)
await session.commit()
return True
return False
BaseSQLQueryRepository
¶
Bases: Generic[T, M]
BaseSQLQueryRepository — generic find_all for SQLAlchemy async.
find_all
async
¶
find_all(query: QueryFilter) -> PagedResult[T]
Source code in apogee_core/infrastructure/persistence/sql/base_sql_query_repository.py
async def find_all(self, query: QueryFilter) -> PagedResult[T]:
async with self._session_factory() as session:
stmt = self._build_select(query)
count_stmt = select(func.count()).select_from(stmt.subquery())
total = (await session.execute(count_stmt)).scalar_one()
result = await session.execute(stmt.offset(query.skip).limit(query.limit))
models = result.scalars().all()
return PagedResult(
data=[self._to_entity(m) for m in models],
total=total,
page=query.page,
per_page=query.per_page,
)
find_by_id
async
¶
Source code in apogee_core/infrastructure/persistence/sql/base_sql_query_repository.py
async def find_by_id(self, entity_id: UUID) -> Optional[T]:
async with self._session_factory() as session:
result = await session.execute(
select(self._model_class).where(self._model_class.id == str(entity_id))
)
model = result.scalar_one_or_none()
return self._to_entity(model) if model else None
count
async
¶
ErrorDetail
¶
ErrorResponse
¶
Bases: BaseModel
GenericCrudController
¶
Bases: Generic[TRequestCreate, TRequestUpdate, TCreateDTO, TUpdateDTO], BaseController
Controlador genérico que abstrai operações CRUD padrão. Subclasses devem implementar _map_create_request_to_dto e _map_update_request_to_dto.
create
async
classmethod
¶
create(request: TRequestCreate, use_case: Any) -> SuccessResponse
get
async
classmethod
¶
get(entity_id: str, use_case: Any) -> SuccessResponse
list_all
async
classmethod
¶
list_all(use_case: Any, query: QueryFilter) -> PagedResponse
update
async
classmethod
¶
update(entity_id: str, request: TRequestUpdate, use_case: Any) -> SuccessResponse
Source code in apogee_core/presentation/http/controllers/generic_controller.py
delete
async
classmethod
¶
delete(entity_id: str, use_case: Any) -> SuccessResponse
GenericCrudService
¶
GenericCrudService(command_repository: ICommandRepository[TEntity], query_repository: IQueryRepository[TEntity])
Bases: Generic[TEntity, TCreateDTO, TUpdateDTO, TOutputDTO]
Source code in apogee_core/application/services/generic_service.py
create
async
¶
get
async
¶
list
async
¶
list(query: QueryFilter) -> PagedResult[TOutputDTO]
Source code in apogee_core/application/services/generic_service.py
update
async
¶
Source code in apogee_core/application/services/generic_service.py
async def update(self, entity_id: str, dto: TUpdateDTO) -> TOutputDTO:
entity = await self.query_repository.find_by_id(UUID(entity_id))
if not entity:
raise self._not_found_exception(entity_id)
self._update_entity_from_dto(entity, dto)
entity.updated_at = datetime.now(timezone.utc)
updated = await self.command_repository.update(entity)
return self._to_output(updated)
delete
async
¶
HttpStatus
¶
Bases: IntEnum
InMemoryCacheStore
¶
PageMeta
¶
PagedResponse
¶
PagedResult
¶
Bases: BaseModel, Generic[T]
QueryFilter
¶
Bases: BaseModel
page_must_be_positive
classmethod
¶
per_page_in_range
classmethod
¶
from_params
classmethod
¶
from_params(*, page: int = 1, per_page: int = 20, search: Optional[str] = None, search_fields: tuple[str, ...] = (), sort: Optional[str] = None, fields: Optional[str] = None, filters: Optional[dict[str, Any]] = None) -> 'QueryFilter'
Source code in apogee_core/domain/value_objects/query_filter_vo.py
@classmethod
def from_params(
cls,
*,
page: int = 1,
per_page: int = 20,
search: Optional[str] = None,
search_fields: tuple[str, ...] = (),
sort: Optional[str] = None,
fields: Optional[str] = None,
filters: Optional[dict[str, Any]] = None,
) -> "QueryFilter":
return cls(
page=max(1, page),
per_page=max(1, min(per_page, 1000)),
search=search or None,
search_fields=search_fields,
sort=_parse_sort(sort) if sort else (),
fields=_parse_fields(fields) if fields else (),
filters=filters or {},
)
QueryFilterBuilder
¶
build
staticmethod
¶
build(*, page: int = 1, per_page: int = 20, q: Optional[str] = None, sort: Optional[str] = None, fields: Optional[str] = None, search_fields: frozenset[str] = frozenset(), raw_params: Optional[dict[str, Any]] = None) -> QueryFilter
Source code in apogee_core/presentation/http/helpers/query_filter_builder.py
@staticmethod
def build(
*,
page: int = 1,
per_page: int = 20,
q: Optional[str] = None,
sort: Optional[str] = None,
fields: Optional[str] = None,
search_fields: frozenset[str] = frozenset(),
raw_params: Optional[dict[str, Any]] = None,
) -> QueryFilter:
filters = _parse_bracket_filters(raw_params or {})
return QueryFilter.from_params(
page=page, per_page=per_page, search=q,
search_fields=tuple(search_fields) if q else (),
sort=sort, fields=fields, filters=filters,
)
from_fastapi_request
staticmethod
¶
from_fastapi_request(request: Any, search_fields: frozenset[str] = frozenset()) -> QueryFilter
Source code in apogee_core/presentation/http/helpers/query_filter_builder.py
@staticmethod
def from_fastapi_request(
request: Any,
search_fields: frozenset[str] = frozenset(),
) -> QueryFilter:
raw = dict(request.query_params)
q = raw.pop("q", None) or None
sort = raw.pop("sort", None) or None
fields = raw.pop("fields", None) or None
page = int(raw.pop("page", 1) or 1)
per_page = int(raw.pop("per_page", 20) or 20)
filters = _parse_bracket_filters(raw)
return QueryFilter.from_params(
page=page, per_page=per_page, search=q,
search_fields=tuple(search_fields) if q else (),
sort=sort, fields=fields, filters=filters,
)
RedisCacheStore
¶
RedisCacheStore(client: Any | None = None, url: str = 'redis://localhost:6379/0', namespace: str = '', serializer: str = 'json')
Lazy redis-py async adapter.
JSON-encodes by default; falls back to pickle when serializer="pickle".
Source code in apogee_core/infrastructure/cache/redis_cache_store.py
SqlCacheAside
¶
SqlCacheAside(*args: Any, cache: Any | None = None, cache_ttl_s: float | None = None, **kwargs: Any)
Mixin: applies cache-aside on top of a BaseSQLQueryRepository.
Subclasses pass an ICacheStore via cache=... constructor kwarg
plus a cache_ttl_s (optional). The mixin keeps a per-class prefix
derived from the model's name to avoid collisions across repos.
Source code in apogee_core/infrastructure/cache/sql_cache_aside.py
def __init__(
self,
*args: Any,
cache: Any | None = None,
cache_ttl_s: float | None = None,
**kwargs: Any,
) -> None:
super().__init__(*args, **kwargs)
self._cache = cache
if cache_ttl_s is not None:
self.cache_ttl_s = cache_ttl_s
model_name = getattr(getattr(self, "_model_class", None), "__name__", "Entity")
self._cache_prefix = f"sql:{model_name}"
get_by_id
async
¶
Source code in apogee_core/infrastructure/cache/sql_cache_aside.py
async def get_by_id(self, entity_id: UUID) -> Any:
if self._cache is None:
return await super().get_by_id(entity_id) # type: ignore[misc]
key = self._cache_key(f"id:{entity_id}")
cached = await self._cache.get(key)
if cached is not None:
return cached
value = await super().get_by_id(entity_id) # type: ignore[misc]
if value is not None:
await self._cache.set(key, value, ttl_s=self.cache_ttl_s)
return value
find_all
async
¶
Source code in apogee_core/infrastructure/cache/sql_cache_aside.py
async def find_all(self, *args: Any, **kwargs: Any) -> Any:
if self._cache is None:
return await super().find_all(*args, **kwargs) # type: ignore[misc]
key = self._cache_key("find:" + self._digest({"args": args, "kwargs": kwargs}))
cached = await self._cache.get(key)
if cached is not None:
return cached
value = await super().find_all(*args, **kwargs) # type: ignore[misc]
await self._cache.set(key, value, ttl_s=self.cache_ttl_s)
return value
invalidate
async
¶
SuccessResponse
¶
handle_domain_exception
¶
Returns (status_code, response_body) for known domain exceptions.
Source code in apogee_core/presentation/http/base_error_handlers/base_error_handler.py
def handle_domain_exception(exc: Exception) -> tuple[int, dict]:
"""Returns (status_code, response_body) for known domain exceptions."""
from apogee_core.domain.exceptions.base_exception import (
AlreadyExistsException,
DomainException,
NotFoundException,
)
if isinstance(exc, NotFoundException):
return 404, {"success": False, "error": {"code": exc.code, "message": exc.message}}
if isinstance(exc, AlreadyExistsException):
return 409, {"success": False, "error": {"code": exc.code, "message": exc.message}}
if isinstance(exc, DomainException):
return 400, {"success": False, "error": {"code": exc.code, "message": exc.message}}
logger.error(f"Unhandled exception: {exc}")
return 500, {"success": False, "error": {"code": "INTERNAL_ERROR", "message": "Internal Server Error"}}
parse_validation_errors
¶
Converts Pydantic validation errors to API-friendly format.
Source code in apogee_core/presentation/http/helpers/validation_error_parser.py
def parse_validation_errors(errors: list[dict[str, Any]]) -> list[dict[str, str]]:
"""Converts Pydantic validation errors to API-friendly format."""
result = []
for err in errors:
loc = ".".join(str(p) for p in err.get("loc", []))
result.append({
"field": loc,
"message": err.get("msg", "Invalid value"),
"type": err.get("type", "value_error"),
})
return result
Other · Enums¶
DbEngineEnum
¶
Other · Exceptions¶
AlreadyExistsException
¶
DomainException
¶
Bases: Exception
Source code in apogee_core/domain/exceptions/base_exception.py
NotFoundException
¶
Other · Protocols (ports)¶
ICacheStore
¶
IFilterableQueryRepository
¶
Bases: IQueryRepository[T], Protocol[T]
find_all
async
¶
IGenericCrudService
¶
Bases: Protocol, Generic[TCreateDTO, TUpdateDTO, TOutputDTO]
create
async
¶
get
async
¶
list
async
¶
list(query: QueryFilter) -> PagedResult[TOutputDTO]
update
async
¶
delete
async
¶
IMongoQueryRepository
¶
Bases: Protocol[T]
get_by_id
async
¶
find_all
async
¶
find_all(query: QueryFilter | None = None) -> PagedResult[T]