Skip to content

Commit

Permalink
complete CRUD functions for organization
Browse files Browse the repository at this point in the history
  • Loading branch information
calebbourg committed Dec 1, 2023
1 parent 24c1c1e commit b7e05d8
Show file tree
Hide file tree
Showing 6 changed files with 94 additions and 85 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions entity_api/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ edition = "2021"
[dependencies]
entity = { path = "../entity" }
serde_json = "1.0.107"
serde = { version = "1.0", features = ["derive"] }
log = "0.4.20"

[dependencies.sea-orm]
Expand Down
18 changes: 10 additions & 8 deletions entity_api/src/error.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
use std::error::Error as StdError;
use std::fmt;

use serde::Serialize;

use sea_orm::error::DbErr;

/// Errors while executing operations related to entities.
Expand All @@ -10,12 +12,12 @@ use sea_orm::error::DbErr;
#[derive(Debug)]
pub struct Error {
// Underlying error emitted from seaORM internals
pub inner: DbErr,
pub inner: Option<DbErr>,
// Enum representing which category of error
pub error_type: EntityApiErrorType,
}

#[derive(Debug)]
#[derive(Debug, Serialize)]
pub enum EntityApiErrorType {
// Record not found
RecordNotFound,
Expand All @@ -37,27 +39,27 @@ impl From<DbErr> for Error {
fn from(err: DbErr) -> Self {
match err {
DbErr::RecordNotFound(_) => Error {
inner: err,
inner: Some(err),
error_type: EntityApiErrorType::RecordNotFound,
},
DbErr::RecordNotUpdated => Error {
inner: err,
inner: Some(err),
error_type: EntityApiErrorType::RecordNotUpdated,
},
DbErr::ConnectionAcquire(_) => Error {
inner: err,
inner: Some(err),
error_type: EntityApiErrorType::SystemError,
},
DbErr::Conn(_) => Error {
inner: err,
inner: Some(err),
error_type: EntityApiErrorType::SystemError,
},
DbErr::Exec(_) => Error {
inner: err,
inner: Some(err),
error_type: EntityApiErrorType::SystemError,
},
_ => Error {
inner: err,
inner: Some(err),
error_type: EntityApiErrorType::SystemError,
},
}
Expand Down
45 changes: 42 additions & 3 deletions entity_api/src/organization.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,48 @@
use super::error::Error;
use super::error::{EntityApiErrorType, Error};
use entity::organization;
use organization::{Entity, Model};
use sea_orm::{entity::prelude::*, ActiveValue, DatabaseConnection};
use organization::{ActiveModel, Entity, Model};
use sea_orm::{entity::prelude::*, ActiveValue, DatabaseConnection, TryIntoModel};
use serde_json::json;

pub async fn create(db: &DatabaseConnection, organization_model: Model) -> Result<Model, Error> {
let organization_active_model: ActiveModel = organization_model.into();
Ok(organization_active_model.insert(db).await?)
}

pub async fn update(
db: &DatabaseConnection,
id: i32,
organization_model: Model,
) -> Result<Model, Error> {
let result = find_by_id(db, id).await?;

match result {
Some(_) => {
let active_model: ActiveModel = organization_model.into();
Ok(active_model.save(db).await?.try_into_model()?)
}
None => Err(Error {
inner: None,
error_type: EntityApiErrorType::RecordNotFound,
}),
}
}

pub async fn delete_by_id(db: &DatabaseConnection, id: i32) -> Result<(), Error> {
let result = find_by_id(db, id).await?;

match result {
Some(organization_model) => {
organization_model.delete(db).await?;
Ok(())
}
None => Err(Error {
inner: None,
error_type: EntityApiErrorType::RecordNotFound,
}),
}
}

pub async fn find_all(db: &DatabaseConnection) -> Vec<Model> {
Entity::find().all(db).await.unwrap_or(vec![])
}
Expand Down
82 changes: 26 additions & 56 deletions web/src/controller/organization_controller.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,7 @@ use axum::extract::{Path, Query, State};
use axum::response::IntoResponse;
use axum::Json;
use entity::organization;
use entity::organization::Entity as Organization;
use entity_api::organization as OrganizationApi;
use sea_orm::entity::EntityTrait;
use sea_orm::ActiveModelTrait;
use sea_orm::ActiveValue::{NotSet, Set};
use sea_orm::DeleteResult;
use serde_json::json;

extern crate log;
Expand All @@ -32,15 +27,16 @@ impl OrganizationController {
/// Test this with curl: curl --header "Content-Type: application/json" \ in zsh at 12:03:06
/// --request GET \
/// http://localhost:4000/organizations/<id>
pub async fn read(State(app_state): State<AppState>, Path(id): Path<i32>) -> impl IntoResponse {
pub async fn read(
State(app_state): State<AppState>,
Path(id): Path<i32>,
) -> Result<impl IntoResponse, Error> {
debug!("GET Organization by id: {}", id);

let organization: Result<Option<organization::Model>, Error> =
OrganizationApi::find_by_id(&app_state.database_connection.unwrap(), id)
.await
.map_err(|err| err.into());
let organization: Option<organization::Model> =
OrganizationApi::find_by_id(&app_state.database_connection.unwrap(), id).await?;

Json(organization)
Ok(Json(organization))
}

/// CREATE a new Organization entity
Expand All @@ -50,21 +46,15 @@ impl OrganizationController {
/// http://localhost:4000/organizations
pub async fn create(
State(app_state): State<AppState>,
Json(organization_json): Json<organization::Model>,
) -> impl IntoResponse {
debug!("CREATE new Organization: {}", organization_json.name);
Json(organization_model): Json<organization::Model>,
) -> Result<impl IntoResponse, Error> {
debug!("CREATE new Organization: {}", organization_model.name);

let organization_active_model = organization::ActiveModel {
id: NotSet,
name: Set(organization_json.name),
};
let organization: organization::Model =
OrganizationApi::create(&app_state.database_connection.unwrap(), organization_model)
.await?;

let organization: organization::Model = organization_active_model
.insert(&app_state.database_connection.unwrap())
.await
.unwrap_or_default();

Json(organization)
Ok(Json(organization))
}

/// UPDATE a particular Organization entity specified by its primary key
Expand All @@ -74,32 +64,19 @@ impl OrganizationController {
pub async fn update(
State(app_state): State<AppState>,
Path(id): Path<i32>,
Query(organization_params): Query<organization::Model>,
) -> Result<Json<entity::organization::Model>, Error> {
Query(organization_model): Query<organization::Model>,
) -> Result<impl IntoResponse, Error> {
debug!(
"UPDATE the entire Organization by id: {}, new name: {}",
id, organization_params.name
id, organization_model.name
);

let db = app_state.database_connection.as_ref().unwrap();

let organization_to_update = organization::Entity::find_by_id(id)
.one(db)
.await
.unwrap_or_default();

let updated_organization = match organization_to_update {
Some(org) => {
let mut organization_am: organization::ActiveModel = org.into();
organization_am.name = Set(organization_params.name);

organization::Entity::update(organization_am)
.exec(db)
.await
.unwrap()
}
None => return Err(Error::EntityNotFound),
};
let updated_organization: organization::Model = OrganizationApi::update(
&app_state.database_connection.unwrap(),
id,
organization_model,
)
.await?;

Ok(Json(updated_organization))
}
Expand All @@ -111,17 +88,10 @@ impl OrganizationController {
pub async fn delete(
State(app_state): State<AppState>,
Path(id): Path<i32>,
) -> impl IntoResponse {
) -> Result<impl IntoResponse, Error> {
debug!("DELETE Organization by id: {}", id);

let res: DeleteResult = Organization::delete_by_id(id)
.exec(&app_state.database_connection.unwrap())
.await
.unwrap();

// TODO: temporary check while learning, return a DBErr instead
assert_eq!(res.rows_affected, 1);

Json(json!({"id": id}))
OrganizationApi::delete_by_id(&app_state.database_connection.unwrap(), id).await?;
Ok(Json(json!({"id": id})))
}
}
32 changes: 14 additions & 18 deletions web/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,8 @@ use entity_api::error::Error as EntityApiError;

pub type Result<T> = core::result::Result<T, Error>;

#[derive(Debug, Serialize)]

pub enum Error {
InternalServer,
EntityNotFound,
UnprocessableEntity,
}
#[derive(Debug)]
pub struct Error(EntityApiError);

impl StdError for Error {}

Expand All @@ -27,24 +22,25 @@ impl std::fmt::Display for Error {

impl IntoResponse for Error {
fn into_response(self) -> Response {
match self {
Error::InternalServer => {
match self.0.error_type {
EntityApiErrorType::SystemError => {
(StatusCode::INTERNAL_SERVER_ERROR, "INTERNAL SERVER ERROR").into_response()
}
Error::EntityNotFound => (StatusCode::NO_CONTENT, "NO CONTENT").into_response(),
Error::UnprocessableEntity => {
EntityApiErrorType::RecordNotFound => {
(StatusCode::NO_CONTENT, "NO CONTENT").into_response()
}
EntityApiErrorType::RecordNotUpdated => {
(StatusCode::UNPROCESSABLE_ENTITY, "UNPROCESSABLE ENTITY").into_response()
}
}
}
}

impl From<EntityApiError> for Error {
fn from(err: EntityApiError) -> Self {
match err.error_type {
EntityApiErrorType::RecordNotFound => Error::EntityNotFound,
EntityApiErrorType::RecordNotUpdated => Error::UnprocessableEntity,
EntityApiErrorType::SystemError => Error::InternalServer,
}
impl<E> From<E> for Error
where
E: Into<EntityApiError>,
{
fn from(err: E) -> Self {
Self(err.into())
}
}

0 comments on commit b7e05d8

Please sign in to comment.