blog/backend/feature/post/src/adapter/delivery/post_controller.rs
SquidSpirit 71528294ae
All checks were successful
Frontend CI / build (push) Successful in 1m8s
BLOG-105 Implement CRUD functionality for Labels (#107)
### Description

This PR introduces full CRUD (Create, Read, Update) functionality for post labels, implemented by following the existing Clean Architecture.

#### Backend

* **New API Endpoints for Label Management:**
    * `POST /label`: Create a new label (**authentication required**).
    * `PUT /label/{id}`: Update a label by its ID (**authentication required**).
    * `GET /label`: Get all labels.

* **Architectural Implementation:**
    * **Delivery Layer**: Added `CreateLabelRequestDto`, `UpdateLabelRequestDto`, and updated `PostController` with methods to handle label-related operations.
    * **Application Layer**: Created corresponding use cases (`CreateLabelUseCase`, `UpdateLabelUseCase`, `GetAllLabelsUseCase`) to handle business logic.
    * **Gateway/Framework Layer**: Implemented `LabelRepository` and `LabelDbService` to manage database interactions, including creating, updating, and querying labels.

* **Route Adjustment:**
    * The route for fetching all post info has been changed from `GET /post/all` to `GET /post` to be more RESTful.

#### Frontend

* **API Call Update:**
    * To match the backend route change, the API path for fetching all posts is updated from `/post/all` to `/post`.

### Package Changes

_No response_

### Screenshots

_No response_

### Reference

Resolves #105

### Checklist

- [x] A milestone is set
- [x] The related issuse has been linked to this branch

Reviewed-on: #107
Co-authored-by: SquidSpirit <squid@squidspirit.com>
Co-committed-by: SquidSpirit <squid@squidspirit.com>
2025-08-02 10:46:00 +08:00

140 lines
4.1 KiB
Rust

use std::sync::Arc;
use async_trait::async_trait;
use crate::{
adapter::delivery::{
create_label_request_dto::CreateLabelRequestDto, post_info_query_dto::PostQueryDto,
update_label_request_dto::UpdateLabelRequestDto,
},
application::{
error::post_error::PostError,
use_case::{
create_label_use_case::CreateLabelUseCase,
get_all_labels_use_case::GetAllLabelsUseCase,
get_all_post_info_use_case::GetAllPostInfoUseCase,
get_full_post_use_case::GetFullPostUseCase, update_label_use_case::UpdateLabelUseCase,
},
},
};
use super::{
label_response_dto::LabelResponseDto, post_info_response_dto::PostInfoResponseDto,
post_response_dto::PostResponseDto,
};
#[async_trait]
pub trait PostController: Send + Sync {
async fn get_all_post_info(
&self,
query: PostQueryDto,
) -> Result<Vec<PostInfoResponseDto>, PostError>;
async fn get_post_by_id(&self, id: i32) -> Result<PostResponseDto, PostError>;
async fn create_label(
&self,
label: CreateLabelRequestDto,
) -> Result<LabelResponseDto, PostError>;
async fn update_label(
&self,
id: i32,
label: UpdateLabelRequestDto,
) -> Result<LabelResponseDto, PostError>;
async fn get_all_labels(&self) -> Result<Vec<LabelResponseDto>, PostError>;
}
pub struct PostControllerImpl {
get_all_post_info_use_case: Arc<dyn GetAllPostInfoUseCase>,
get_full_post_use_case: Arc<dyn GetFullPostUseCase>,
create_label_use_case: Arc<dyn CreateLabelUseCase>,
update_label_use_case: Arc<dyn UpdateLabelUseCase>,
get_all_labels_use_case: Arc<dyn GetAllLabelsUseCase>,
}
impl PostControllerImpl {
pub fn new(
get_all_post_info_use_case: Arc<dyn GetAllPostInfoUseCase>,
get_full_post_use_case: Arc<dyn GetFullPostUseCase>,
create_label_use_case: Arc<dyn CreateLabelUseCase>,
update_label_use_case: Arc<dyn UpdateLabelUseCase>,
get_all_labels_use_case: Arc<dyn GetAllLabelsUseCase>,
) -> Self {
Self {
get_all_post_info_use_case,
get_full_post_use_case,
create_label_use_case,
update_label_use_case,
get_all_labels_use_case,
}
}
}
#[async_trait]
impl PostController for PostControllerImpl {
async fn get_all_post_info(
&self,
query: PostQueryDto,
) -> Result<Vec<PostInfoResponseDto>, PostError> {
let result = self
.get_all_post_info_use_case
.execute(query.is_published_only.unwrap_or(true))
.await;
result.map(|post_info_list| {
let post_info_response_dto_list: Vec<PostInfoResponseDto> = post_info_list
.into_iter()
.map(|post_info| PostInfoResponseDto::from(post_info))
.collect();
post_info_response_dto_list
})
}
async fn get_post_by_id(&self, id: i32) -> Result<PostResponseDto, PostError> {
let result = self.get_full_post_use_case.execute(id).await;
result.map(PostResponseDto::from)
}
async fn create_label(
&self,
label: CreateLabelRequestDto,
) -> Result<LabelResponseDto, PostError> {
let mut label_entity = label.into_entity();
let id = self
.create_label_use_case
.execute(label_entity.clone())
.await?;
label_entity.id = id;
Ok(LabelResponseDto::from(label_entity))
}
async fn update_label(
&self,
id: i32,
label: UpdateLabelRequestDto,
) -> Result<LabelResponseDto, PostError> {
let label_entity = label.into_entity(id);
self.update_label_use_case
.execute(label_entity.clone())
.await?;
Ok(LabelResponseDto::from(label_entity))
}
async fn get_all_labels(&self) -> Result<Vec<LabelResponseDto>, PostError> {
let result = self.get_all_labels_use_case.execute().await;
result.map(|labels| {
labels
.into_iter()
.map(|label| LabelResponseDto::from(label))
.collect()
})
}
}