Skip to content

Commit

Permalink
feat: Add update-item (#58)
Browse files Browse the repository at this point in the history
  • Loading branch information
szinn authored Aug 19, 2024
1 parent b463de7 commit 638b83c
Show file tree
Hide file tree
Showing 6 changed files with 83 additions and 9 deletions.
34 changes: 29 additions & 5 deletions crates/arch-api/src/http/v1/items.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
use std::{sync::Arc, time::Duration};

use arch_domain_api::{ArchApi, ItemApi};
use arch_domain_models::item::NewItem;
use arch_domain_models::item::{NewItem, UpdateItem};
use arch_utils::arcbox::ArcBox;
use axum::{
extract::{Path, State},
routing::{get, post},
routing::{get, patch, post},
Json, Router,
};
use hyper::StatusCode;
Expand All @@ -22,19 +22,26 @@ struct Item {
}

#[derive(Deserialize)]
struct ItemParams {
struct NewItemParams {
text: String,
}

#[derive(Debug, Deserialize)]
struct UpdateItemParams {
version: i32,
text: String,
}

pub(crate) fn get_routes(arch_api: Arc<ArchApi>) -> Router<()> {
Router::new()
.route("/:uuid", get(get_item))
.route("/", post(create_item))
.route("/:uuid", patch(update_item))
.with_state(arch_api.item_api.clone())
.layer(TimeoutLayer::new(Duration::from_secs(2)))
}

#[tracing::instrument(level = "trace", skip(item_api, uuid))]
#[tracing::instrument(level = "trace", skip(item_api))]
async fn get_item(State(item_api): State<ArcBox<dyn ItemApi>>, Path(uuid): Path<Uuid>) -> Result<Json<Item>, StatusCode> {
match item_api.get_item(&uuid).await {
Err(_) => Err(StatusCode::BAD_REQUEST),
Expand All @@ -44,7 +51,7 @@ async fn get_item(State(item_api): State<ArcBox<dyn ItemApi>>, Path(uuid): Path<
}

#[tracing::instrument(level = "trace", skip(item_api, params))]
async fn create_item(State(item_api): State<ArcBox<dyn ItemApi>>, Json(params): Json<ItemParams>) -> Result<Json<Item>, StatusCode> {
async fn create_item(State(item_api): State<ArcBox<dyn ItemApi>>, Json(params): Json<NewItemParams>) -> Result<Json<Item>, StatusCode> {
let new_item = NewItem { text: params.text };

match item_api.create_item(&new_item).await {
Expand All @@ -53,6 +60,23 @@ async fn create_item(State(item_api): State<ArcBox<dyn ItemApi>>, Json(params):
}
}

#[tracing::instrument(level = "trace", skip(item_api))]
async fn update_item(
State(item_api): State<ArcBox<dyn ItemApi>>,
Path(uuid): Path<Uuid>,
Json(params): Json<UpdateItemParams>,
) -> Result<Json<Item>, StatusCode> {
let update_item = UpdateItem {
version: params.version,
text: params.text,
};

match item_api.update_item(&uuid, &update_item).await {
Ok(item) => Ok(Json(item.into())),
Err(_) => Err(StatusCode::BAD_REQUEST),
}
}

impl From<arch_domain_models::item::Item> for Item {
fn from(value: arch_domain_models::item::Item) -> Self {
Item {
Expand Down
3 changes: 2 additions & 1 deletion crates/arch-db/src/adapters.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use arch_domain_models::item::{Item, NewItem};
use arch_domain_models::item::{Item, NewItem, UpdateItem};
use sea_orm::prelude::Uuid;
use sea_orm_migration::async_trait::async_trait;

Expand All @@ -10,4 +10,5 @@ pub(crate) mod item;
pub trait ItemAdapter: Send + Sync {
async fn create_item(&self, new_item: &NewItem) -> Result<Item, Error>;
async fn get_item(&self, uuid: &Uuid) -> Result<Option<Item>, Error>;
async fn update_item(&self, uuid: &Uuid, update_item: &UpdateItem) -> Result<Item, Error>;
}
36 changes: 35 additions & 1 deletion crates/arch-db/src/adapters/item.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use std::sync::Arc;

use arch_domain_models::item::{Item, NewItem};
use arch_domain_models::item::{Item, NewItem, UpdateItem};
use sea_orm::{prelude::Uuid, ActiveModelTrait, ColumnTrait, EntityTrait, QueryFilter, Set, TransactionTrait};
use sea_orm_migration::async_trait::async_trait;

Expand Down Expand Up @@ -60,4 +60,38 @@ impl ItemAdapter for ItemAdapterImpl {
Some(model) => Ok(Some(ItemAdapterImpl::from_model(model))),
}
}

#[tracing::instrument(level = "trace", skip(self))]
async fn update_item(&self, uuid: &Uuid, update_item: &UpdateItem) -> Result<Item, Error> {
let tx = self.repository.database.begin().await?;

let model = prelude::Items::find().filter(items::Column::Uuid.eq(*uuid)).one(&tx).await?;

let model = match model {
Some(model) => model,
None => return Err(Error::SeaOrm(sea_orm::DbErr::RecordNotFound(uuid.to_string()))),
};

if model.version != update_item.version {
return Err(Error::SeaOrm(sea_orm::DbErr::RecordNotUpdated));
}

let new_version = model.version + 1;
let mut new_model: items::ActiveModel = model.into();
new_model.text = Set(update_item.text.clone());
new_model.version = Set(new_version);

new_model.update(&tx).await?;

let model = prelude::Items::find().filter(items::Column::Uuid.eq(*uuid)).one(&tx).await?;

let item = match model {
None => return Err(Error::SeaOrm(sea_orm::DbErr::RecordNotFound(uuid.to_string()))),
Some(model) => ItemAdapterImpl::from_model(model),
};

tx.commit().await?;

Ok(item)
}
}
3 changes: 2 additions & 1 deletion crates/arch-domain-api/src/item.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use arch_db::sea_orm::prelude::Uuid;
use arch_domain_models::item::{Item, NewItem};
use arch_domain_models::item::{Item, NewItem, UpdateItem};
use async_trait::async_trait;

use crate::Error;
Expand All @@ -8,4 +8,5 @@ use crate::Error;
pub trait ItemApi: Send + Sync {
async fn create_item(&self, new_item: &NewItem) -> Result<Item, Error>;
async fn get_item(&self, uuid: &Uuid) -> Result<Option<Item>, Error>;
async fn update_item(&self, uuid: &Uuid, update_item: &UpdateItem) -> Result<Item, Error>;
}
10 changes: 9 additions & 1 deletion crates/arch-domain-core/src/item.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use arch_db::{adapters::ItemAdapter, sea_orm::prelude::Uuid};
use arch_domain_api::{Error, ItemApi};
use arch_domain_models::item::{Item, NewItem};
use arch_domain_models::item::{Item, NewItem, UpdateItem};
use arch_utils::arcbox::ArcBox;
use async_trait::async_trait;

Expand Down Expand Up @@ -32,4 +32,12 @@ impl ItemApi for ItemService {
Ok(item) => Ok(item),
}
}

#[tracing::instrument(level = "trace", skip(self))]
async fn update_item(&self, uuid: &Uuid, update_item: &UpdateItem) -> Result<Item, Error> {
match self.repository.update_item(uuid, update_item).await {
Err(err) => Err(Error::DatabaseError(err)),
Ok(item) => Ok(item),
}
}
}
6 changes: 6 additions & 0 deletions crates/arch-domain-models/src/item.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,12 @@ pub struct NewItem {
pub text: String,
}

#[derive(Debug)]
pub struct UpdateItem {
pub version: i32,
pub text: String,
}

#[derive(Debug)]
pub struct Item {
pub id: i64,
Expand Down

0 comments on commit 638b83c

Please sign in to comment.