feat: enhance error handling and API documentation for image and label features
This commit is contained in:
parent
6213ab7a95
commit
3b16b165bb
@ -7,6 +7,12 @@ pub enum ImageError {
|
||||
Unexpected(anyhow::Error),
|
||||
}
|
||||
|
||||
impl Into<String> for ImageError {
|
||||
fn into(self) -> String {
|
||||
format!("{}", self)
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for ImageError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
|
@ -15,7 +15,7 @@ use crate::{
|
||||
summary = "Get image by ID",
|
||||
responses (
|
||||
(status = 200, body = inline(ResponseBodySchema), content_type = "image/*"),
|
||||
(status = 404, description = "Image not found")
|
||||
(status = 404, description = ImageError::NotFound),
|
||||
)
|
||||
)]
|
||||
pub async fn get_image_by_id_handler(
|
||||
@ -31,12 +31,12 @@ pub async fn get_image_by_id_handler(
|
||||
.body(image_response.data),
|
||||
Err(e) => match e {
|
||||
ImageError::NotFound => HttpResponse::NotFound().finish(),
|
||||
ImageError::Unexpected(e) => {
|
||||
capture_anyhow(&e);
|
||||
ImageError::UnsupportedMimeType(_) => {
|
||||
capture_anyhow(&anyhow!(e));
|
||||
HttpResponse::InternalServerError().finish()
|
||||
}
|
||||
_ => {
|
||||
capture_anyhow(&anyhow!(e));
|
||||
ImageError::Unexpected(e) => {
|
||||
capture_anyhow(&e);
|
||||
HttpResponse::InternalServerError().finish()
|
||||
}
|
||||
},
|
||||
|
@ -25,7 +25,7 @@ use crate::{
|
||||
),
|
||||
responses (
|
||||
(status = 201, body = ImageInfoResponseDto),
|
||||
(status = 400, description = "Unsupported MIME type or file field not found"),
|
||||
(status = 400, description = ImageError::UnsupportedMimeType("{MIME_TYPE}".to_string())),
|
||||
),
|
||||
security(
|
||||
("oauth2" = [])
|
||||
@ -78,12 +78,12 @@ pub async fn upload_image_handler(
|
||||
ImageError::UnsupportedMimeType(mime_type) => {
|
||||
HttpResponse::BadRequest().body(format!("Unsupported MIME type: {}", mime_type))
|
||||
}
|
||||
ImageError::Unexpected(e) => {
|
||||
capture_anyhow(&e);
|
||||
ImageError::NotFound => {
|
||||
capture_anyhow(&anyhow!(e));
|
||||
HttpResponse::InternalServerError().finish()
|
||||
}
|
||||
_ => {
|
||||
capture_anyhow(&anyhow!(e));
|
||||
ImageError::Unexpected(e) => {
|
||||
capture_anyhow(&e);
|
||||
HttpResponse::InternalServerError().finish()
|
||||
}
|
||||
},
|
||||
|
@ -8,11 +8,17 @@ pub enum LabelError {
|
||||
Unexpected(anyhow::Error),
|
||||
}
|
||||
|
||||
impl Into<String> for LabelError {
|
||||
fn into(self) -> String {
|
||||
format!("{}", self)
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for LabelError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
LabelError::NotFound => write!(f, "Label not found"),
|
||||
LabelError::Unauthorized => write!(f, "Unauthorized access to label"),
|
||||
LabelError::Unauthorized => write!(f, "Unauthorized access"),
|
||||
LabelError::DuplicatedLabelName => write!(f, "Label name already exists"),
|
||||
LabelError::Unexpected(e) => write!(f, "Unexpected error: {}", e),
|
||||
}
|
||||
|
@ -14,10 +14,12 @@ use crate::{
|
||||
#[utoipa::path(
|
||||
post,
|
||||
path = "/label",
|
||||
tag = "post",
|
||||
tag = "label",
|
||||
summary = "Create a new label",
|
||||
responses(
|
||||
(status = 201, body = LabelResponseDto),
|
||||
(status = 401, description = LabelError::Unauthorized),
|
||||
(status = 409, description = LabelError::DuplicatedLabelName),
|
||||
),
|
||||
security(
|
||||
("oauth2" = [])
|
||||
|
@ -10,7 +10,7 @@ use crate::{
|
||||
#[utoipa::path(
|
||||
get,
|
||||
path = "/label",
|
||||
tag = "post",
|
||||
tag = "label",
|
||||
summary = "Get all labels",
|
||||
responses(
|
||||
(status = 200, body = Vec<LabelResponseDto>)
|
||||
|
@ -13,13 +13,13 @@ use crate::{
|
||||
#[utoipa::path(
|
||||
put,
|
||||
path = "/label/{id}",
|
||||
tag = "post",
|
||||
tag = "label",
|
||||
summary = "Update a label by ID",
|
||||
responses(
|
||||
(status = 200, body = LabelResponseDto),
|
||||
(status = 401, description = "Unauthorized"),
|
||||
(status = 404, description = "Label not found"),
|
||||
(status = 409, description = "Duplicated label name"),
|
||||
(status = 401, description = LabelError::Unauthorized),
|
||||
(status = 404, description = LabelError::NotFound),
|
||||
(status = 409, description = LabelError::DuplicatedLabelName),
|
||||
),
|
||||
security(
|
||||
("oauth2" = [])
|
||||
|
@ -9,14 +9,20 @@ pub enum PostError {
|
||||
Unexpected(anyhow::Error),
|
||||
}
|
||||
|
||||
impl Into<String> for PostError {
|
||||
fn into(self) -> String {
|
||||
format!("{}", self)
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for PostError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
PostError::NotFound => write!(f, "Post not found"),
|
||||
PostError::Unauthorized => write!(f, "Unauthorized access to post"),
|
||||
PostError::Unauthorized => write!(f, "Unauthorized access"),
|
||||
PostError::InvalidSemanticId => write!(
|
||||
f,
|
||||
"Semantic ID shouldn't be numeric and must conform to `^[0-9a-zA-Z_\\-]+$`"
|
||||
"Semantic ID shouldn't be numeric and must conform to `^[0-9a-zA-Z_-]+$`"
|
||||
),
|
||||
PostError::DuplicatedSemanticId => write!(f, "Semantic ID already exists"),
|
||||
PostError::Unexpected(e) => write!(f, "Unexpected error: {}", e),
|
||||
|
@ -20,7 +20,7 @@ impl PostInfo {
|
||||
return Err(PostError::InvalidSemanticId);
|
||||
}
|
||||
|
||||
let re = Regex::new(r"^[0-9a-zA-Z_\-]+$").unwrap();
|
||||
let re = Regex::new(r"^[0-9a-zA-Z_-]+$").unwrap();
|
||||
if !re.is_match(&self.semantic_id) {
|
||||
return Err(PostError::InvalidSemanticId);
|
||||
}
|
||||
|
@ -18,6 +18,9 @@ use crate::{
|
||||
summary = "Create a new post",
|
||||
responses(
|
||||
(status = 201, body = PostResponseDto),
|
||||
(status = 400, description = PostError::InvalidSemanticId),
|
||||
(status = 401, description = PostError::Unauthorized),
|
||||
(status = 409, description = PostError::DuplicatedSemanticId),
|
||||
),
|
||||
security(
|
||||
("oauth2" = [])
|
||||
|
@ -16,7 +16,8 @@ use crate::{
|
||||
description = "Only authenticated users can access unpublished posts. Accepts either numeric ID or semantic ID.",
|
||||
responses (
|
||||
(status = 200, body = PostResponseDto),
|
||||
(status = 404, description = "Post not found")
|
||||
(status = 401, description = PostError::Unauthorized),
|
||||
(status = 404, description = PostError::NotFound),
|
||||
)
|
||||
)]
|
||||
pub async fn get_post_by_id_handler(
|
||||
|
@ -17,7 +17,10 @@ use crate::{
|
||||
summary = "Update a post by ID",
|
||||
responses(
|
||||
(status = 200, body = PostResponseDto),
|
||||
(status = 404, description = "Post not found"),
|
||||
(status = 400, description = PostError::InvalidSemanticId),
|
||||
(status = 401, description = PostError::Unauthorized),
|
||||
(status = 404, description = PostError::NotFound),
|
||||
(status = 409, description = PostError::DuplicatedSemanticId),
|
||||
),
|
||||
security(
|
||||
("oauth2" = [])
|
||||
|
@ -1,6 +1,7 @@
|
||||
use actix_web::web;
|
||||
use auth::framework::web::auth_api_doc;
|
||||
use image::framework::web::image_api_doc;
|
||||
use label::framework::web::label_api_doc;
|
||||
use post::framework::web::post_api_doc;
|
||||
use utoipa::{
|
||||
OpenApi,
|
||||
@ -40,6 +41,7 @@ pub fn configure_api_doc_routes(cfg: &mut web::ServiceConfig) {
|
||||
let openapi = ApiDoc::openapi()
|
||||
.merge_from(auth_api_doc::openapi())
|
||||
.merge_from(image_api_doc::openapi())
|
||||
.merge_from(label_api_doc::openapi())
|
||||
.merge_from(post_api_doc::openapi());
|
||||
|
||||
cfg.service(Redoc::with_url("/redoc", openapi));
|
||||
|
@ -85,6 +85,7 @@ fn create_app(
|
||||
.wrap(session_middleware_builder.build())
|
||||
.app_data(web::Data::from(container.auth_controller))
|
||||
.app_data(web::Data::from(container.image_controller))
|
||||
.app_data(web::Data::from(container.label_controller))
|
||||
.app_data(web::Data::from(container.post_controller))
|
||||
.configure(configure_api_doc_routes)
|
||||
.configure(configure_auth_routes)
|
||||
|
Loading…
x
Reference in New Issue
Block a user