diff --git a/.sqlxconfig b/.sqlxconfig new file mode 100644 index 0000000..10ce579 --- /dev/null +++ b/.sqlxconfig @@ -0,0 +1,8 @@ +{ + "dev": { + "migrations": "./migrations" + }, + "test": { + "migrations": "./test_migrations" + } +} diff --git a/Cargo.lock b/Cargo.lock index bc99eb6..306c757 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -752,7 +752,7 @@ dependencies = [ [[package]] name = "icarus" -version = "0.1.93" +version = "0.1.94" dependencies = [ "axum", "common-multipart-rfc7578", diff --git a/Cargo.toml b/Cargo.toml index e88cf17..80dff0f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "icarus" -version = "0.1.93" +version = "0.1.94" edition = "2024" rust-version = "1.88" diff --git a/src/callers/mod.rs b/src/callers/mod.rs index ab4be4e..6e31042 100644 --- a/src/callers/mod.rs +++ b/src/callers/mod.rs @@ -16,6 +16,7 @@ pub mod endpoints { pub const QUEUECOVERARTDATAWIPE: &str = "/api/v2/coverart/queue/data/wipe"; pub const CREATESONG: &str = "/api/v2/song"; + pub const GETSONGS: &str = "/api/v2/song"; pub const CREATECOVERART: &str = "/api/v2/coverart"; } diff --git a/src/callers/song.rs b/src/callers/song.rs index 7d23857..b752536 100644 --- a/src/callers/song.rs +++ b/src/callers/song.rs @@ -92,6 +92,13 @@ pub mod request { pub user_id: uuid::Uuid, } } + + pub mod get_songs { + #[derive(Debug, Default, serde::Deserialize, serde::Serialize)] + pub struct Params { + pub id: Option, + } + } } pub mod response { @@ -158,6 +165,14 @@ pub mod response { pub data: Vec, } } + + pub mod get_songs { + #[derive(Debug, Default, serde::Deserialize, serde::Serialize)] + pub struct Response { + pub message: String, + pub data: Vec, + } + } } // TODO: Might make a distinction between year and date in a song's tag at some point @@ -968,4 +983,32 @@ pub mod endpoint { } } } + + pub async fn get_songs( + axum::Extension(pool): axum::Extension, + axum::extract::Query(params): axum::extract::Query, + ) -> ( + axum::http::StatusCode, + Json, + ) { + let mut response = super::response::get_songs::Response::default(); + + match params.id { + Some(id) => match super::song_db::get_song(&pool, &id).await { + Ok(song) => { + response.message = String::from(super::super::response::SUCCESSFUL); + response.data.push(song); + (axum::http::StatusCode::OK, axum::Json(response)) + } + Err(err) => { + response.message = err.to_string(); + (axum::http::StatusCode::BAD_REQUEST, axum::Json(response)) + } + }, + None => { + response.message = String::from("Invalid parameters"); + (axum::http::StatusCode::BAD_REQUEST, axum::Json(response)) + } + } + } } diff --git a/src/main.rs b/src/main.rs index ea6b0e0..a122e09 100644 --- a/src/main.rs +++ b/src/main.rs @@ -115,12 +115,17 @@ pub mod init { crate::callers::endpoints::CREATECOVERART, post(crate::callers::coverart::endpoint::create_coverart), ) + .route( + crate::callers::endpoints::GETSONGS, + get(crate::callers::song::endpoint::get_songs), + ) } pub async fn app() -> axum::Router { let pool = crate::db::create_pool() .await .expect("Failed to create pool"); + // TODO: Look into handling this. Seems redundant to run migrations multiple times crate::db::migrations(&pool).await; routes() @@ -227,6 +232,15 @@ mod tests { Err("Error parsing".into()) } } + + pub async fn migrations(pool: &sqlx::PgPool) { + // Run migrations using the sqlx::migrate! macro + // Assumes your test migrations are in a ./test_migrations folder relative to Cargo.toml + sqlx::migrate!("./test_migrations") + .run(pool) + .await + .expect("Failed to run migrations"); + } } mod init { @@ -247,7 +261,7 @@ mod tests { ) -> Result { // Create multipart form let mut form = MultipartForm::default(); - let _ = form.add_file("flac", "tests/IAmWe/track01.flac"); + let _ = form.add_file("flac", "tests/I/track01.flac"); // Create request let content_type = form.content_type(); @@ -331,7 +345,7 @@ mod tests { app: &axum::Router, ) -> Result { let mut form = MultipartForm::default(); - let _ = form.add_file("jpg", "tests/IAmWe/Coverart.jpg"); + let _ = form.add_file("jpg", "tests/I/Coverart.jpg"); // Create request let content_type = form.content_type(); @@ -1799,4 +1813,69 @@ mod tests { let _ = db_mgr::drop_database(&tm_pool, &db_name).await; } + + pub mod after_song_queue { + use tower::ServiceExt; + + #[tokio::test] + async fn test_get_songs() { + let tm_pool = super::db_mgr::get_pool().await.unwrap(); + let db_name = super::db_mgr::generate_db_name().await; + + match super::db_mgr::create_database(&tm_pool, &db_name).await { + Ok(_) => { + println!("Success"); + } + Err(err) => { + assert!(false, "Error: {:?}", err); + } + } + + let pool = super::db_mgr::connect_to_db(&db_name).await.unwrap(); + super::db_mgr::migrations(&pool).await; + + let app = super::init::app(pool).await; + + let mut id = uuid::Uuid::nil(); + match uuid::Uuid::parse_str("44cf7940-34ff-489f-9124-d0ec90a55af9") { + Ok(val) => { + id = val; + } + Err(err) => { + assert!(false, "Error: {err:?}"); + } + }; + + let uri = format!("{}?id={id}", crate::callers::endpoints::GETSONGS); + + match app + .clone() + .oneshot( + axum::http::Request::builder() + .method(axum::http::Method::GET) + .uri(uri) + .header(axum::http::header::CONTENT_TYPE, "application/json") + .body(axum::body::Body::empty()) + .unwrap(), + ) + .await + { + Ok(response) => { + let resp = super::get_resp_data::< + crate::callers::song::response::get_songs::Response, + >(response) + .await; + assert_eq!(false, resp.data.is_empty(), "Should not be empty"); + + let song = resp.data[0].clone(); + assert_eq!(id, song.id, "Id does not match {song:?}"); + } + Err(err) => { + assert!(false, "Error: {err:?}"); + } + } + + let _ = super::db_mgr::drop_database(&tm_pool, &db_name).await; + } + } } diff --git a/test_migrations/20250725213944_init.sql b/test_migrations/20250725213944_init.sql new file mode 100644 index 0000000..0c78fc4 --- /dev/null +++ b/test_migrations/20250725213944_init.sql @@ -0,0 +1,62 @@ +-- Add migration script here +CREATE EXTENSION IF NOT EXISTS pgcrypto; + +-- Table to store queued songs to process +CREATE TABLE IF NOT EXISTS "songQueue" ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + filename TEXT NOT NULL, + status TEXT CHECK (status IN ('pending', 'ready', 'processing', 'done')), + data BYTEA NULL, + user_id UUID NULL +); + +-- Table to store queued metadata +CREATE TABLE IF NOT EXISTS "metadataQueue" ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + metadata jsonb NOT NULL, + created_at timestamptz DEFAULT now(), + song_queue_id UUID NOT NULL +); + +-- Table to store queued coverart +CREATE TABLE IF NOT EXISTS "coverartQueue" ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + data BYTEA NULL, + song_queue_id UUID NULL +); + +-- Create an index for better query performance +CREATE INDEX metadata_queue_data_metadata ON "metadataQueue" USING gin (metadata); + +-- Table to store a song's info +CREATE TABLE IF NOT EXISTS "song" ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + title TEXT NOT NULL, + artist TEXT NOT NULL, + album_artist TEXT NOT NULL, + album TEXT NOT NULL, + genre TEXT NOT NULL, + year INT NOT NULL, + track INT NOT NULL, + disc INT NOT NULL, + track_count INT NOT NULL, + disc_count INT NOT NULL, + duration INT NOT NULL, + audio_type TEXT NOT NULL, + date_created timestamptz DEFAULT now(), + filename TEXT NOT NULL, + directory TEXT NOT NULL, + user_id UUID NULL +); + + +CREATE TABLE IF NOT EXISTS "coverart" ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + title TEXT NOT NULL, + path TEXT NOT NULL, + song_id UUID NOT NULL +); + +-- Might not to disable constraints on fields +-- INSERT INTO "song" (id, title, artist, album_artist, album, genre, year, track, disc, track_count, disc_count, duration, audio_type, date_created, filename, directory, user_id) VALUES('44cf7940-34ff-489f-9124-d0ec90a55af9', 'Hypocrite Like The Rest', 'Kuoth', 'Kuoth', 'I', 'Alternative Hip-Hop', 2020, 1, 1, 9, 1, 139, 'flac', '2020-01-01 13:00:00-05', 'track01.flac', 'tests/I', '47491f9b-725a-4ba4-b9a5-711e1be46670'); +-- Re-enable the constraints on the fields diff --git a/test_migrations/20250725214735_song_queue_processed.sql b/test_migrations/20250725214735_song_queue_processed.sql new file mode 100644 index 0000000..759da00 --- /dev/null +++ b/test_migrations/20250725214735_song_queue_processed.sql @@ -0,0 +1,3 @@ +-- Add migration script here +INSERT INTO "song" (id, title, artist, album_artist, album, genre, year, track, disc, track_count, disc_count, duration, audio_type, date_created, filename, directory, user_id) VALUES('44cf7940-34ff-489f-9124-d0ec90a55af9', 'Hypocrite Like The Rest', 'Kuoth', 'Kuoth', 'I', 'Alternative Hip-Hop', 2020, 1, 1, 9, 1, 139, 'flac', '2020-01-01 13:00:00-05', 'track01.flac', 'tests/I', '47491f9b-725a-4ba4-b9a5-711e1be46670'); +INSERT INTO "coverart" VALUES('996122cd-5ae9-4013-9934-60768d3006ed', 'I', 'tests/I/Coverart.jpg', '44cf7940-34ff-489f-9124-d0ec90a55af9'); diff --git a/tests/IAmWe/Coverart.jpg b/tests/I/Coverart.jpg similarity index 100% rename from tests/IAmWe/Coverart.jpg rename to tests/I/Coverart.jpg diff --git a/tests/IAmWe/album.json b/tests/I/album.json similarity index 98% rename from tests/IAmWe/album.json rename to tests/I/album.json index 0e97edf..65f9f6d 100644 --- a/tests/IAmWe/album.json +++ b/tests/I/album.json @@ -1,5 +1,5 @@ { - "album": "I Am We", + "album": "I", "album_artist": "Kuoth", "disc_count": 1, "genre": "Alternative Hip-Hop", diff --git a/tests/IAmWe/track01.flac b/tests/I/track01.flac similarity index 65% rename from tests/IAmWe/track01.flac rename to tests/I/track01.flac index 25fd5c6..7e59de7 100644 Binary files a/tests/IAmWe/track01.flac and b/tests/I/track01.flac differ diff --git a/tests/IAmWe/track02.flac b/tests/I/track02.flac similarity index 100% rename from tests/IAmWe/track02.flac rename to tests/I/track02.flac diff --git a/tests/IAmWe/track03.flac b/tests/I/track03.flac similarity index 100% rename from tests/IAmWe/track03.flac rename to tests/I/track03.flac diff --git a/tests/IAmWe/track04.flac b/tests/I/track04.flac similarity index 100% rename from tests/IAmWe/track04.flac rename to tests/I/track04.flac diff --git a/tests/IAmWe/track05.flac b/tests/I/track05.flac similarity index 100% rename from tests/IAmWe/track05.flac rename to tests/I/track05.flac diff --git a/tests/IAmWe/track06.flac b/tests/I/track06.flac similarity index 100% rename from tests/IAmWe/track06.flac rename to tests/I/track06.flac diff --git a/tests/IAmWe/track07.flac b/tests/I/track07.flac similarity index 100% rename from tests/IAmWe/track07.flac rename to tests/I/track07.flac diff --git a/tests/IAmWe/track08.flac b/tests/I/track08.flac similarity index 100% rename from tests/IAmWe/track08.flac rename to tests/I/track08.flac diff --git a/tests/IAmWe/track09.flac b/tests/I/track09.flac similarity index 100% rename from tests/IAmWe/track09.flac rename to tests/I/track09.flac