"""Update post use case. This module implements the use case for updating blog posts. Supports partial updates and includes authorization checks. """ from uuid import UUID from app.application.dtos.post import PostResponseDTO, UpdatePostDTO 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.value_objects import Content, Title class UpdatePostUseCase: """Use case for updating a blog post. Handles post updates with authorization checks. Supports partial updates - only provided fields are changed. Users can only update posts they authored. Attributes: _post_repo: Repository for post data access. _tx_manager: Transaction manager for commit control. Example: >>> use_case = UpdatePostUseCase(post_repo, tx_manager) >>> dto = UpdatePostDTO(title="New Title") >>> result = await use_case.execute(post_id, dto, 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 execute( self, post_id: UUID, dto: UpdatePostDTO, current_user_id: str, ) -> PostResponseDTO: """Execute the use case to update a post. Args: post_id: Unique identifier of the post to update. dto: Data transfer object with update data. current_user_id: ID of the user requesting update. Returns: PostResponseDTO with updated post data. Raises: NotFoundException: If post with given ID does not exist. ForbiddenException: If user is not the post author. """ post = await self._post_repo.get_by_id(post_id) if not post: raise NotFoundException(f"Post with id '{post_id}' not found") if post.author_id != current_user_id: raise ForbiddenException("You can only update your own posts") if dto.title is not None: post.update_title(Title(dto.title)) if dto.content is not None: post.update_content(Content(dto.content)) if dto.tags is not None: for tag in post.tags[:]: post.remove_tag(tag) for tag in dto.tags: post.add_tag(tag) 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, )