Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 36 additions & 1 deletion Cargo.lock

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

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -47,4 +47,4 @@ tower-http = { version = "0.6.6", features = ["trace", "cors"] }
tracing = "0.1.41"
tracing-subscriber = { version = "0.3.20", features = ["env-filter", "fmt"] }
usecase = { path = "crates/usecase" }
uuid = "1.18.1"
uuid = { version = "1.18.1", features = ["v4"] }
10 changes: 10 additions & 0 deletions crates/domain/src/repository/music.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,4 +54,14 @@ pub trait MusicRepository: Send + Sync {
&self,
music_id: &str,
) -> impl Future<Output = Result<MusicWithSheets, MusicRepositoryError>> + Send;

fn insert_with_sheets(
&self,
music: MusicWithSheets,
) -> impl Future<Output = Result<MusicWithSheets, MusicRepositoryError>> + Send;

fn update_with_sheets(
&self,
music: MusicWithSheets,
) -> impl Future<Output = Result<MusicWithSheets, MusicRepositoryError>> + Send;
}
20 changes: 19 additions & 1 deletion crates/infrastructure/src/music/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
mod adapter;
mod read;
mod read_adapter;
mod write;
mod write_adapter;

use std::sync::Arc;

Expand Down Expand Up @@ -46,4 +48,20 @@ impl MusicRepository for MusicRepositoryImpl {
debug!("Loading music metadata by id via SeaORM");
read::find_with_sheets(self.db.as_ref(), music_id).await
}

#[instrument(skip(self), fields(music_id = %music.music.id()))]
async fn insert_with_sheets(
&self,
music: MusicWithSheets,
) -> Result<MusicWithSheets, MusicRepositoryError> {
write::insert_with_sheets(self.db.as_ref(), music).await
}

#[instrument(skip(self), fields(music_id = %music.music.id()))]
async fn update_with_sheets(
&self,
music: MusicWithSheets,
) -> Result<MusicWithSheets, MusicRepositoryError> {
write::update_with_sheets(self.db.as_ref(), music).await
}
}
14 changes: 7 additions & 7 deletions crates/infrastructure/src/music/read.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use domain::repository::music::{
use sea_orm::{ColumnTrait, Condition, DbConn, EntityTrait, QueryFilter, QueryOrder, QuerySelect};
use tracing::{debug, error};

use super::adapter;
use super::read_adapter;
use crate::entities;

/// Collects every music alongside its sheets.
Expand All @@ -28,8 +28,8 @@ pub async fn list_with_sheets(db: &DbConn) -> Result<Vec<MusicWithSheets>, Music

let mut musics = Vec::with_capacity(models.len());
for (music_model, sheet_models) in models {
let music = adapter::convert_music(music_model)?;
let sheets = adapter::convert_sheets(sheet_models)?;
let music = read_adapter::convert_music(music_model)?;
let sheets = read_adapter::convert_sheets(sheet_models)?;
musics.push(MusicWithSheets::new(music, sheets));
}

Expand Down Expand Up @@ -88,8 +88,8 @@ pub async fn list_with_sheets_page(

let mut items = Vec::with_capacity(models.len());
for (music_model, sheet_models) in models {
let music = adapter::convert_music(music_model)?;
let sheets = adapter::convert_sheets(sheet_models)?;
let music = read_adapter::convert_music(music_model)?;
let sheets = read_adapter::convert_sheets(sheet_models)?;
items.push(MusicWithSheets::new(music, sheets));
}

Expand Down Expand Up @@ -119,7 +119,7 @@ pub async fn find_with_sheets(
};

Ok(MusicWithSheets::new(
adapter::convert_music(music_model)?,
adapter::convert_sheets(sheet_models)?,
read_adapter::convert_music(music_model)?,
read_adapter::convert_sheets(sheet_models)?,
))
}
89 changes: 89 additions & 0 deletions crates/infrastructure/src/music/write.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
use anyhow::Error as AnyError;
use domain::repository::music::{MusicRepositoryError, MusicWithSheets};
use sea_orm::{ActiveModelTrait, ColumnTrait, DbConn, EntityTrait, QueryFilter, TransactionTrait};
use tracing::error;

use super::write_adapter::{
music_active_model_for_insert, music_active_model_for_update, sheet_active_model_for_insert,
sheet_active_model_for_update,
};
use crate::entities;

pub async fn insert_with_sheets(
db: &DbConn,
music: MusicWithSheets,
) -> Result<MusicWithSheets, MusicRepositoryError> {
let txn = db.begin().await.map_err(internal)?;
let music_model = music_active_model_for_insert(&music.music)?;
let result = async {
music_model.insert(&txn).await.map_err(internal)?;
for sheet in &music.sheets {
sheet_active_model_for_insert(sheet)?
.insert(&txn)
.await
.map_err(internal)?;
}
Ok::<_, MusicRepositoryError>(())
}
.await;
if let Err(error) = result {
let _ = txn.rollback().await;
return Err(error);
}
txn.commit().await.map_err(internal)?;
Ok(music)
}

pub async fn update_with_sheets(
db: &DbConn,
music: MusicWithSheets,
) -> Result<MusicWithSheets, MusicRepositoryError> {
let txn = db.begin().await.map_err(internal)?;
let music_id = uuid::Uuid::parse_str(music.music.id())
.map_err(|error| MusicRepositoryError::InternalError(AnyError::from(error)))?;
let result = async {
let existing = entities::sheets::Entity::find()
.filter(entities::sheets::Column::MusicId.eq(music_id))
.all(&txn)
.await
.map_err(internal)?;
let existing_ids: std::collections::HashSet<_> =
existing.iter().map(|sheet| sheet.id).collect();
let requested_ids: std::collections::HashSet<_> = music
.sheets
.iter()
.map(|sheet| {
uuid::Uuid::parse_str(sheet.id())
.map_err(|error| MusicRepositoryError::InternalError(AnyError::from(error)))
})
.collect::<Result<_, _>>()?;
if existing_ids != requested_ids || existing.len() != 3 {
return Err(MusicRepositoryError::InternalError(AnyError::msg(
"music must have exactly three existing sheets",
)));
}
music_active_model_for_update(&music.music)?
.update(&txn)
.await
.map_err(internal)?;
for sheet in &music.sheets {
sheet_active_model_for_update(sheet)?
.update(&txn)
.await
.map_err(internal)?;
}
Ok::<_, MusicRepositoryError>(())
}
.await;
if let Err(error) = result {
let _ = txn.rollback().await;
return Err(error);
}
txn.commit().await.map_err(internal)?;
Ok(music)
}

fn internal(error: sea_orm::DbErr) -> MusicRepositoryError {
error!(error = %error, "Failed to write music metadata");
MusicRepositoryError::InternalError(AnyError::from(error))
}
Loading
Loading