"""Publish post use case. This module implements the use case for publishing and unpublishing blog posts. Includes authorization checks to ensure users can only manage their own posts. """ from uuid import UUID from app.application.dtos.post import PostResponseDTO from app.application.interfaces import TransactionManager from app.domain.entities import Post from app.domain.exceptions import ForbiddenException, NotFoundException from app.domain.repositories import PostRepository from app.domain.roles import Role class PublishPostUseCase: """Use case for publishing/unpublishing a blog post. Handles post publication state changes with authorization checks. Users can only publish or unpublish posts they authored. Attributes: _post_repo: Repository for post data access. _tx_manager: Transaction manager for commit control. Example: >>> use_case = PublishPostUseCase(post_repo, tx_manager) >>> post = await use_case.publish(post_id, user_id) """ def __init__( self, post_repo: PostRepository, tx_manager: TransactionManager, ) -> None: """Initialize use case with dependencies. Args: post_repo: Repository for post operations. tx_manager: Transaction manager instance. """ self._post_repo = post_repo self._tx_manager = tx_manager async def publish( self, post_id: UUID, current_user_id: str, current_role: Role = Role.USER, ) -> PostResponseDTO: """Publish a post. Args: post_id: Unique identifier of the post. current_user_id: ID of the user requesting publication. current_role: Role of the requesting user (default USER). Returns: PostResponseDTO with updated post data. Raises: NotFoundException: If post with given ID does not exist. ForbiddenException: If user is not the post author and not admin. """ post = await self._post_repo.get_by_id(post_id) if not post: raise NotFoundException(f"Post with id '{post_id}' not found") if current_role != Role.ADMIN and post.author_id != current_user_id: raise ForbiddenException("You can only publish your own posts") post.publish() await self._post_repo.update(post) await self._tx_manager.commit() return self._map_to_dto(post) async def unpublish( self, post_id: UUID, current_user_id: str, current_role: Role = Role.USER, ) -> PostResponseDTO: """Unpublish a post. Args: post_id: Unique identifier of the post. current_user_id: ID of the user requesting unpublish. current_role: Role of the requesting user (default USER). Returns: PostResponseDTO with updated post data. Raises: NotFoundException: If post with given ID does not exist. ForbiddenException: If user is not the post author and not admin. """ post = await self._post_repo.get_by_id(post_id) if not post: raise NotFoundException(f"Post with id '{post_id}' not found") if current_role != Role.ADMIN and post.author_id != current_user_id: raise ForbiddenException("You can only unpublish your own posts") post.unpublish() await self._post_repo.update(post) await self._tx_manager.commit() return self._map_to_dto(post) def _map_to_dto(self, post: Post) -> PostResponseDTO: """Map domain entity to response DTO. Args: post: Domain post entity. Returns: PostResponseDTO with all post attributes. """ return PostResponseDTO( id=post.id, title=post.title.value, content=post.content.value, slug=post.slug.value, author_id=post.author_id, published=post.published, tags=post.tags.copy(), created_at=post.created_at, updated_at=post.updated_at, )