tsk-218: Separate Song and SongQueue in the endpoints #221

Merged
kdeng00 merged 6 commits from tsk-218 into main 2025-10-26 17:48:18 -04:00
8 changed files with 566 additions and 531 deletions
Generated
+1 -1
View File
@@ -834,7 +834,7 @@ dependencies = [
[[package]] [[package]]
name = "icarus" name = "icarus"
version = "0.3.6" version = "0.3.7"
dependencies = [ dependencies = [
"axum", "axum",
"axum-extra", "axum-extra",
+1 -1
View File
@@ -1,6 +1,6 @@
[package] [package]
name = "icarus" name = "icarus"
version = "0.3.6" version = "0.3.7"
edition = "2024" edition = "2024"
rust-version = "1.90" rust-version = "1.90"
-10
View File
@@ -4,17 +4,7 @@ pub mod queue;
pub mod song; pub mod song;
pub mod endpoints { pub mod endpoints {
pub const QUEUESONG: &str = "/api/v2/song/queue";
pub const QUEUESONGLINKUSERID: &str = "/api/v2/song/queue/link";
pub const QUEUESONGDATA: &str = "/api/v2/song/queue/{id}";
pub const QUEUESONGUPDATE: &str = "/api/v2/song/queue/{id}";
pub const NEXTQUEUESONG: &str = "/api/v2/song/queue/next";
pub const QUEUEMETADATA: &str = "/api/v2/song/metadata/queue"; pub const QUEUEMETADATA: &str = "/api/v2/song/metadata/queue";
pub const QUEUECOVERART: &str = "/api/v2/coverart/queue";
pub const QUEUECOVERARTDATA: &str = "/api/v2/coverart/queue/data/{id}";
pub const QUEUECOVERARTLINK: &str = "/api/v2/coverart/queue/link";
pub const QUEUESONGDATAWIPE: &str = "/api/v2/song/queue/data/wipe";
pub const QUEUECOVERARTDATAWIPE: &str = "/api/v2/coverart/queue/data/wipe";
pub const CREATESONG: &str = "/api/v2/song"; pub const CREATESONG: &str = "/api/v2/song";
pub const GETSONGS: &str = "/api/v2/song"; pub const GETSONGS: &str = "/api/v2/song";
+5 -5
View File
@@ -125,7 +125,7 @@ pub mod endpoint {
/// Endpoint to queue cover art /// Endpoint to queue cover art
#[utoipa::path( #[utoipa::path(
post, post,
path = super::super::super::endpoints::QUEUECOVERART, path = super::super::endpoints::QUEUECOVERART,
request_body( request_body(
content = super::request::queue::Request, content = super::request::queue::Request,
), ),
@@ -203,7 +203,7 @@ pub mod endpoint {
/// Endpoint to link queued cover art /// Endpoint to link queued cover art
#[utoipa::path( #[utoipa::path(
post, post,
path = super::super::super::endpoints::QUEUECOVERARTLINK, path = super::super::endpoints::QUEUECOVERARTLINK,
request_body( request_body(
content = super::request::link::Request, content = super::request::link::Request,
description = "Linking queued cover art to queued song", description = "Linking queued cover art to queued song",
@@ -244,7 +244,7 @@ pub mod endpoint {
/// Endpoint to fetch cover art details /// Endpoint to fetch cover art details
#[utoipa::path( #[utoipa::path(
get, get,
path = super::super::super::endpoints::QUEUECOVERART, path = super::super::endpoints::QUEUECOVERART,
params( params(
("id" = uuid::Uuid, Path, description = "Queued cover art Id"), ("id" = uuid::Uuid, Path, description = "Queued cover art Id"),
("song_queue_id" = uuid::Uuid, Path, description = "Queued song Id") ("song_queue_id" = uuid::Uuid, Path, description = "Queued song Id")
@@ -305,7 +305,7 @@ pub mod endpoint {
/// Endpoint to fetch the queued cover art data /// Endpoint to fetch the queued cover art data
#[utoipa::path( #[utoipa::path(
get, get,
path = super::super::super::endpoints::QUEUECOVERARTDATA, path = super::super::endpoints::QUEUECOVERARTDATA,
params(("id" = uuid::Uuid, Path, description = "Queued cover art Id")), params(("id" = uuid::Uuid, Path, description = "Queued cover art Id")),
responses( responses(
(status = 200, description = "Queued cover art data", body = Vec<u8>), (status = 200, description = "Queued cover art data", body = Vec<u8>),
@@ -343,7 +343,7 @@ pub mod endpoint {
/// Endpoint to wipe data from the cover art queue /// Endpoint to wipe data from the cover art queue
#[utoipa::path( #[utoipa::path(
patch, patch,
path = super::super::super::endpoints::QUEUECOVERARTDATAWIPE, path = super::super::endpoints::QUEUECOVERARTDATAWIPE,
request_body( request_body(
content = super::request::wipe_data_from_coverart_queue::Request, content = super::request::wipe_data_from_coverart_queue::Request,
description = "Data required to wipe the data from the cover art queue", description = "Data required to wipe the data from the cover art queue",
+14
View File
@@ -1 +1,15 @@
pub mod coverart; pub mod coverart;
pub mod song;
pub mod endpoints {
pub const QUEUESONG: &str = "/api/v2/song/queue";
pub const QUEUESONGLINKUSERID: &str = "/api/v2/song/queue/link";
pub const QUEUESONGDATA: &str = "/api/v2/song/queue/{id}";
pub const QUEUESONGUPDATE: &str = "/api/v2/song/queue/{id}";
pub const NEXTQUEUESONG: &str = "/api/v2/song/queue/next";
pub const QUEUECOVERART: &str = "/api/v2/coverart/queue";
pub const QUEUECOVERARTDATA: &str = "/api/v2/coverart/queue/data/{id}";
pub const QUEUECOVERARTLINK: &str = "/api/v2/coverart/queue/link";
pub const QUEUESONGDATAWIPE: &str = "/api/v2/song/queue/data/wipe";
pub const QUEUECOVERARTDATAWIPE: &str = "/api/v2/coverart/queue/data/wipe";
}
+446
View File
@@ -0,0 +1,446 @@
pub mod request {
pub mod song_queue {
#[derive(utoipa::ToSchema)]
pub struct SongQueueRequest {
/// Filename
pub file: String,
#[schema(rename = "type")]
/// File type. Should be a file and not a value
pub file_type: String,
/// Raw data of the flac file
pub value: Vec<u8>,
}
}
pub mod update_status {
#[derive(Default, serde::Deserialize, serde::Serialize, utoipa::ToSchema)]
pub struct Request {
pub id: uuid::Uuid,
pub status: String,
}
}
pub mod wipe_data_from_song_queue {
#[derive(Debug, serde::Deserialize, serde::Serialize, utoipa::ToSchema)]
pub struct Request {
pub song_queue_id: uuid::Uuid,
}
}
pub mod link_user_id {
#[derive(Debug, serde::Deserialize, serde::Serialize, utoipa::ToSchema)]
pub struct Request {
pub song_queue_id: uuid::Uuid,
pub user_id: uuid::Uuid,
}
}
}
pub mod response {
pub mod song_queue {
/// Song queue response
#[derive(Default, serde::Deserialize, serde::Serialize, utoipa::ToSchema)]
pub struct Response {
pub message: String,
/// Id of the queued song
pub data: Vec<uuid::Uuid>,
}
}
pub mod fetch_queue_song {
use serde::{Deserialize, Serialize};
#[derive(Default, Deserialize, Serialize, utoipa::ToSchema)]
pub struct Response {
pub message: String,
pub data: Vec<crate::repo::queue::song::SongQueue>,
}
}
pub mod update_status {
#[derive(serde::Deserialize, serde::Serialize, utoipa::ToSchema)]
pub struct ChangedStatus {
pub old_status: String,
pub new_status: String,
}
#[derive(Default, serde::Deserialize, serde::Serialize, utoipa::ToSchema)]
pub struct Response {
pub message: String,
pub data: Vec<ChangedStatus>,
}
}
pub mod update_song_queue {
#[derive(Default, serde::Deserialize, serde::Serialize, utoipa::ToSchema)]
pub struct Response {
pub message: String,
pub data: Vec<uuid::Uuid>,
}
}
pub mod wipe_data_from_song_queue {
#[derive(Debug, Default, serde::Deserialize, serde::Serialize, utoipa::ToSchema)]
pub struct Response {
pub message: String,
pub data: Vec<uuid::Uuid>,
}
}
pub mod link_user_id {
#[derive(Debug, Default, serde::Deserialize, serde::Serialize, utoipa::ToSchema)]
pub struct Response {
pub message: String,
pub data: Vec<uuid::Uuid>,
}
}
}
pub mod endpoint {
use axum::response::IntoResponse;
use crate::repo::queue as repo;
/// Endpoint to queue a song. Starts the process and places the song in a queue
#[utoipa::path(
post,
path = super::super::endpoints::QUEUESONG,
request_body(
content = super::request::song_queue::SongQueueRequest,
description = "Multipart form data for uploading song",
content_type = "multipart/form-data"
),
responses(
(status = 200, description = "Song queued", body = super::response::song_queue::Response)
)
)]
pub async fn queue_song(
axum::Extension(pool): axum::Extension<sqlx::PgPool>,
mut multipart: axum::extract::Multipart,
) -> (
axum::http::StatusCode,
axum::Json<super::response::song_queue::Response>,
) {
let mut results: Vec<uuid::Uuid> = Vec::new();
let mut response = super::response::song_queue::Response::default();
while let Some(field) = multipart.next_field().await.unwrap() {
let name = field.name().unwrap().to_string();
let file_name = field.file_name().unwrap().to_string();
let content_type = field.content_type().unwrap().to_string();
let data = field.bytes().await.unwrap();
println!(
"Received file '{}' (name = '{}', content-type = '{}', size = {})",
file_name,
name,
content_type,
data.len()
);
let raw_data: Vec<u8> = data.to_vec();
let queue_repo = repo::song::insert(
&pool,
&raw_data,
&file_name,
&crate::repo::queue::song::status::PENDING.to_string(),
)
.await
.unwrap();
results.push(queue_repo);
}
response.data = results;
response.message = if response.data.is_empty() {
String::from("Error")
} else {
String::from("Success")
};
(axum::http::StatusCode::OK, axum::Json(response))
}
/// Endpoint to link a user id to a queued song
#[utoipa::path(
patch,
path = super::super::endpoints::QUEUESONGLINKUSERID,
request_body(
content = super::request::link_user_id::Request,
description = "User Id and queued song id",
content_type = "application/json"
),
responses(
(status = 200, description = "Queued song linked", body = super::response::link_user_id::Response),
(status = 400, description = "Linkage failed", body = super::response::link_user_id::Response)
)
)]
pub async fn link_user_id(
axum::Extension(pool): axum::Extension<sqlx::PgPool>,
axum::Json(payload): axum::Json<super::request::link_user_id::Request>,
) -> (
axum::http::StatusCode,
axum::Json<super::response::link_user_id::Response>,
) {
let mut response = super::response::link_user_id::Response::default();
match repo::song::get_song_queue(&pool, &payload.song_queue_id).await {
Ok(song_queue) => {
match repo::song::link_user_id(&pool, &song_queue.id, &payload.user_id).await {
Ok(user_id) => {
response.message = String::from(crate::callers::response::SUCCESSFUL);
response.data.push(user_id);
(axum::http::StatusCode::OK, axum::Json(response))
}
Err(err) => {
response.message = err.to_string();
(axum::http::StatusCode::BAD_REQUEST, axum::Json(response))
}
}
}
Err(err) => {
response.message = err.to_string();
(axum::http::StatusCode::BAD_REQUEST, axum::Json(response))
}
}
}
/// Endpoint to fetch the next queued song as long as it is available
#[utoipa::path(
get,
path = super::super::endpoints::NEXTQUEUESONG,
responses(
(status = 200, description = "Queued song is present and available", body = super::response::fetch_queue_song::Response),
(status = 400, description = "Linkage failed", body = super::response::fetch_queue_song::Response)
)
)]
pub async fn fetch_queue_song(
axum::Extension(pool): axum::Extension<sqlx::PgPool>,
) -> (
axum::http::StatusCode,
axum::Json<super::response::fetch_queue_song::Response>,
) {
let mut response = super::response::fetch_queue_song::Response::default();
match repo::song::get_most_recent_and_update(&pool).await {
Ok(item) => {
response.message = String::from("Successful");
response.data.push(item);
(axum::http::StatusCode::OK, axum::Json(response))
}
Err(err) => {
response.message = err.to_string();
(axum::http::StatusCode::BAD_REQUEST, axum::Json(response))
}
}
}
// TODO: Rename
/// Endpoint to download the queued song
#[utoipa::path(
get,
path = super::super::endpoints::QUEUESONGDATA,
params(("id" = uuid::Uuid, Path, description = "Queued song Id")),
responses(
(status = 200, description = "Queued song linked", body = Vec<u8>),
(status = 400, description = "Linkage failed", body = Vec<u8>)
)
)]
pub async fn download_queued_song(
axum::Extension(pool): axum::Extension<sqlx::PgPool>,
axum::extract::Path(id): axum::extract::Path<uuid::Uuid>,
) -> (axum::http::StatusCode, axum::response::Response) {
println!("Id: {id}");
match repo::song::get_data(&pool, &id).await {
Ok(data) => {
let by = axum::body::Bytes::from(data);
let mut response = by.into_response();
let headers = response.headers_mut();
headers.insert(
axum::http::header::CONTENT_TYPE,
"audio/flac".parse().unwrap(),
);
headers.insert(
axum::http::header::CONTENT_DISPOSITION,
format!("attachment; filename=\"{id}.flac\"")
.parse()
.unwrap(),
);
(axum::http::StatusCode::OK, response)
}
Err(_err) => (
axum::http::StatusCode::BAD_REQUEST,
axum::response::Response::default(),
),
}
}
/// Endpoint to update the status of a queued song
#[utoipa::path(
patch,
path = super::super::endpoints::QUEUESONG,
request_body(
content = super::request::update_status::Request,
description = "Update the status of a queued song",
content_type = "application/json"
),
responses(
(status = 200, description = "Status has been updated", body = super::response::update_status::Response),
(status = 400, description = "Error updating status of queued song", body = super::response::update_status::Response)
)
)]
pub async fn update_song_queue_status(
axum::Extension(pool): axum::Extension<sqlx::PgPool>,
axum::Json(payload): axum::Json<super::request::update_status::Request>,
) -> (
axum::http::StatusCode,
axum::Json<super::response::update_status::Response>,
) {
let mut response = super::response::update_status::Response::default();
if repo::song::status::is_valid(&payload.status).await {
let id = payload.id;
if !id.is_nil() {
match repo::song::get_status_of_song_queue(&pool, &id).await {
Ok(old) => {
match repo::song::update_song_queue_status(&pool, &payload.status, &id)
.await
{
Ok(new) => {
response.message = String::from("Successful");
response
.data
.push(super::response::update_status::ChangedStatus {
old_status: old,
new_status: new,
});
(axum::http::StatusCode::OK, axum::Json(response))
}
Err(err) => {
response.message = err.to_string();
(axum::http::StatusCode::BAD_REQUEST, axum::Json(response))
}
}
}
Err(err) => {
response.message = err.to_string();
(axum::http::StatusCode::BAD_REQUEST, axum::Json(response))
}
}
} else {
response.message = String::from("Id is nil");
(axum::http::StatusCode::BAD_REQUEST, axum::Json(response))
}
} else {
response.message = String::from("Status not valid");
(axum::http::StatusCode::BAD_REQUEST, axum::Json(response))
}
}
/// Endpoint to update the queued song data
#[utoipa::path(
patch,
path = super::super::endpoints::QUEUESONGUPDATE,
request_body(
content = super::request::song_queue::SongQueueRequest,
description = "Multipart form data for uploading song",
content_type = "multipart/form-data"
),
params(("id" = uuid::Uuid, Path, description = "Queued song Id")),
responses(
(status = 200, description = "Queued song updated", body = super::response::update_song_queue::Response),
(status = 400, description = "Error updating queued song", body = super::response::update_song_queue::Response),
(status = 404, description = "Queued song not found", body = super::response::update_song_queue::Response)
)
)]
pub async fn update_song_queue(
axum::extract::Path(id): axum::extract::Path<uuid::Uuid>,
axum::Extension(pool): axum::Extension<sqlx::PgPool>,
mut multipart: axum::extract::Multipart,
) -> (
axum::http::StatusCode,
axum::Json<super::response::update_song_queue::Response>,
) {
let mut response = super::response::update_song_queue::Response::default();
if let Some(field) = multipart.next_field().await.unwrap() {
let name = field.name().unwrap().to_string();
let file_name = field.file_name().unwrap().to_string();
let content_type = match field.content_type() {
Some(ct) => ct.to_string(),
None => String::new(),
};
let data = field.bytes().await.unwrap();
println!(
"Received file '{}' (name = '{}', content-type = '{}', size = {})",
file_name,
name,
content_type,
data.len()
);
let raw_data: Vec<u8> = data.to_vec();
match repo::song::update(&pool, &raw_data, &id).await {
Ok(_) => {
response.message = String::from("Successful");
response.data.push(id);
(axum::http::StatusCode::OK, axum::Json(response))
}
Err(err) => {
response.message = err.to_string();
(axum::http::StatusCode::BAD_REQUEST, axum::Json(response))
}
}
} else {
response.message = String::from("No data provided");
(axum::http::StatusCode::NOT_FOUND, axum::Json(response))
}
}
/// Endpoint to wipe the data from a queued song
#[utoipa::path(
patch,
path = super::super::endpoints::QUEUESONGDATAWIPE,
request_body(
content = super::request::wipe_data_from_song_queue::Request,
description = "Pass the queued song Id to wipe the data",
content_type = "application/json"
),
responses(
(status = 200, description = "Queued song data wiped", body = super::response::wipe_data_from_song_queue::Response),
(status = 404, description = "Queued song cannot be found", body = super::response::wipe_data_from_song_queue::Response)
)
)]
pub async fn wipe_data_from_song_queue(
axum::Extension(pool): axum::Extension<sqlx::PgPool>,
axum::Json(payload): axum::Json<super::request::wipe_data_from_song_queue::Request>,
) -> (
axum::http::StatusCode,
axum::Json<super::response::wipe_data_from_song_queue::Response>,
) {
let mut response = super::response::wipe_data_from_song_queue::Response::default();
let id = payload.song_queue_id;
match repo::song::get_song_queue(&pool, &id).await {
Ok(song_queue) => match repo::song::wipe_data(&pool, &song_queue.id).await {
Ok(wiped_id) => {
response.message = String::from("Success");
response.data.push(wiped_id);
(axum::http::StatusCode::OK, axum::Json(response))
}
Err(err) => {
response.message = err.to_string();
(axum::http::StatusCode::NOT_FOUND, axum::Json(response))
}
},
Err(err) => {
response.message = err.to_string();
(axum::http::StatusCode::NOT_FOUND, axum::Json(response))
}
}
}
}
+1 -446
View File
@@ -1,33 +1,4 @@
// TODO: Separate queue and song endpoints
pub mod request { pub mod request {
use serde::{Deserialize, Serialize};
#[derive(Default, Deserialize, Serialize)]
pub struct Request {
pub message: String,
}
pub mod song_queue {
#[derive(utoipa::ToSchema)]
pub struct SongQueueRequest {
/// Filename
pub file: String,
#[schema(rename = "type")]
/// File type. Should be a file and not a value
pub file_type: String,
/// Raw data of the flac file
pub value: Vec<u8>,
}
}
pub mod update_status {
#[derive(Default, serde::Deserialize, serde::Serialize, utoipa::ToSchema)]
pub struct Request {
pub id: uuid::Uuid,
pub status: String,
}
}
pub mod create_metadata { pub mod create_metadata {
#[derive(Debug, serde::Deserialize, serde::Serialize, utoipa::ToSchema)] #[derive(Debug, serde::Deserialize, serde::Serialize, utoipa::ToSchema)]
pub struct Request { pub struct Request {
@@ -87,21 +58,6 @@ pub mod request {
} }
} }
pub mod wipe_data_from_song_queue {
#[derive(Debug, serde::Deserialize, serde::Serialize, utoipa::ToSchema)]
pub struct Request {
pub song_queue_id: uuid::Uuid,
}
}
pub mod link_user_id {
#[derive(Debug, serde::Deserialize, serde::Serialize, utoipa::ToSchema)]
pub struct Request {
pub song_queue_id: uuid::Uuid,
pub user_id: uuid::Uuid,
}
}
pub mod get_songs { pub mod get_songs {
#[derive(Debug, Default, serde::Deserialize, serde::Serialize, utoipa::ToSchema)] #[derive(Debug, Default, serde::Deserialize, serde::Serialize, utoipa::ToSchema)]
pub struct Params { pub struct Params {
@@ -111,48 +67,6 @@ pub mod request {
} }
pub mod response { pub mod response {
use serde::{Deserialize, Serialize};
/// Song queue response
#[derive(Default, Deserialize, Serialize, utoipa::ToSchema)]
pub struct Response {
pub message: String,
/// Id of the queued song
pub data: Vec<uuid::Uuid>,
}
pub mod fetch_queue_song {
use serde::{Deserialize, Serialize};
#[derive(Default, Deserialize, Serialize, utoipa::ToSchema)]
pub struct Response {
pub message: String,
pub data: Vec<crate::repo::queue::song::SongQueue>,
}
}
pub mod update_status {
#[derive(serde::Deserialize, serde::Serialize, utoipa::ToSchema)]
pub struct ChangedStatus {
pub old_status: String,
pub new_status: String,
}
#[derive(Default, serde::Deserialize, serde::Serialize, utoipa::ToSchema)]
pub struct Response {
pub message: String,
pub data: Vec<ChangedStatus>,
}
}
pub mod update_song_queue {
#[derive(Default, serde::Deserialize, serde::Serialize, utoipa::ToSchema)]
pub struct Response {
pub message: String,
pub data: Vec<uuid::Uuid>,
}
}
pub mod create_metadata { pub mod create_metadata {
#[derive(Debug, Default, serde::Deserialize, serde::Serialize, utoipa::ToSchema)] #[derive(Debug, Default, serde::Deserialize, serde::Serialize, utoipa::ToSchema)]
pub struct Response { pub struct Response {
@@ -161,22 +75,6 @@ pub mod response {
} }
} }
pub mod wipe_data_from_song_queue {
#[derive(Debug, Default, serde::Deserialize, serde::Serialize, utoipa::ToSchema)]
pub struct Response {
pub message: String,
pub data: Vec<uuid::Uuid>,
}
}
pub mod link_user_id {
#[derive(Debug, Default, serde::Deserialize, serde::Serialize, utoipa::ToSchema)]
pub struct Response {
pub message: String,
pub data: Vec<uuid::Uuid>,
}
}
pub mod get_songs { pub mod get_songs {
#[derive(Debug, Default, serde::Deserialize, serde::Serialize, utoipa::ToSchema)] #[derive(Debug, Default, serde::Deserialize, serde::Serialize, utoipa::ToSchema)]
pub struct Response { pub struct Response {
@@ -204,310 +102,11 @@ pub mod response {
/// Module for song related endpoints /// Module for song related endpoints
pub mod endpoint { pub mod endpoint {
use axum::{Json, http::StatusCode, response::IntoResponse}; use axum::{Json, response::IntoResponse};
// use crate::callers::song::song_queue;
use crate::repo; use crate::repo;
use crate::repo::queue as repo_queue; use crate::repo::queue as repo_queue;
/// Endpoint to queue a song. Starts the process and places the song in a queue
#[utoipa::path(
post,
path = super::super::endpoints::QUEUESONG,
request_body(
content = super::request::song_queue::SongQueueRequest,
description = "Multipart form data for uploading song",
content_type = "multipart/form-data"
),
responses(
(status = 200, description = "Song queued", body = super::response::Response)
)
)]
pub async fn queue_song(
axum::Extension(pool): axum::Extension<sqlx::PgPool>,
mut multipart: axum::extract::Multipart,
) -> (StatusCode, Json<super::response::Response>) {
let mut results: Vec<uuid::Uuid> = Vec::new();
let mut response = super::response::Response::default();
while let Some(field) = multipart.next_field().await.unwrap() {
let name = field.name().unwrap().to_string();
let file_name = field.file_name().unwrap().to_string();
let content_type = field.content_type().unwrap().to_string();
let data = field.bytes().await.unwrap();
println!(
"Received file '{}' (name = '{}', content-type = '{}', size = {})",
file_name,
name,
content_type,
data.len()
);
let raw_data: Vec<u8> = data.to_vec();
let queue_repo = repo_queue::song::insert(
&pool,
&raw_data,
&file_name,
&repo::queue::song::status::PENDING.to_string(),
)
.await
.unwrap();
results.push(queue_repo);
}
response.data = results;
response.message = if response.data.is_empty() {
String::from("Error")
} else {
String::from("Success")
};
(StatusCode::OK, Json(response))
}
/// Endpoint to link a user id to a queued song
#[utoipa::path(
patch,
path = super::super::endpoints::QUEUESONGLINKUSERID,
request_body(
content = super::request::link_user_id::Request,
description = "User Id and queued song id",
content_type = "application/json"
),
responses(
(status = 200, description = "Queued song linked", body = super::response::link_user_id::Response),
(status = 400, description = "Linkage failed", body = super::response::link_user_id::Response)
)
)]
pub async fn link_user_id(
axum::Extension(pool): axum::Extension<sqlx::PgPool>,
axum::Json(payload): axum::Json<super::request::link_user_id::Request>,
) -> (
axum::http::StatusCode,
axum::Json<super::response::link_user_id::Response>,
) {
let mut response = super::response::link_user_id::Response::default();
match repo_queue::song::get_song_queue(&pool, &payload.song_queue_id).await {
Ok(song_queue) => {
match repo_queue::song::link_user_id(&pool, &song_queue.id, &payload.user_id).await
{
Ok(user_id) => {
response.message = String::from(crate::callers::response::SUCCESSFUL);
response.data.push(user_id);
(axum::http::StatusCode::OK, axum::Json(response))
}
Err(err) => {
response.message = err.to_string();
(axum::http::StatusCode::BAD_REQUEST, axum::Json(response))
}
}
}
Err(err) => {
response.message = err.to_string();
(axum::http::StatusCode::BAD_REQUEST, axum::Json(response))
}
}
}
/// Endpoint to fetch the next queued song as long as it is available
#[utoipa::path(
get,
path = super::super::endpoints::NEXTQUEUESONG,
responses(
(status = 200, description = "Queued song is present and available", body = super::response::fetch_queue_song::Response),
(status = 400, description = "Linkage failed", body = super::response::fetch_queue_song::Response)
)
)]
pub async fn fetch_queue_song(
axum::Extension(pool): axum::Extension<sqlx::PgPool>,
) -> (
StatusCode,
Json<super::response::fetch_queue_song::Response>,
) {
let mut response = super::response::fetch_queue_song::Response::default();
match repo_queue::song::get_most_recent_and_update(&pool).await {
Ok(item) => {
response.message = String::from("Successful");
response.data.push(item);
(StatusCode::OK, Json(response))
}
Err(err) => {
response.message = err.to_string();
(StatusCode::BAD_REQUEST, Json(response))
}
}
}
// TODO: Rename
/// Endpoint to download the queued song
#[utoipa::path(
get,
path = super::super::endpoints::QUEUESONGDATA,
params(("id" = uuid::Uuid, Path, description = "Queued song Id")),
responses(
(status = 200, description = "Queued song linked", body = Vec<u8>),
(status = 400, description = "Linkage failed", body = Vec<u8>)
)
)]
pub async fn download_queued_song(
axum::Extension(pool): axum::Extension<sqlx::PgPool>,
axum::extract::Path(id): axum::extract::Path<uuid::Uuid>,
) -> (StatusCode, axum::response::Response) {
println!("Id: {id}");
match repo_queue::song::get_data(&pool, &id).await {
Ok(data) => {
let by = axum::body::Bytes::from(data);
let mut response = by.into_response();
let headers = response.headers_mut();
headers.insert(
axum::http::header::CONTENT_TYPE,
"audio/flac".parse().unwrap(),
);
headers.insert(
axum::http::header::CONTENT_DISPOSITION,
format!("attachment; filename=\"{id}.flac\"")
.parse()
.unwrap(),
);
(StatusCode::OK, response)
}
Err(_err) => (StatusCode::BAD_REQUEST, axum::response::Response::default()),
}
}
/// Endpoint to update the status of a queued song
#[utoipa::path(
patch,
path = super::super::endpoints::QUEUESONG,
request_body(
content = super::request::update_status::Request,
description = "Update the status of a queued song",
content_type = "application/json"
),
responses(
(status = 200, description = "Status has been updated", body = super::response::update_status::Response),
(status = 400, description = "Error updating status of queued song", body = super::response::update_status::Response)
)
)]
pub async fn update_song_queue_status(
axum::Extension(pool): axum::Extension<sqlx::PgPool>,
axum::Json(payload): axum::Json<super::request::update_status::Request>,
) -> (
axum::http::StatusCode,
axum::Json<super::response::update_status::Response>,
) {
let mut response = super::response::update_status::Response::default();
if repo::queue::song::status::is_valid(&payload.status).await {
let id = payload.id;
if !id.is_nil() {
match repo::queue::song::get_status_of_song_queue(&pool, &id).await {
Ok(old) => {
match repo::queue::song::update_song_queue_status(
&pool,
&payload.status,
&id,
)
.await
{
Ok(new) => {
response.message = String::from("Successful");
response
.data
.push(super::response::update_status::ChangedStatus {
old_status: old,
new_status: new,
});
(axum::http::StatusCode::OK, axum::Json(response))
}
Err(err) => {
response.message = err.to_string();
(axum::http::StatusCode::BAD_REQUEST, axum::Json(response))
}
}
}
Err(err) => {
response.message = err.to_string();
(axum::http::StatusCode::BAD_REQUEST, axum::Json(response))
}
}
} else {
response.message = String::from("Id is nil");
(axum::http::StatusCode::BAD_REQUEST, axum::Json(response))
}
} else {
response.message = String::from("Status not valid");
(axum::http::StatusCode::BAD_REQUEST, axum::Json(response))
}
}
/// Endpoint to update the queued song data
#[utoipa::path(
patch,
path = super::super::endpoints::QUEUESONGUPDATE,
request_body(
content = super::request::song_queue::SongQueueRequest,
description = "Multipart form data for uploading song",
content_type = "multipart/form-data"
),
params(("id" = uuid::Uuid, Path, description = "Queued song Id")),
responses(
(status = 200, description = "Queued song updated", body = super::response::update_song_queue::Response),
(status = 400, description = "Error updating queued song", body = super::response::update_song_queue::Response),
(status = 404, description = "Queued song not found", body = super::response::update_song_queue::Response)
)
)]
pub async fn update_song_queue(
axum::extract::Path(id): axum::extract::Path<uuid::Uuid>,
axum::Extension(pool): axum::Extension<sqlx::PgPool>,
mut multipart: axum::extract::Multipart,
) -> (
axum::http::StatusCode,
axum::Json<super::response::update_song_queue::Response>,
) {
let mut response = super::response::update_song_queue::Response::default();
if let Some(field) = multipart.next_field().await.unwrap() {
let name = field.name().unwrap().to_string();
let file_name = field.file_name().unwrap().to_string();
let content_type = match field.content_type() {
Some(ct) => ct.to_string(),
None => String::new(),
};
let data = field.bytes().await.unwrap();
println!(
"Received file '{}' (name = '{}', content-type = '{}', size = {})",
file_name,
name,
content_type,
data.len()
);
let raw_data: Vec<u8> = data.to_vec();
match repo_queue::song::update(&pool, &raw_data, &id).await {
Ok(_) => {
response.message = String::from("Successful");
response.data.push(id);
(axum::http::StatusCode::OK, axum::Json(response))
}
Err(err) => {
response.message = err.to_string();
(axum::http::StatusCode::BAD_REQUEST, axum::Json(response))
}
}
} else {
response.message = String::from("No data provided");
(axum::http::StatusCode::NOT_FOUND, axum::Json(response))
}
}
/// Endpoint to create song /// Endpoint to create song
#[utoipa::path( #[utoipa::path(
post, post,
@@ -591,50 +190,6 @@ pub mod endpoint {
} }
} }
/// Endpoint to wipe the data from a queued song
#[utoipa::path(
patch,
path = super::super::endpoints::QUEUESONGDATAWIPE,
request_body(
content = super::request::wipe_data_from_song_queue::Request,
description = "Pass the queued song Id to wipe the data",
content_type = "application/json"
),
responses(
(status = 200, description = "Queued song data wiped", body = super::response::wipe_data_from_song_queue::Response),
(status = 404, description = "Queued song cannot be found", body = super::response::wipe_data_from_song_queue::Response)
)
)]
pub async fn wipe_data_from_song_queue(
axum::Extension(pool): axum::Extension<sqlx::PgPool>,
axum::Json(payload): axum::Json<super::request::wipe_data_from_song_queue::Request>,
) -> (
axum::http::StatusCode,
axum::Json<super::response::wipe_data_from_song_queue::Response>,
) {
let mut response = super::response::wipe_data_from_song_queue::Response::default();
let id = payload.song_queue_id;
match repo_queue::song::get_song_queue(&pool, &id).await {
Ok(song_queue) => match repo_queue::song::wipe_data(&pool, &song_queue.id).await {
Ok(wiped_id) => {
response.message = String::from("Success");
response.data.push(wiped_id);
(axum::http::StatusCode::OK, axum::Json(response))
}
Err(err) => {
response.message = err.to_string();
(axum::http::StatusCode::NOT_FOUND, axum::Json(response))
}
},
Err(err) => {
response.message = err.to_string();
(axum::http::StatusCode::NOT_FOUND, axum::Json(response))
}
}
}
// Endpoint to get songs // Endpoint to get songs
#[utoipa::path( #[utoipa::path(
get, get,
+96 -66
View File
@@ -55,6 +55,7 @@ pub mod init {
use crate::callers::coverart as coverart_caller; use crate::callers::coverart as coverart_caller;
use crate::callers::metadata as metadata_caller; use crate::callers::metadata as metadata_caller;
use crate::callers::queue::coverart as coverart_queue_callers; use crate::callers::queue::coverart as coverart_queue_callers;
use crate::callers::queue::song as song_queue_callers;
use crate::callers::song as song_caller; use crate::callers::song as song_caller;
use coverart_caller::endpoint as coverart_endpoints; use coverart_caller::endpoint as coverart_endpoints;
use coverart_caller::response as coverart_responses; use coverart_caller::response as coverart_responses;
@@ -64,6 +65,8 @@ pub mod init {
use metadata_caller::response as metadata_responses; use metadata_caller::response as metadata_responses;
use song_caller::endpoint as song_endpoints; use song_caller::endpoint as song_endpoints;
use song_caller::response as song_responses; use song_caller::response as song_responses;
use song_queue_callers::endpoint as song_queue_endpoints;
use song_queue_callers::response as song_queue_responses;
mod cors { mod cors {
pub async fn configure_cors() -> tower_http::cors::CorsLayer { pub async fn configure_cors() -> tower_http::cors::CorsLayer {
@@ -115,15 +118,15 @@ pub mod init {
#[derive(utoipa::OpenApi)] #[derive(utoipa::OpenApi)]
#[openapi( #[openapi(
paths(song_endpoints::queue_song, song_endpoints::link_user_id, song_endpoints::fetch_queue_song, song_endpoints::download_queued_song, paths(song_queue_endpoints::queue_song, song_queue_endpoints::link_user_id, song_queue_endpoints::fetch_queue_song, song_queue_endpoints::download_queued_song,
song_endpoints::update_song_queue_status, song_endpoints::update_song_queue, song_endpoints::create_metadata, song_endpoints::wipe_data_from_song_queue, song_endpoints::get_songs, song_endpoints::get_all_songs, song_endpoints::stream_song, song_endpoints::download_song, song_queue_endpoints::update_song_queue_status, song_queue_endpoints::update_song_queue, song_endpoints::create_metadata, song_queue_endpoints::wipe_data_from_song_queue, song_endpoints::get_songs, song_endpoints::get_all_songs, song_endpoints::stream_song, song_endpoints::download_song,
song_endpoints::delete_song, coverart_queue_endpoints::queue, coverart_queue_endpoints::link, coverart_queue_endpoints::fetch_coverart_no_data, song_endpoints::delete_song, coverart_queue_endpoints::queue, coverart_queue_endpoints::link, coverart_queue_endpoints::fetch_coverart_no_data,
coverart_queue_endpoints::fetch_coverart_with_data, coverart_endpoints::create_coverart, coverart_queue_endpoints::wipe_data_from_coverart_queue, coverart_queue_endpoints::fetch_coverart_with_data, coverart_endpoints::create_coverart, coverart_queue_endpoints::wipe_data_from_coverart_queue,
coverart_endpoints::get_coverart, coverart_endpoints::download_coverart, coverart_endpoints::get_coverart, coverart_endpoints::download_coverart,
metadata_endpoints::queue_metadata, metadata_endpoints::fetch_metadata), metadata_endpoints::queue_metadata, metadata_endpoints::fetch_metadata),
components(schemas(song_responses::Response, song_responses::link_user_id::Response, song_responses::fetch_queue_song::Response, components(schemas(song_queue_callers::response::song_queue::Response, song_queue_callers::response::link_user_id::Response, song_queue_callers::response::fetch_queue_song::Response,
song_responses::update_status::Response, song_responses::update_song_queue::Response, song_responses::create_metadata::Response, song_queue_responses::update_status::Response, song_queue_callers::response::update_song_queue::Response, song_responses::create_metadata::Response,
song_responses::wipe_data_from_song_queue::Response, song_responses::get_songs::Response, song_responses::delete_song::Response, song_queue_callers::response::wipe_data_from_song_queue::Response, song_responses::get_songs::Response, song_responses::delete_song::Response,
coverart_queue_responses::queue::Response, coverart_queue_responses::link::Response, coverart_queue_responses::fetch_coverart_no_data::Response, coverart_queue_responses::queue::Response, coverart_queue_responses::link::Response, coverart_queue_responses::fetch_coverart_no_data::Response,
coverart_queue_responses::fetch_coverart_with_data::Response, coverart_responses::create_coverart::Response, coverart_queue_responses::fetch_coverart_with_data::Response, coverart_responses::create_coverart::Response,
coverart_queue_responses::wipe_data_from_coverart_queue::Response, coverart_responses::get_coverart::Response, coverart_queue_responses::wipe_data_from_coverart_queue::Response, coverart_responses::get_coverart::Response,
@@ -138,46 +141,47 @@ pub mod init {
axum::Router::new() axum::Router::new()
.route(crate::ROOT, get(crate::root)) .route(crate::ROOT, get(crate::root))
.route( .route(
crate::callers::endpoints::QUEUESONG, crate::callers::queue::endpoints::QUEUESONG,
post(crate::callers::song::endpoint::queue_song).route_layer( post(crate::callers::queue::song::endpoint::queue_song).route_layer(
axum::middleware::from_fn(crate::auth::auth::<axum::body::Body>), axum::middleware::from_fn(crate::auth::auth::<axum::body::Body>),
), ),
) )
.route( .route(
crate::callers::endpoints::QUEUESONG, crate::callers::queue::endpoints::QUEUESONG,
patch(crate::callers::song::endpoint::update_song_queue_status).route_layer( patch(crate::callers::queue::song::endpoint::update_song_queue_status).route_layer(
axum::middleware::from_fn(crate::auth::auth::<axum::body::Body>), axum::middleware::from_fn(crate::auth::auth::<axum::body::Body>),
), ),
) )
.route( .route(
crate::callers::endpoints::QUEUESONGLINKUSERID, crate::callers::queue::endpoints::QUEUESONGLINKUSERID,
patch(crate::callers::song::endpoint::link_user_id).route_layer( patch(crate::callers::queue::song::endpoint::link_user_id).route_layer(
axum::middleware::from_fn(crate::auth::auth::<axum::body::Body>), axum::middleware::from_fn(crate::auth::auth::<axum::body::Body>),
), ),
) )
.route( .route(
crate::callers::endpoints::QUEUESONGDATA, crate::callers::queue::endpoints::QUEUESONGDATA,
get(crate::callers::song::endpoint::download_queued_song).route_layer( get(crate::callers::queue::song::endpoint::download_queued_song).route_layer(
axum::middleware::from_fn(crate::auth::auth::<axum::body::Body>), axum::middleware::from_fn(crate::auth::auth::<axum::body::Body>),
), ),
) )
.route( .route(
crate::callers::endpoints::NEXTQUEUESONG, crate::callers::queue::endpoints::NEXTQUEUESONG,
get(crate::callers::song::endpoint::fetch_queue_song).route_layer( get(crate::callers::queue::song::endpoint::fetch_queue_song).route_layer(
axum::middleware::from_fn(crate::auth::auth::<axum::body::Body>), axum::middleware::from_fn(crate::auth::auth::<axum::body::Body>),
), ),
) )
.route( .route(
crate::callers::endpoints::QUEUESONGUPDATE, crate::callers::queue::endpoints::QUEUESONGUPDATE,
patch(crate::callers::song::endpoint::update_song_queue).route_layer( patch(crate::callers::queue::song::endpoint::update_song_queue).route_layer(
axum::middleware::from_fn(crate::auth::auth::<axum::body::Body>), axum::middleware::from_fn(crate::auth::auth::<axum::body::Body>),
), ),
) )
.route( .route(
crate::callers::endpoints::QUEUESONGDATAWIPE, crate::callers::queue::endpoints::QUEUESONGDATAWIPE,
patch(crate::callers::song::endpoint::wipe_data_from_song_queue).route_layer( patch(crate::callers::queue::song::endpoint::wipe_data_from_song_queue)
axum::middleware::from_fn(crate::auth::auth::<axum::body::Body>), .route_layer(axum::middleware::from_fn(
), crate::auth::auth::<axum::body::Body>,
)),
) )
.route( .route(
crate::callers::endpoints::QUEUEMETADATA, crate::callers::endpoints::QUEUEMETADATA,
@@ -192,32 +196,32 @@ pub mod init {
), ),
) )
.route( .route(
crate::callers::endpoints::QUEUECOVERART, crate::callers::queue::endpoints::QUEUECOVERART,
post(crate::callers::queue::coverart::endpoint::queue).route_layer( post(crate::callers::queue::coverart::endpoint::queue).route_layer(
axum::middleware::from_fn(crate::auth::auth::<axum::body::Body>), axum::middleware::from_fn(crate::auth::auth::<axum::body::Body>),
), ),
) )
.route( .route(
crate::callers::endpoints::QUEUECOVERARTDATA, crate::callers::queue::endpoints::QUEUECOVERARTDATA,
get(crate::callers::queue::coverart::endpoint::fetch_coverart_with_data) get(crate::callers::queue::coverart::endpoint::fetch_coverart_with_data)
.route_layer(axum::middleware::from_fn( .route_layer(axum::middleware::from_fn(
crate::auth::auth::<axum::body::Body>, crate::auth::auth::<axum::body::Body>,
)), )),
) )
.route( .route(
crate::callers::endpoints::QUEUECOVERART, crate::callers::queue::endpoints::QUEUECOVERART,
get(crate::callers::queue::coverart::endpoint::fetch_coverart_no_data).route_layer( get(crate::callers::queue::coverart::endpoint::fetch_coverart_no_data).route_layer(
axum::middleware::from_fn(crate::auth::auth::<axum::body::Body>), axum::middleware::from_fn(crate::auth::auth::<axum::body::Body>),
), ),
) )
.route( .route(
crate::callers::endpoints::QUEUECOVERARTLINK, crate::callers::queue::endpoints::QUEUECOVERARTLINK,
patch(crate::callers::queue::coverart::endpoint::link).route_layer( patch(crate::callers::queue::coverart::endpoint::link).route_layer(
axum::middleware::from_fn(crate::auth::auth::<axum::body::Body>), axum::middleware::from_fn(crate::auth::auth::<axum::body::Body>),
), ),
) )
.route( .route(
crate::callers::endpoints::QUEUECOVERARTDATAWIPE, crate::callers::queue::endpoints::QUEUECOVERARTDATAWIPE,
patch(crate::callers::queue::coverart::endpoint::wipe_data_from_coverart_queue) patch(crate::callers::queue::coverart::endpoint::wipe_data_from_coverart_queue)
.route_layer(axum::middleware::from_fn( .route_layer(axum::middleware::from_fn(
crate::auth::auth::<axum::body::Body>, crate::auth::auth::<axum::body::Body>,
@@ -471,7 +475,7 @@ mod tests {
let body = MultipartBody::from(form); let body = MultipartBody::from(form);
let req = axum::http::Request::builder() let req = axum::http::Request::builder()
.method(axum::http::Method::POST) .method(axum::http::Method::POST)
.uri(crate::callers::endpoints::QUEUESONG) .uri(crate::callers::queue::endpoints::QUEUESONG)
.header(axum::http::header::CONTENT_TYPE, content_type) .header(axum::http::header::CONTENT_TYPE, content_type)
.header(axum::http::header::AUTHORIZATION, bearer_auth().await) .header(axum::http::header::AUTHORIZATION, bearer_auth().await)
.body(axum::body::Body::from_stream(body)) .body(axum::body::Body::from_stream(body))
@@ -491,7 +495,7 @@ mod tests {
let req = axum::http::Request::builder() let req = axum::http::Request::builder()
.method(axum::http::Method::PATCH) .method(axum::http::Method::PATCH)
.uri(crate::callers::endpoints::QUEUESONGLINKUSERID) .uri(crate::callers::queue::endpoints::QUEUESONGLINKUSERID)
.header(axum::http::header::CONTENT_TYPE, "application/json") .header(axum::http::header::CONTENT_TYPE, "application/json")
.header(axum::http::header::AUTHORIZATION, bearer_auth().await) .header(axum::http::header::AUTHORIZATION, bearer_auth().await)
.body(axum::body::Body::from(payload.to_string())) .body(axum::body::Body::from(payload.to_string()))
@@ -505,7 +509,7 @@ mod tests {
) -> Result<axum::response::Response, std::convert::Infallible> { ) -> Result<axum::response::Response, std::convert::Infallible> {
let fetch_req = axum::http::Request::builder() let fetch_req = axum::http::Request::builder()
.method(axum::http::Method::GET) .method(axum::http::Method::GET)
.uri(crate::callers::endpoints::NEXTQUEUESONG) .uri(crate::callers::queue::endpoints::NEXTQUEUESONG)
.header(axum::http::header::CONTENT_TYPE, "application/json") .header(axum::http::header::CONTENT_TYPE, "application/json")
.header(axum::http::header::AUTHORIZATION, bearer_auth().await) .header(axum::http::header::AUTHORIZATION, bearer_auth().await)
.body(axum::body::Body::empty()) .body(axum::body::Body::empty())
@@ -534,7 +538,7 @@ mod tests {
app: &axum::Router, app: &axum::Router,
id: &uuid::Uuid, id: &uuid::Uuid,
) -> Result<axum::response::Response, std::convert::Infallible> { ) -> Result<axum::response::Response, std::convert::Infallible> {
let raw_uri = String::from(crate::callers::endpoints::QUEUESONGDATA); let raw_uri = String::from(crate::callers::queue::endpoints::QUEUESONGDATA);
let end_index = raw_uri.len() - 4; let end_index = raw_uri.len() - 4;
let mut uri: String = (&raw_uri[..end_index]).to_string(); let mut uri: String = (&raw_uri[..end_index]).to_string();
uri += &id.to_string(); uri += &id.to_string();
@@ -561,7 +565,7 @@ mod tests {
let req = axum::http::Request::builder() let req = axum::http::Request::builder()
.method(axum::http::Method::POST) .method(axum::http::Method::POST)
.uri(crate::callers::endpoints::QUEUECOVERART) .uri(crate::callers::queue::endpoints::QUEUECOVERART)
.header(axum::http::header::CONTENT_TYPE, content_type) .header(axum::http::header::CONTENT_TYPE, content_type)
.header(axum::http::header::AUTHORIZATION, bearer_auth().await) .header(axum::http::header::AUTHORIZATION, bearer_auth().await)
.body(axum::body::Body::from_stream(body)) .body(axum::body::Body::from_stream(body))
@@ -600,7 +604,7 @@ mod tests {
}); });
let req = axum::http::Request::builder() let req = axum::http::Request::builder()
.method(axum::http::Method::PATCH) .method(axum::http::Method::PATCH)
.uri(crate::callers::endpoints::QUEUECOVERARTLINK) .uri(crate::callers::queue::endpoints::QUEUECOVERARTLINK)
.header(axum::http::header::CONTENT_TYPE, "application/json") .header(axum::http::header::CONTENT_TYPE, "application/json")
.header(axum::http::header::AUTHORIZATION, bearer_auth().await) .header(axum::http::header::AUTHORIZATION, bearer_auth().await)
.body(axum::body::Body::from(payload.to_string())) .body(axum::body::Body::from(payload.to_string()))
@@ -657,7 +661,7 @@ mod tests {
let req = axum::http::Request::builder() let req = axum::http::Request::builder()
.method(axum::http::Method::PATCH) .method(axum::http::Method::PATCH)
.uri(crate::callers::endpoints::QUEUESONG) .uri(crate::callers::queue::endpoints::QUEUESONG)
.header(axum::http::header::CONTENT_TYPE, "application/json") .header(axum::http::header::CONTENT_TYPE, "application/json")
.header(axum::http::header::AUTHORIZATION, bearer_auth().await) .header(axum::http::header::AUTHORIZATION, bearer_auth().await)
.body(axum::body::Body::from(payload.to_string())) .body(axum::body::Body::from(payload.to_string()))
@@ -672,7 +676,7 @@ mod tests {
) -> Result<axum::response::Response, std::convert::Infallible> { ) -> Result<axum::response::Response, std::convert::Infallible> {
let uri = format!( let uri = format!(
"{}?id={}", "{}?id={}",
crate::callers::endpoints::QUEUECOVERART, crate::callers::queue::endpoints::QUEUECOVERART,
coverart_queue_id coverart_queue_id
); );
@@ -694,8 +698,9 @@ mod tests {
) -> Result<(axum::response::Response, uuid::Uuid), std::convert::Infallible> { ) -> Result<(axum::response::Response, uuid::Uuid), std::convert::Infallible> {
match super::song_queue_req(&app).await { match super::song_queue_req(&app).await {
Ok(response) => { Ok(response) => {
let resp = let resp = super::get_resp_data::<
super::get_resp_data::<crate::callers::song::response::Response>(response) crate::callers::queue::song::response::song_queue::Response,
>(response)
.await; .await;
assert_eq!(false, resp.data.is_empty(), "Should not be empty"); assert_eq!(false, resp.data.is_empty(), "Should not be empty");
assert_eq!(false, resp.data[0].is_nil(), "Should not be empty"); assert_eq!(false, resp.data[0].is_nil(), "Should not be empty");
@@ -707,7 +712,7 @@ mod tests {
match super::song_queue_link_req(&app, &song_queue_id, &user_id).await { match super::song_queue_link_req(&app, &song_queue_id, &user_id).await {
Ok(response) => { Ok(response) => {
let resp = super::get_resp_data::< let resp = super::get_resp_data::<
crate::callers::song::response::link_user_id::Response, crate::callers::queue::song::response::link_user_id::Response,
>(response) >(response)
.await; .await;
assert_eq!( assert_eq!(
@@ -719,7 +724,7 @@ mod tests {
match super::queue_metadata_req(&app, &song_queue_id).await { match super::queue_metadata_req(&app, &song_queue_id).await {
Ok(response) => { Ok(response) => {
let resp = super::get_resp_data::< let resp = super::get_resp_data::<
crate::callers::song::response::Response, crate::callers::queue::song::response::song_queue::Response,
>(response) >(response)
.await; .await;
assert_eq!(false, resp.data.is_empty(), "Should not be empty"); assert_eq!(false, resp.data.is_empty(), "Should not be empty");
@@ -928,8 +933,10 @@ mod tests {
// Send request // Send request
match song_queue_req(&app).await { match song_queue_req(&app).await {
Ok(response) => { Ok(response) => {
let resp = let resp = get_resp_data::<
get_resp_data::<crate::callers::song::response::Response>(response).await; crate::callers::queue::song::response::song_queue::Response,
>(response)
.await;
assert_eq!(false, resp.data.is_empty(), "Should not be empty"); assert_eq!(false, resp.data.is_empty(), "Should not be empty");
assert_eq!(false, resp.data[0].is_nil(), "Should not be empty"); assert_eq!(false, resp.data[0].is_nil(), "Should not be empty");
} }
@@ -962,8 +969,10 @@ mod tests {
match song_queue_req(&app).await { match song_queue_req(&app).await {
Ok(response) => { Ok(response) => {
let resp = let resp = get_resp_data::<
get_resp_data::<crate::callers::song::response::Response>(response).await; crate::callers::queue::song::response::song_queue::Response,
>(response)
.await;
assert_eq!(false, resp.data.is_empty(), "Should not be empty"); assert_eq!(false, resp.data.is_empty(), "Should not be empty");
assert_eq!(false, resp.data[0].is_nil(), "Should not be empty"); assert_eq!(false, resp.data[0].is_nil(), "Should not be empty");
@@ -974,7 +983,7 @@ mod tests {
match song_queue_link_req(&app, &song_queue_id, &user_id).await { match song_queue_link_req(&app, &song_queue_id, &user_id).await {
Ok(response) => { Ok(response) => {
let resp = get_resp_data::< let resp = get_resp_data::<
crate::callers::song::response::link_user_id::Response, crate::callers::queue::song::response::link_user_id::Response,
>(response) >(response)
.await; .await;
let collected_user_id = &resp.data[0]; let collected_user_id = &resp.data[0];
@@ -1039,7 +1048,7 @@ mod tests {
match update_song_queue_status_req(&app, &song_queue_id).await { match update_song_queue_status_req(&app, &song_queue_id).await {
Ok(response) => { Ok(response) => {
let resp = get_resp_data::< let resp = get_resp_data::<
crate::callers::song::response::update_status::Response, crate::callers::queue::song::response::update_status::Response,
>(response) >(response)
.await; .await;
assert_eq!(false, resp.data.is_empty(), "Should not be empty"); assert_eq!(false, resp.data.is_empty(), "Should not be empty");
@@ -1054,7 +1063,7 @@ mod tests {
match fetch_queue_req(&app).await { match fetch_queue_req(&app).await {
Ok(response) => { Ok(response) => {
let resp = get_resp_data::< let resp = get_resp_data::<
crate::callers::song::response::fetch_queue_song::Response, crate::callers::queue::song::response::fetch_queue_song::Response,
>(response) >(response)
.await; .await;
assert_eq!(false, resp.data.is_empty(), "Should not be empty"); assert_eq!(false, resp.data.is_empty(), "Should not be empty");
@@ -1099,8 +1108,10 @@ mod tests {
// Send request // Send request
match song_queue_req(&app).await { match song_queue_req(&app).await {
Ok(response) => { Ok(response) => {
let resp = let resp = get_resp_data::<
get_resp_data::<crate::callers::song::response::Response>(response).await; crate::callers::queue::song::response::song_queue::Response,
>(response)
.await;
assert_eq!(false, resp.data.is_empty(), "Should not be empty"); assert_eq!(false, resp.data.is_empty(), "Should not be empty");
assert_eq!(false, resp.data[0].is_nil(), "Should not be empty"); assert_eq!(false, resp.data[0].is_nil(), "Should not be empty");
@@ -1114,19 +1125,32 @@ mod tests {
let temp_file = let temp_file =
tempfile::tempdir().expect("Could not create test directory"); tempfile::tempdir().expect("Could not create test directory");
let test_dir = String::from(temp_file.path().to_str().unwrap()); let test_dir = String::from(temp_file.path().to_str().unwrap());
let new_file = format!("{}/new_file.flac", test_dir); let song = icarus_models::song::Song {
directory: test_dir,
let mut file = std::fs::File::create(&new_file).unwrap(); filename: icarus_models::song::generate_filename(
file.write_all(&bytes).unwrap(); icarus_models::types::MusicTypes::FlacExtension,
true,
),
data: bytes.to_vec(),
..Default::default()
};
match song.save_to_filesystem() {
Ok(_) => {}
Err(err) => {
assert!(false, "Error: {err:?}")
}
}
let songpath = song.song_path().unwrap();
let mut form = MultipartForm::default(); let mut form = MultipartForm::default();
let _ = form.add_file("flac", new_file); let _ = form.add_file("flac", &songpath);
// Create request // Create request
let content_type = form.content_type(); let content_type = form.content_type();
let body = MultipartBody::from(form); let body = MultipartBody::from(form);
let raw_uri = String::from(crate::callers::endpoints::QUEUESONGUPDATE); let raw_uri =
String::from(crate::callers::queue::endpoints::QUEUESONGUPDATE);
let end_index = raw_uri.len() - 5; let end_index = raw_uri.len() - 5;
let uri = format!( let uri = format!(
@@ -1153,7 +1177,7 @@ mod tests {
{ {
Ok(response) => { Ok(response) => {
let resp = get_resp_data::< let resp = get_resp_data::<
crate::callers::song::response::update_song_queue::Response, crate::callers::queue::song::response::update_song_queue::Response,
>(response) >(response)
.await; .await;
assert_eq!(false, resp.data.is_empty(), "Should not be empty"); assert_eq!(false, resp.data.is_empty(), "Should not be empty");
@@ -1209,8 +1233,10 @@ mod tests {
// Send request // Send request
match song_queue_req(&app).await { match song_queue_req(&app).await {
Ok(response) => { Ok(response) => {
let resp = let resp = get_resp_data::<
get_resp_data::<crate::callers::song::response::Response>(response).await; crate::callers::queue::song::response::song_queue::Response,
>(response)
.await;
assert_eq!(false, resp.data.is_empty(), "Should not be empty"); assert_eq!(false, resp.data.is_empty(), "Should not be empty");
assert_eq!(false, resp.data[0].is_nil(), "Should not be empty"); assert_eq!(false, resp.data[0].is_nil(), "Should not be empty");
let id = resp.data[0]; let id = resp.data[0];
@@ -1272,7 +1298,7 @@ mod tests {
match update_song_queue_status_req(&app, &song_queue_id).await { match update_song_queue_status_req(&app, &song_queue_id).await {
Ok(response) => { Ok(response) => {
let resp = get_resp_data::< let resp = get_resp_data::<
crate::callers::song::response::update_status::Response, crate::callers::queue::song::response::update_status::Response,
>(response) >(response)
.await; .await;
assert_eq!(false, resp.data.is_empty(), "Should not be empty"); assert_eq!(false, resp.data.is_empty(), "Should not be empty");
@@ -1316,15 +1342,18 @@ mod tests {
// Send request // Send request
match song_queue_req(&app).await { match song_queue_req(&app).await {
Ok(response) => { Ok(response) => {
let resp = let resp = get_resp_data::<
get_resp_data::<crate::callers::song::response::Response>(response).await; crate::callers::queue::song::response::song_queue::Response,
>(response)
.await;
assert_eq!(false, resp.data.is_empty(), "Should not be empty"); assert_eq!(false, resp.data.is_empty(), "Should not be empty");
assert_eq!(false, resp.data[0].is_nil(), "Should not be empty"); assert_eq!(false, resp.data[0].is_nil(), "Should not be empty");
match queue_metadata_req(&app, &resp.data[0]).await { match queue_metadata_req(&app, &resp.data[0]).await {
Ok(response) => { Ok(response) => {
let resp = let resp = get_resp_data::<
get_resp_data::<crate::callers::song::response::Response>(response) crate::callers::queue::song::response::song_queue::Response,
>(response)
.await; .await;
assert_eq!(false, resp.data.is_empty(), "Should not be empty"); assert_eq!(false, resp.data.is_empty(), "Should not be empty");
} }
@@ -1601,8 +1630,9 @@ mod tests {
"Should not be empty" "Should not be empty"
); );
let raw_uri = let raw_uri = String::from(
String::from(crate::callers::endpoints::QUEUECOVERARTDATA); crate::callers::queue::endpoints::QUEUECOVERARTDATA,
);
let end_index = raw_uri.len() - 5; let end_index = raw_uri.len() - 5;
let uri = format!( let uri = format!(
"{}/{}", "{}/{}",
@@ -1886,7 +1916,7 @@ mod tests {
.oneshot( .oneshot(
axum::http::Request::builder() axum::http::Request::builder()
.method(axum::http::Method::PATCH) .method(axum::http::Method::PATCH)
.uri(crate::callers::endpoints::QUEUESONGDATAWIPE) .uri(crate::callers::queue::endpoints::QUEUESONGDATAWIPE)
.header(axum::http::header::CONTENT_TYPE, "application/json") .header(axum::http::header::CONTENT_TYPE, "application/json")
.header(axum::http::header::AUTHORIZATION, bearer_auth().await) .header(axum::http::header::AUTHORIZATION, bearer_auth().await)
.body(axum::body::Body::from(payload.to_string())) .body(axum::body::Body::from(payload.to_string()))
@@ -1895,7 +1925,7 @@ mod tests {
.await .await
{ {
Ok(response) => { Ok(response) => {
let resp = get_resp_data::<crate::callers::song::response::wipe_data_from_song_queue::Response>(response).await; let resp = get_resp_data::<crate::callers::queue::song::response::wipe_data_from_song_queue::Response>(response).await;
assert_eq!( assert_eq!(
false, false,
resp.data.is_empty(), resp.data.is_empty(),
@@ -2004,7 +2034,7 @@ mod tests {
.oneshot( .oneshot(
axum::http::Request::builder() axum::http::Request::builder()
.method(axum::http::Method::PATCH) .method(axum::http::Method::PATCH)
.uri(crate::callers::endpoints::QUEUECOVERARTDATAWIPE) .uri(crate::callers::queue::endpoints::QUEUECOVERARTDATAWIPE)
.header( .header(
axum::http::header::CONTENT_TYPE, axum::http::header::CONTENT_TYPE,
"application/json", "application/json",