Get songs endpoint (#162)
* Added test_migrations directory * Added skeleton endpoint to get songs * Code formatting * Added more code * Added TODO * Code formatting * Updated album in album json file * Changing directory name * Test refactoring * Forgot to include this * Added test for getting songs and added function for test_migrations * Adding test migrations * Removing placeholder * Renamed * Renamed * Fixed what caused test failure * Created migration with cli command Creating test_migrations/20250725213448_migration_name.sql * Migration changes * Removing migration * More migration changes * Made endpoint available * Migration changes * Got the test to work * Cleaned up test * Code formatting * More cleanup * Version bump
This commit was merged in pull request #162.
This commit is contained in:
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"dev": {
|
||||
"migrations": "./migrations"
|
||||
},
|
||||
"test": {
|
||||
"migrations": "./test_migrations"
|
||||
}
|
||||
}
|
||||
Generated
+1
-1
@@ -752,7 +752,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "icarus"
|
||||
version = "0.1.93"
|
||||
version = "0.1.94"
|
||||
dependencies = [
|
||||
"axum",
|
||||
"common-multipart-rfc7578",
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "icarus"
|
||||
version = "0.1.93"
|
||||
version = "0.1.94"
|
||||
edition = "2024"
|
||||
rust-version = "1.88"
|
||||
|
||||
|
||||
@@ -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";
|
||||
}
|
||||
|
||||
|
||||
@@ -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<uuid::Uuid>,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub mod response {
|
||||
@@ -158,6 +165,14 @@ pub mod response {
|
||||
pub data: Vec<uuid::Uuid>,
|
||||
}
|
||||
}
|
||||
|
||||
pub mod get_songs {
|
||||
#[derive(Debug, Default, serde::Deserialize, serde::Serialize)]
|
||||
pub struct Response {
|
||||
pub message: String,
|
||||
pub data: Vec<icarus_models::song::Song>,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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<sqlx::PgPool>,
|
||||
axum::extract::Query(params): axum::extract::Query<super::request::get_songs::Params>,
|
||||
) -> (
|
||||
axum::http::StatusCode,
|
||||
Json<super::response::get_songs::Response>,
|
||||
) {
|
||||
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))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+81
-2
@@ -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<axum::response::Response, std::convert::Infallible> {
|
||||
// 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<axum::response::Response, std::convert::Infallible> {
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -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');
|
||||
|
Before Width: | Height: | Size: 6.7 MiB After Width: | Height: | Size: 6.7 MiB |
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"album": "I Am We",
|
||||
"album": "I",
|
||||
"album_artist": "Kuoth",
|
||||
"disc_count": 1,
|
||||
"genre": "Alternative Hip-Hop",
|
||||
Binary file not shown.
Reference in New Issue
Block a user