feat: add validation to project creation and update endpoints
Some checks failed
ci/woodpecker/push/check_fmt Pipeline failed
Some checks failed
ci/woodpecker/push/check_fmt Pipeline failed
This commit is contained in:
parent
f20802682a
commit
8e460ec6dd
6 changed files with 48 additions and 25 deletions
|
@ -14,3 +14,6 @@ SECRET_KEY=
|
||||||
|
|
||||||
# LDAP section
|
# LDAP section
|
||||||
LDAP_ADMIN_PASSWORD=
|
LDAP_ADMIN_PASSWORD=
|
||||||
|
|
||||||
|
# Rust log level
|
||||||
|
RUST_LOG=info
|
|
@ -20,6 +20,7 @@ env_logger = "0.11"
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
|
|
||||||
serde = { version = "1", features = ["derive"] }
|
serde = { version = "1", features = ["derive"] }
|
||||||
|
validator = { version = "0.20.0", features = ["derive"] }
|
||||||
sea-orm = { version = "1.1", features = [
|
sea-orm = { version = "1.1", features = [
|
||||||
"sqlx-postgres",
|
"sqlx-postgres",
|
||||||
"runtime-tokio-rustls",
|
"runtime-tokio-rustls",
|
||||||
|
|
|
@ -1,18 +1,13 @@
|
||||||
|
use std::path;
|
||||||
|
|
||||||
use actix_web::{Result, delete, get, post, put, web};
|
use actix_web::{Result, delete, get, post, put, web};
|
||||||
use sea_orm::prelude::Uuid;
|
use sea_orm::prelude::Uuid;
|
||||||
use serde::Deserialize;
|
use validator::Validate;
|
||||||
|
|
||||||
use crate::dto::project::UpdateProject;
|
|
||||||
|
|
||||||
use crate::db::Database;
|
use crate::db::Database;
|
||||||
|
use crate::db::project::CreateProject;
|
||||||
use crate::error::ApiError;
|
use crate::error::ApiError;
|
||||||
|
|
||||||
// Maybe move this here into the corresponding DTO file
|
|
||||||
#[derive(Deserialize)]
|
|
||||||
struct CreateProject {
|
|
||||||
name: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn setup(cfg: &mut actix_web::web::ServiceConfig) {
|
pub fn setup(cfg: &mut actix_web::web::ServiceConfig) {
|
||||||
cfg.service(get_project)
|
cfg.service(get_project)
|
||||||
.service(get_projects)
|
.service(get_projects)
|
||||||
|
@ -45,20 +40,22 @@ async fn get_project(
|
||||||
#[post("")]
|
#[post("")]
|
||||||
async fn create_project(
|
async fn create_project(
|
||||||
db: web::Data<Database>,
|
db: web::Data<Database>,
|
||||||
create_project_struct: web::Json<CreateProject>,
|
create_project: web::Json<CreateProject>,
|
||||||
) -> Result<web::Json<entity::project::Model>, ApiError> {
|
) -> Result<web::Json<entity::project::Model>, ApiError> {
|
||||||
let result = db.create_project(&create_project_struct.name).await?;
|
create_project.validate()?;
|
||||||
|
let result = db.create_project(create_project.into_inner()).await?;
|
||||||
|
|
||||||
Ok(web::Json(result))
|
Ok(web::Json(result))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[put("")]
|
#[put("/{id}")]
|
||||||
async fn update_project(
|
async fn update_project(
|
||||||
db: web::Data<Database>,
|
db: web::Data<Database>,
|
||||||
update_project_struct: web::Json<UpdateProject>,
|
path: web::Path<Uuid>,
|
||||||
|
update_project: web::Json<CreateProject>,
|
||||||
) -> Result<web::Json<entity::project::Model>, ApiError> {
|
) -> Result<web::Json<entity::project::Model>, ApiError> {
|
||||||
let updated_project = db
|
let updated_project = db
|
||||||
.update_project(update_project_struct.into_inner())
|
.update_project(&path, update_project.into_inner())
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
Ok(web::Json(updated_project))
|
Ok(web::Json(updated_project))
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use sea_orm::{ConnectOptions, DatabaseConnection};
|
use sea_orm::{ConnectOptions, DatabaseConnection};
|
||||||
|
|
||||||
mod group;
|
mod group;
|
||||||
mod project;
|
pub mod project;
|
||||||
mod user;
|
mod user;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
|
|
|
@ -2,12 +2,18 @@ use super::Database;
|
||||||
use crate::error::ApiError;
|
use crate::error::ApiError;
|
||||||
use log::debug;
|
use log::debug;
|
||||||
|
|
||||||
use crate::dto::project::UpdateProject;
|
|
||||||
|
|
||||||
use entity::project;
|
use entity::project;
|
||||||
use sea_orm::ActiveValue::{NotSet, Set, Unchanged};
|
use sea_orm::ActiveValue::{NotSet, Set, Unchanged};
|
||||||
use sea_orm::prelude::Uuid;
|
use sea_orm::prelude::Uuid;
|
||||||
use sea_orm::{ActiveModelTrait, DeleteResult, EntityTrait};
|
use sea_orm::{ActiveModelTrait, DeleteResult, EntityTrait};
|
||||||
|
use serde::Deserialize;
|
||||||
|
use validator::Validate;
|
||||||
|
|
||||||
|
#[derive(Deserialize, Validate)]
|
||||||
|
pub struct CreateProject {
|
||||||
|
#[validate(length(min = 3))]
|
||||||
|
name: String,
|
||||||
|
}
|
||||||
|
|
||||||
impl Database {
|
impl Database {
|
||||||
pub async fn get_projects(&self) -> Result<Vec<project::Model>, ApiError> {
|
pub async fn get_projects(&self) -> Result<Vec<project::Model>, ApiError> {
|
||||||
|
@ -30,23 +36,30 @@ impl Database {
|
||||||
Ok(project)
|
Ok(project)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn create_project(&self, name: &str) -> Result<project::Model, ApiError> {
|
pub async fn create_project(
|
||||||
debug!("Creating project with name: {}", name);
|
&self,
|
||||||
|
create_project: CreateProject,
|
||||||
|
) -> Result<project::Model, ApiError> {
|
||||||
|
debug!("Creating project with name: {}", create_project.name);
|
||||||
|
|
||||||
let project = project::ActiveModel {
|
let project = project::ActiveModel {
|
||||||
id: NotSet,
|
id: NotSet,
|
||||||
name: Set(name.to_owned()),
|
name: Set(create_project.name),
|
||||||
};
|
};
|
||||||
|
|
||||||
let project = project.insert(&self.conn).await?;
|
let project = project.insert(&self.conn).await?;
|
||||||
Ok(project)
|
Ok(project)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn update_project(&self, project: UpdateProject) -> Result<project::Model, ApiError> {
|
pub async fn update_project(
|
||||||
debug!("Updating project with id: {}", &project.id);
|
&self,
|
||||||
|
id: &Uuid,
|
||||||
|
project: CreateProject,
|
||||||
|
) -> Result<project::Model, ApiError> {
|
||||||
|
debug!("Updating project with id: {}", &id);
|
||||||
|
|
||||||
let active_model = project::ActiveModel {
|
let active_model = project::ActiveModel {
|
||||||
id: Unchanged(project.id),
|
id: Unchanged(*id),
|
||||||
name: Set(project.name),
|
name: Set(project.name),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,12 +1,18 @@
|
||||||
use actix_web::{HttpResponse, ResponseError, http::StatusCode};
|
use actix_web::{HttpResponse, ResponseError, cookie::time::error, http::StatusCode};
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
#[derive(Error, Debug)]
|
#[derive(Error, Debug)]
|
||||||
pub enum ApiError {
|
pub enum ApiError {
|
||||||
#[error("Database Error: {0}")]
|
#[error("Database Error: {0}")]
|
||||||
Database(#[from] sea_orm::DbErr),
|
Database(#[from] sea_orm::DbErr),
|
||||||
|
#[error("Unauthorized")]
|
||||||
|
Unauthorized,
|
||||||
#[error("Not Found")]
|
#[error("Not Found")]
|
||||||
NotFound,
|
NotFound,
|
||||||
|
#[error("Bad Request: {0}")]
|
||||||
|
BadRequest(String),
|
||||||
|
#[error("Validation Error: {0}")]
|
||||||
|
ValidationError(#[from] validator::ValidationErrors),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ResponseError for ApiError {
|
impl ResponseError for ApiError {
|
||||||
|
@ -14,6 +20,9 @@ impl ResponseError for ApiError {
|
||||||
match self {
|
match self {
|
||||||
ApiError::Database(..) => StatusCode::INTERNAL_SERVER_ERROR,
|
ApiError::Database(..) => StatusCode::INTERNAL_SERVER_ERROR,
|
||||||
ApiError::NotFound => StatusCode::NOT_FOUND,
|
ApiError::NotFound => StatusCode::NOT_FOUND,
|
||||||
|
ApiError::Unauthorized => StatusCode::UNAUTHORIZED,
|
||||||
|
ApiError::BadRequest(..) => StatusCode::BAD_REQUEST,
|
||||||
|
ApiError::ValidationError(..) => StatusCode::BAD_REQUEST,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue