BLOG-105 Implement CRUD functionality for Labels #107
1
backend/Cargo.lock
generated
1
backend/Cargo.lock
generated
@ -2232,6 +2232,7 @@ version = "0.2.0"
|
||||
dependencies = [
|
||||
"actix-web",
|
||||
"async-trait",
|
||||
"auth",
|
||||
"chrono",
|
||||
"log",
|
||||
"serde",
|
||||
|
@ -11,3 +11,5 @@ log.workspace = true
|
||||
serde.workspace = true
|
||||
sqlx.workspace = true
|
||||
utoipa.workspace = true
|
||||
|
||||
auth.workspace = true
|
||||
|
@ -1,6 +1,9 @@
|
||||
pub mod color_request_dto;
|
||||
pub mod color_response_dto;
|
||||
pub mod create_label_request_dto;
|
||||
pub mod label_response_dto;
|
||||
pub mod post_controller;
|
||||
pub mod post_info_query_dto;
|
||||
pub mod post_info_response_dto;
|
||||
pub mod post_response_dto;
|
||||
pub mod update_label_request_dto;
|
||||
|
@ -0,0 +1,30 @@
|
||||
use serde::Deserialize;
|
||||
use utoipa::ToSchema;
|
||||
|
||||
use crate::domain::entity::color::Color;
|
||||
|
||||
#[derive(Deserialize, ToSchema)]
|
||||
pub struct ColorRequestDto {
|
||||
#[schema(maximum = 255)]
|
||||
pub red: u8,
|
||||
|
||||
#[schema(maximum = 255)]
|
||||
pub green: u8,
|
||||
|
||||
#[schema(maximum = 255)]
|
||||
pub blue: u8,
|
||||
|
||||
#[schema(maximum = 255)]
|
||||
pub alpha: u8,
|
||||
}
|
||||
|
||||
impl ColorRequestDto {
|
||||
pub fn into_entity(self) -> Color {
|
||||
Color {
|
||||
red: self.red,
|
||||
green: self.green,
|
||||
blue: self.blue,
|
||||
alpha: self.alpha,
|
||||
}
|
||||
}
|
||||
}
|
@ -5,9 +5,16 @@ use crate::domain::entity::color::Color;
|
||||
|
||||
#[derive(Serialize, ToSchema)]
|
||||
pub struct ColorResponseDto {
|
||||
#[schema(maximum = 255)]
|
||||
pub red: u8,
|
||||
|
||||
#[schema(maximum = 255)]
|
||||
pub green: u8,
|
||||
|
||||
#[schema(maximum = 255)]
|
||||
pub blue: u8,
|
||||
|
||||
#[schema(maximum = 255)]
|
||||
pub alpha: u8,
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,20 @@
|
||||
use serde::Deserialize;
|
||||
use utoipa::ToSchema;
|
||||
|
||||
use crate::{adapter::delivery::color_request_dto::ColorRequestDto, domain::entity::label::Label};
|
||||
|
||||
#[derive(Deserialize, ToSchema)]
|
||||
pub struct CreateLabelRequestDto {
|
||||
pub name: String,
|
||||
pub color: ColorRequestDto,
|
||||
}
|
||||
|
||||
impl CreateLabelRequestDto {
|
||||
pub fn into_entity(self) -> Label {
|
||||
Label {
|
||||
id: -1,
|
||||
name: self.name,
|
||||
color: self.color.into_entity(),
|
||||
}
|
||||
}
|
||||
}
|
@ -3,17 +3,25 @@ use std::sync::Arc;
|
||||
use async_trait::async_trait;
|
||||
|
||||
use crate::{
|
||||
adapter::delivery::post_info_query_dto::PostQueryDto,
|
||||
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,
|
||||
get_full_post_use_case::GetFullPostUseCase, update_label_use_case::UpdateLabelUseCase,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
use super::{post_info_response_dto::PostInfoResponseDto, post_response_dto::PostResponseDto};
|
||||
use super::{
|
||||
label_response_dto::LabelResponseDto, post_info_response_dto::PostInfoResponseDto,
|
||||
post_response_dto::PostResponseDto,
|
||||
};
|
||||
|
||||
#[async_trait]
|
||||
pub trait PostController: Send + Sync {
|
||||
@ -23,21 +31,43 @@ pub trait PostController: Send + Sync {
|
||||
) -> 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,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -68,4 +98,42 @@ impl PostController for PostControllerImpl {
|
||||
|
||||
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()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,20 @@
|
||||
use serde::Deserialize;
|
||||
use utoipa::ToSchema;
|
||||
|
||||
use crate::{adapter::delivery::color_request_dto::ColorRequestDto, domain::entity::label::Label};
|
||||
|
||||
#[derive(Deserialize, ToSchema)]
|
||||
pub struct UpdateLabelRequestDto {
|
||||
pub name: String,
|
||||
pub color: ColorRequestDto,
|
||||
}
|
||||
|
||||
impl UpdateLabelRequestDto {
|
||||
pub fn into_entity(self, id: i32) -> Label {
|
||||
Label {
|
||||
id,
|
||||
name: self.name,
|
||||
color: self.color.into_entity(),
|
||||
}
|
||||
}
|
||||
}
|
@ -1,5 +1,7 @@
|
||||
pub mod color_db_mapper;
|
||||
pub mod label_db_mapper;
|
||||
pub mod label_db_service;
|
||||
pub mod label_repository_impl;
|
||||
pub mod post_db_mapper;
|
||||
pub mod post_db_service;
|
||||
pub mod post_info_db_mapper;
|
||||
|
@ -14,3 +14,14 @@ impl ColorMapper {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Color> for ColorMapper {
|
||||
fn from(color: Color) -> Self {
|
||||
let value: u32 = ((color.red as u32) << 24)
|
||||
| ((color.green as u32) << 16)
|
||||
| ((color.blue as u32) << 8)
|
||||
| (color.alpha as u32);
|
||||
|
||||
Self { value }
|
||||
}
|
||||
}
|
||||
|
@ -15,3 +15,13 @@ impl LabelMapper {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Label> for LabelMapper {
|
||||
fn from(label: Label) -> Self {
|
||||
Self {
|
||||
id: label.id,
|
||||
name: label.name,
|
||||
color: ColorMapper::from(label.color),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
13
backend/feature/post/src/adapter/gateway/label_db_service.rs
Normal file
13
backend/feature/post/src/adapter/gateway/label_db_service.rs
Normal file
@ -0,0 +1,13 @@
|
||||
use async_trait::async_trait;
|
||||
|
||||
use crate::{
|
||||
adapter::gateway::label_db_mapper::LabelMapper, application::error::post_error::PostError,
|
||||
};
|
||||
|
||||
#[async_trait]
|
||||
pub trait LabelDbService: Send + Sync {
|
||||
async fn create_label(&self, label: LabelMapper) -> Result<i32, PostError>;
|
||||
async fn update_label(&self, label: LabelMapper) -> Result<(), PostError>;
|
||||
async fn get_label_by_id(&self, id: i32) -> Result<LabelMapper, PostError>;
|
||||
async fn get_all_labels(&self) -> Result<Vec<LabelMapper>, PostError>;
|
||||
}
|
@ -0,0 +1,50 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use async_trait::async_trait;
|
||||
|
||||
use crate::{
|
||||
adapter::gateway::{label_db_mapper::LabelMapper, label_db_service::LabelDbService},
|
||||
application::{error::post_error::PostError, gateway::label_repository::LabelRepository},
|
||||
domain::entity::label::Label,
|
||||
};
|
||||
|
||||
pub struct LabelRepositoryImpl {
|
||||
label_db_service: Arc<dyn LabelDbService>,
|
||||
}
|
||||
|
||||
impl LabelRepositoryImpl {
|
||||
pub fn new(label_db_service: Arc<dyn LabelDbService>) -> Self {
|
||||
Self { label_db_service }
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl LabelRepository for LabelRepositoryImpl {
|
||||
async fn create_label(&self, label: Label) -> Result<i32, PostError> {
|
||||
self.label_db_service
|
||||
.create_label(LabelMapper::from(label))
|
||||
.await
|
||||
}
|
||||
|
||||
async fn update_label(&self, label: Label) -> Result<(), PostError> {
|
||||
self.label_db_service
|
||||
.update_label(LabelMapper::from(label))
|
||||
.await
|
||||
}
|
||||
|
||||
async fn get_label_by_id(&self, id: i32) -> Result<Label, PostError> {
|
||||
self.label_db_service
|
||||
.get_label_by_id(id)
|
||||
.await
|
||||
.map(|mapper| mapper.into_entity())
|
||||
}
|
||||
|
||||
async fn get_all_labels(&self) -> Result<Vec<Label>, PostError> {
|
||||
self.label_db_service.get_all_labels().await.map(|mappers| {
|
||||
mappers
|
||||
.into_iter()
|
||||
.map(|mapper| mapper.into_entity())
|
||||
.collect()
|
||||
})
|
||||
}
|
||||
}
|
@ -1 +1,2 @@
|
||||
pub mod label_repository;
|
||||
pub mod post_repository;
|
||||
|
@ -0,0 +1,11 @@
|
||||
use async_trait::async_trait;
|
||||
|
||||
use crate::{application::error::post_error::PostError, domain::entity::label::Label};
|
||||
|
||||
#[async_trait]
|
||||
pub trait LabelRepository: Send + Sync {
|
||||
async fn create_label(&self, label: Label) -> Result<i32, PostError>;
|
||||
async fn update_label(&self, label: Label) -> Result<(), PostError>;
|
||||
async fn get_label_by_id(&self, id: i32) -> Result<Label, PostError>;
|
||||
async fn get_all_labels(&self) -> Result<Vec<Label>, PostError>;
|
||||
}
|
@ -1,2 +1,5 @@
|
||||
pub mod create_label_use_case;
|
||||
pub mod get_all_labels_use_case;
|
||||
pub mod get_all_post_info_use_case;
|
||||
pub mod get_full_post_use_case;
|
||||
pub mod update_label_use_case;
|
||||
|
@ -0,0 +1,30 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use async_trait::async_trait;
|
||||
|
||||
use crate::{
|
||||
application::{error::post_error::PostError, gateway::label_repository::LabelRepository},
|
||||
domain::entity::label::Label,
|
||||
};
|
||||
|
||||
#[async_trait]
|
||||
pub trait CreateLabelUseCase: Send + Sync {
|
||||
async fn execute(&self, label: Label) -> Result<i32, PostError>;
|
||||
}
|
||||
|
||||
pub struct CreateLabelUseCaseImpl {
|
||||
label_repository: Arc<dyn LabelRepository>,
|
||||
}
|
||||
|
||||
impl CreateLabelUseCaseImpl {
|
||||
pub fn new(label_repository: Arc<dyn LabelRepository>) -> Self {
|
||||
Self { label_repository }
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl CreateLabelUseCase for CreateLabelUseCaseImpl {
|
||||
async fn execute(&self, label: Label) -> Result<i32, PostError> {
|
||||
self.label_repository.create_label(label).await
|
||||
}
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use async_trait::async_trait;
|
||||
|
||||
use crate::{
|
||||
application::{error::post_error::PostError, gateway::label_repository::LabelRepository},
|
||||
domain::entity::label::Label,
|
||||
};
|
||||
|
||||
#[async_trait]
|
||||
pub trait GetAllLabelsUseCase: Send + Sync {
|
||||
async fn execute(&self) -> Result<Vec<Label>, PostError>;
|
||||
}
|
||||
|
||||
pub struct GetAllLabelsUseCaseImpl {
|
||||
label_repository: Arc<dyn LabelRepository>,
|
||||
}
|
||||
|
||||
impl GetAllLabelsUseCaseImpl {
|
||||
pub fn new(label_repository: Arc<dyn LabelRepository>) -> Self {
|
||||
Self { label_repository }
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl GetAllLabelsUseCase for GetAllLabelsUseCaseImpl {
|
||||
async fn execute(&self) -> Result<Vec<Label>, PostError> {
|
||||
self.label_repository.get_all_labels().await
|
||||
}
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use async_trait::async_trait;
|
||||
|
||||
use crate::{
|
||||
application::{error::post_error::PostError, gateway::label_repository::LabelRepository},
|
||||
domain::entity::label::Label,
|
||||
};
|
||||
|
||||
#[async_trait]
|
||||
pub trait UpdateLabelUseCase: Send + Sync {
|
||||
async fn execute(&self, label: Label) -> Result<(), PostError>;
|
||||
}
|
||||
|
||||
pub struct UpdateLabelUseCaseImpl {
|
||||
label_repository: Arc<dyn LabelRepository>,
|
||||
}
|
||||
|
||||
impl UpdateLabelUseCaseImpl {
|
||||
pub fn new(label_repository: Arc<dyn LabelRepository>) -> Self {
|
||||
Self { label_repository }
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl UpdateLabelUseCase for UpdateLabelUseCaseImpl {
|
||||
async fn execute(&self, label: Label) -> Result<(), PostError> {
|
||||
self.label_repository.update_label(label).await
|
||||
}
|
||||
}
|
@ -1,3 +1,4 @@
|
||||
#[derive(Clone)]
|
||||
pub struct Color {
|
||||
pub red: u8,
|
||||
pub green: u8,
|
||||
|
@ -1,5 +1,6 @@
|
||||
use crate::domain::entity::color::Color;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Label {
|
||||
pub id: i32,
|
||||
pub name: String,
|
||||
|
@ -1,4 +1,6 @@
|
||||
pub mod label_db_service_impl;
|
||||
pub mod post_db_service_impl;
|
||||
|
||||
mod label_record;
|
||||
mod post_info_with_label_record;
|
||||
mod post_with_label_record;
|
||||
|
102
backend/feature/post/src/framework/db/label_db_service_impl.rs
Normal file
102
backend/feature/post/src/framework/db/label_db_service_impl.rs
Normal file
@ -0,0 +1,102 @@
|
||||
use async_trait::async_trait;
|
||||
use sqlx::{Pool, Postgres};
|
||||
|
||||
use crate::{
|
||||
adapter::gateway::{label_db_mapper::LabelMapper, label_db_service::LabelDbService},
|
||||
application::error::post_error::PostError,
|
||||
framework::db::label_record::LabelRecord,
|
||||
};
|
||||
|
||||
pub struct LabelDbServiceImpl {
|
||||
db_pool: Pool<Postgres>,
|
||||
}
|
||||
|
||||
impl LabelDbServiceImpl {
|
||||
pub fn new(db_pool: Pool<Postgres>) -> Self {
|
||||
Self { db_pool }
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl LabelDbService for LabelDbServiceImpl {
|
||||
async fn create_label(&self, label: LabelMapper) -> Result<i32, PostError> {
|
||||
let id = sqlx::query_scalar!(
|
||||
r#"
|
||||
INSERT INTO label (name, color)
|
||||
VALUES ($1, $2)
|
||||
RETURNING id
|
||||
"#,
|
||||
label.name,
|
||||
label.color.value as i64
|
||||
)
|
||||
.fetch_one(&self.db_pool)
|
||||
.await
|
||||
.map_err(|err| PostError::DatabaseError(err.to_string()))?;
|
||||
|
||||
Ok(id)
|
||||
}
|
||||
|
||||
async fn update_label(&self, label: LabelMapper) -> Result<(), PostError> {
|
||||
let affected_rows = sqlx::query!(
|
||||
r#"
|
||||
UPDATE label
|
||||
SET name = $1, color = $2
|
||||
WHERE id = $3 AND deleted_time IS NULL
|
||||
"#,
|
||||
label.name,
|
||||
label.color.value as i64,
|
||||
label.id
|
||||
)
|
||||
.execute(&self.db_pool)
|
||||
.await
|
||||
.map_err(|err| PostError::DatabaseError(err.to_string()))?
|
||||
.rows_affected();
|
||||
|
||||
if affected_rows == 0 {
|
||||
return Err(PostError::NotFound);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn get_label_by_id(&self, id: i32) -> Result<LabelMapper, PostError> {
|
||||
let record = sqlx::query_as!(
|
||||
LabelRecord,
|
||||
r#"
|
||||
SELECT id, name, color
|
||||
FROM label
|
||||
WHERE id = $1 AND deleted_time IS NULL
|
||||
"#,
|
||||
id
|
||||
)
|
||||
.fetch_optional(&self.db_pool)
|
||||
.await
|
||||
.map_err(|err| PostError::DatabaseError(err.to_string()))?;
|
||||
|
||||
match record {
|
||||
Some(record) => Ok(record.into_mapper()),
|
||||
None => Err(PostError::NotFound),
|
||||
}
|
||||
}
|
||||
|
||||
async fn get_all_labels(&self) -> Result<Vec<LabelMapper>, PostError> {
|
||||
let records = sqlx::query_as!(
|
||||
LabelRecord,
|
||||
r#"
|
||||
SELECT id, name, color
|
||||
FROM label
|
||||
WHERE deleted_time IS NULL
|
||||
ORDER BY id
|
||||
"#
|
||||
)
|
||||
.fetch_all(&self.db_pool)
|
||||
.await
|
||||
.map_err(|err| PostError::DatabaseError(err.to_string()))?;
|
||||
|
||||
let mappers = records
|
||||
.into_iter()
|
||||
.map(|record| record.into_mapper())
|
||||
.collect();
|
||||
Ok(mappers)
|
||||
}
|
||||
}
|
19
backend/feature/post/src/framework/db/label_record.rs
Normal file
19
backend/feature/post/src/framework/db/label_record.rs
Normal file
@ -0,0 +1,19 @@
|
||||
use crate::adapter::gateway::{color_db_mapper::ColorMapper, label_db_mapper::LabelMapper};
|
||||
|
||||
pub struct LabelRecord {
|
||||
pub id: i32,
|
||||
pub name: String,
|
||||
pub color: i64,
|
||||
}
|
||||
|
||||
impl LabelRecord {
|
||||
pub fn into_mapper(self) -> LabelMapper {
|
||||
LabelMapper {
|
||||
id: self.id,
|
||||
name: self.name,
|
||||
color: ColorMapper {
|
||||
value: self.color as u32,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
@ -1,5 +1,8 @@
|
||||
pub mod post_api_doc;
|
||||
pub mod post_web_routes;
|
||||
|
||||
mod create_label_handler;
|
||||
mod get_all_labels_handler;
|
||||
mod get_all_post_info_handler;
|
||||
mod get_post_by_id_handler;
|
||||
mod update_label_handler;
|
||||
|
@ -0,0 +1,35 @@
|
||||
use actix_web::{HttpResponse, Responder, web};
|
||||
use auth::framework::web::auth_middleware::UserId;
|
||||
|
||||
use crate::adapter::delivery::{
|
||||
create_label_request_dto::CreateLabelRequestDto, label_response_dto::LabelResponseDto,
|
||||
post_controller::PostController,
|
||||
};
|
||||
|
||||
#[utoipa::path(
|
||||
post,
|
||||
path = "/label",
|
||||
tag = "post",
|
||||
summary = "Create a new label",
|
||||
responses(
|
||||
(status = 201, body = LabelResponseDto),
|
||||
),
|
||||
security(
|
||||
("oauth2" = [])
|
||||
)
|
||||
)]
|
||||
pub async fn create_label_handler(
|
||||
post_controller: web::Data<dyn PostController>,
|
||||
label_dto: web::Json<CreateLabelRequestDto>,
|
||||
_: UserId,
|
||||
) -> impl Responder {
|
||||
let result = post_controller.create_label(label_dto.into_inner()).await;
|
||||
|
||||
match result {
|
||||
Ok(label) => HttpResponse::Created().json(label),
|
||||
Err(e) => {
|
||||
log::error!("{e:?}");
|
||||
HttpResponse::InternalServerError().finish()
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
use actix_web::{HttpResponse, Responder, web};
|
||||
|
||||
use crate::adapter::delivery::{
|
||||
label_response_dto::LabelResponseDto, post_controller::PostController,
|
||||
};
|
||||
|
||||
#[utoipa::path(
|
||||
get,
|
||||
path = "/label",
|
||||
tag = "post",
|
||||
summary = "Get all labels",
|
||||
responses(
|
||||
(status = 200, body = Vec<LabelResponseDto>)
|
||||
)
|
||||
)]
|
||||
pub async fn get_all_labels_handler(
|
||||
post_controller: web::Data<dyn PostController>,
|
||||
) -> impl Responder {
|
||||
let result = post_controller.get_all_labels().await;
|
||||
|
||||
match result {
|
||||
Ok(labels) => HttpResponse::Ok().json(labels),
|
||||
Err(e) => {
|
||||
log::error!("{e:?}");
|
||||
HttpResponse::InternalServerError().finish()
|
||||
}
|
||||
}
|
||||
}
|
@ -7,7 +7,7 @@ use crate::adapter::delivery::{
|
||||
|
||||
#[utoipa::path(
|
||||
get,
|
||||
path = "/post/all",
|
||||
path = "/post",
|
||||
tag = "post",
|
||||
summary = "Get all post information",
|
||||
params(
|
||||
|
@ -1,10 +1,16 @@
|
||||
use crate::framework::web::{get_all_post_info_handler, get_post_by_id_handler};
|
||||
use crate::framework::web::{
|
||||
create_label_handler, get_all_labels_handler, get_all_post_info_handler,
|
||||
get_post_by_id_handler, update_label_handler,
|
||||
};
|
||||
use utoipa::{OpenApi, openapi};
|
||||
|
||||
#[derive(OpenApi)]
|
||||
#[openapi(paths(
|
||||
get_all_post_info_handler::get_all_post_info_handler,
|
||||
get_post_by_id_handler::get_post_by_id_handler
|
||||
get_post_by_id_handler::get_post_by_id_handler,
|
||||
create_label_handler::create_label_handler,
|
||||
update_label_handler::update_label_handler,
|
||||
get_all_labels_handler::get_all_labels_handler
|
||||
))]
|
||||
struct ApiDoc;
|
||||
|
||||
|
@ -1,14 +1,22 @@
|
||||
use actix_web::web;
|
||||
|
||||
use crate::framework::web::{
|
||||
create_label_handler::create_label_handler, get_all_labels_handler::get_all_labels_handler,
|
||||
get_all_post_info_handler::get_all_post_info_handler,
|
||||
get_post_by_id_handler::get_post_by_id_handler,
|
||||
get_post_by_id_handler::get_post_by_id_handler, update_label_handler::update_label_handler,
|
||||
};
|
||||
|
||||
pub fn configure_post_routes(cfg: &mut web::ServiceConfig) {
|
||||
cfg.service(
|
||||
web::scope("/post")
|
||||
.route("/all", web::get().to(get_all_post_info_handler))
|
||||
.route("", web::get().to(get_all_post_info_handler))
|
||||
.route("/{id}", web::get().to(get_post_by_id_handler)),
|
||||
);
|
||||
|
||||
cfg.service(
|
||||
web::scope("/label")
|
||||
.route("", web::get().to(get_all_labels_handler))
|
||||
.route("", web::post().to(create_label_handler))
|
||||
.route("/{id}", web::put().to(update_label_handler)),
|
||||
);
|
||||
}
|
||||
|
@ -0,0 +1,46 @@
|
||||
use actix_web::{HttpResponse, Responder, web};
|
||||
use auth::framework::web::auth_middleware::UserId;
|
||||
|
||||
use crate::{
|
||||
adapter::delivery::{
|
||||
label_response_dto::LabelResponseDto, post_controller::PostController,
|
||||
update_label_request_dto::UpdateLabelRequestDto,
|
||||
},
|
||||
application::error::post_error::PostError,
|
||||
};
|
||||
|
||||
#[utoipa::path(
|
||||
put,
|
||||
path = "/label/{id}",
|
||||
tag = "post",
|
||||
summary = "Update a label by ID",
|
||||
responses(
|
||||
(status = 200, body = LabelResponseDto),
|
||||
(status = 404, description = "Label not found"),
|
||||
),
|
||||
security(
|
||||
("oauth2" = [])
|
||||
)
|
||||
)]
|
||||
pub async fn update_label_handler(
|
||||
post_controller: web::Data<dyn PostController>,
|
||||
label_dto: web::Json<UpdateLabelRequestDto>,
|
||||
path: web::Path<i32>,
|
||||
_: UserId,
|
||||
) -> impl Responder {
|
||||
let id = path.into_inner();
|
||||
let result = post_controller
|
||||
.update_label(id, label_dto.into_inner())
|
||||
.await;
|
||||
|
||||
match result {
|
||||
Ok(label) => HttpResponse::Ok().json(label),
|
||||
Err(e) => match e {
|
||||
PostError::NotFound => HttpResponse::NotFound().finish(),
|
||||
_ => {
|
||||
log::error!("{e:?}");
|
||||
HttpResponse::InternalServerError().finish()
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
@ -31,13 +31,20 @@ use openidconnect::reqwest;
|
||||
use post::{
|
||||
adapter::{
|
||||
delivery::post_controller::{PostController, PostControllerImpl},
|
||||
gateway::post_repository_impl::PostRepositoryImpl,
|
||||
gateway::{
|
||||
label_repository_impl::LabelRepositoryImpl, post_repository_impl::PostRepositoryImpl,
|
||||
},
|
||||
},
|
||||
application::use_case::{
|
||||
create_label_use_case::CreateLabelUseCaseImpl,
|
||||
get_all_labels_use_case::GetAllLabelsUseCaseImpl,
|
||||
get_all_post_info_use_case::GetAllPostInfoUseCaseImpl,
|
||||
get_full_post_use_case::GetFullPostUseCaseImpl,
|
||||
update_label_use_case::UpdateLabelUseCaseImpl,
|
||||
},
|
||||
framework::db::{
|
||||
label_db_service_impl::LabelDbServiceImpl, post_db_service_impl::PostDbServiceImpl,
|
||||
},
|
||||
framework::db::post_db_service_impl::PostDbServiceImpl,
|
||||
};
|
||||
use sqlx::{Pool, Postgres};
|
||||
|
||||
@ -55,6 +62,7 @@ impl Container {
|
||||
http_client: reqwest::Client,
|
||||
configuration: Configuration,
|
||||
) -> Self {
|
||||
// Auth
|
||||
let oidc_configuration = &configuration.oidc;
|
||||
let auth_oidc_service = Arc::new(AuthOidcServiceImpl::new(
|
||||
oidc_configuration.provider_metadata.clone(),
|
||||
@ -64,40 +72,61 @@ impl Container {
|
||||
http_client,
|
||||
));
|
||||
let user_db_service = Arc::new(UserDbServiceImpl::new(db_pool.clone()));
|
||||
|
||||
let auth_repository = Arc::new(AuthRepositoryImpl::new(user_db_service, auth_oidc_service));
|
||||
|
||||
let get_auth_url_use_case = Arc::new(LoginUseCaseImpl::new(auth_repository.clone()));
|
||||
let exchange_auth_code_use_case =
|
||||
Arc::new(ExchangeAuthCodeUseCaseImpl::new(auth_repository.clone()));
|
||||
let get_user_use_case = Arc::new(GetUserUseCaseImpl::new(auth_repository.clone()));
|
||||
|
||||
let auth_controller = Arc::new(AuthControllerImpl::new(
|
||||
get_auth_url_use_case,
|
||||
exchange_auth_code_use_case,
|
||||
get_user_use_case,
|
||||
));
|
||||
|
||||
// Post
|
||||
let post_db_service = Arc::new(PostDbServiceImpl::new(db_pool.clone()));
|
||||
let label_db_service = Arc::new(LabelDbServiceImpl::new(db_pool.clone()));
|
||||
|
||||
let post_repository = Arc::new(PostRepositoryImpl::new(post_db_service.clone()));
|
||||
let label_repository = Arc::new(LabelRepositoryImpl::new(label_db_service.clone()));
|
||||
|
||||
let get_all_post_info_use_case =
|
||||
Arc::new(GetAllPostInfoUseCaseImpl::new(post_repository.clone()));
|
||||
let get_full_post_use_case = Arc::new(GetFullPostUseCaseImpl::new(post_repository.clone()));
|
||||
let create_label_use_case = Arc::new(CreateLabelUseCaseImpl::new(label_repository.clone()));
|
||||
let update_label_use_case = Arc::new(UpdateLabelUseCaseImpl::new(label_repository.clone()));
|
||||
let get_all_labels_use_case =
|
||||
Arc::new(GetAllLabelsUseCaseImpl::new(label_repository.clone()));
|
||||
|
||||
let post_controller = Arc::new(PostControllerImpl::new(
|
||||
get_all_post_info_use_case,
|
||||
get_full_post_use_case,
|
||||
create_label_use_case,
|
||||
update_label_use_case,
|
||||
get_all_labels_use_case,
|
||||
));
|
||||
|
||||
// Image
|
||||
let image_db_service = Arc::new(ImageDbServiceImpl::new(db_pool.clone()));
|
||||
let image_storage = Arc::new(ImageStorageImpl::new(&configuration.storage.storage_path));
|
||||
|
||||
let image_repository = Arc::new(ImageRepositoryImpl::new(
|
||||
image_db_service.clone(),
|
||||
image_storage.clone(),
|
||||
));
|
||||
|
||||
let upload_image_use_case = Arc::new(UploadImageUseCaseImpl::new(image_repository.clone()));
|
||||
let get_image_use_case = Arc::new(GetImageUseCaseImpl::new(image_repository));
|
||||
|
||||
let image_controller = Arc::new(ImageControllerImpl::new(
|
||||
upload_image_use_case,
|
||||
get_image_use_case,
|
||||
));
|
||||
|
||||
// Return the container with all controllers
|
||||
Self {
|
||||
auth_controller,
|
||||
image_controller,
|
||||
|
@ -7,7 +7,7 @@ export class PostApiServiceImpl implements PostApiService {
|
||||
constructor(private fetchFn: typeof fetch) {}
|
||||
|
||||
async getAllPosts(): Promise<PostInfoResponseDto[]> {
|
||||
const url = new URL('post/all', Environment.API_BASE_URL);
|
||||
const url = new URL('post', Environment.API_BASE_URL);
|
||||
|
||||
const response = await this.fetchFn(url.href);
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user