summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorniliara-edu <nil.jimeno@estudiant.fjaverianas.com>2025-01-21 15:52:44 +0100
committerniliara-edu <nil.jimeno@estudiant.fjaverianas.com>2025-01-21 15:52:44 +0100
commitc277e9937115cc1473219a04199566c7bfdcbaf7 (patch)
treec2806eab444269d92c7c22d1165a9686f5edd9de /src
parent58515ddfecd3701112616c33ad4879dae4b38ffd (diff)
get, post, put, delete covered
Diffstat (limited to 'src')
-rw-r--r--src/database.rs293
-rw-r--r--src/main.rs18
-rw-r--r--src/routes/album.rs56
-rw-r--r--src/routes/artist.rs54
-rw-r--r--src/routes/song.rs56
5 files changed, 415 insertions, 62 deletions
diff --git a/src/database.rs b/src/database.rs
index eff3f0e..fbe8529 100644
--- a/src/database.rs
+++ b/src/database.rs
@@ -1,5 +1,5 @@
-use serde::Serialize;
-use sqlx::mysql::{MySqlPool, MySqlPoolOptions};
+use serde::{Deserialize, Serialize};
+use sqlx::mysql::{MySqlPool, MySqlPoolOptions, MySqlQueryResult};
use std::env;
#[derive(Serialize)]
@@ -17,6 +17,7 @@ pub struct Song {
pub struct Album {
name: Option<String>,
id: Option<i32>,
+ cover: Option<String>,
artist_name: Option<String>,
artist_id: Option<i32>,
}
@@ -27,6 +28,52 @@ pub struct Artist {
id: Option<i32>,
}
+#[derive(Serialize, Deserialize)]
+pub struct SongPost {
+ name: Option<String>,
+ lyrics: Option<String>,
+ album_id: Option<String>,
+}
+
+#[derive(Serialize, Deserialize)]
+pub struct AlbumPost {
+ name: Option<String>,
+ cover: Option<String>,
+ artist_id: Option<String>,
+}
+
+#[derive(Serialize, Deserialize)]
+pub struct ArtistPost {
+ name: Option<String>,
+}
+
+#[derive(Serialize, Deserialize)]
+pub struct SongPut {
+ id: Option<String>,
+ name: Option<String>,
+ lyrics: Option<String>,
+ album_id: Option<String>,
+}
+
+#[derive(Serialize, Deserialize)]
+pub struct AlbumPut {
+ id: Option<String>,
+ name: Option<String>,
+ cover: Option<String>,
+ artist_id: Option<String>,
+}
+
+#[derive(Serialize, Deserialize)]
+pub struct ArtistPut {
+ id: Option<String>,
+ name: Option<String>,
+}
+
+#[derive(Serialize, Deserialize)]
+pub struct Delete {
+ pub id: Option<String>,
+}
+
pub struct DatabaseWrapper {
db_pool: MySqlPool,
}
@@ -47,7 +94,7 @@ impl DatabaseWrapper {
}
pub async fn select_songs(&self) -> Result<Vec<Song>, sqlx::Error> {
- return sqlx::query_as!(
+ sqlx::query_as!(
Song,
"SELECT song.name, song.lyrics, song.id,
album.name as album_name, album.id as album_id,
@@ -58,11 +105,11 @@ impl DatabaseWrapper {
",
)
.fetch_all(&self.db_pool)
- .await;
+ .await
}
pub async fn select_song_by_id(&self, id: &str) -> Result<Option<Song>, sqlx::Error> {
- return sqlx::query_as!(
+ sqlx::query_as!(
Song,
"SELECT song.name, song.lyrics, song.id,
album.name as album_name, album.id as album_id,
@@ -75,11 +122,11 @@ impl DatabaseWrapper {
id,
)
.fetch_optional(&self.db_pool)
- .await;
+ .await
}
pub async fn select_songs_by_album(&self, album_id: &str) -> Result<Vec<Song>, sqlx::Error> {
- return sqlx::query_as!(
+ sqlx::query_as!(
Song,
"SELECT song.name, song.lyrics, song.id,
album.name as album_name, album.id as album_id,
@@ -92,11 +139,11 @@ impl DatabaseWrapper {
album_id,
)
.fetch_all(&self.db_pool)
- .await;
+ .await
}
pub async fn select_songs_by_artist(&self, artist_id: &str) -> Result<Vec<Song>, sqlx::Error> {
- return sqlx::query_as!(
+ sqlx::query_as!(
Song,
"SELECT song.name, song.lyrics, song.id,
album.name as album_name, album.id as album_id,
@@ -109,13 +156,12 @@ impl DatabaseWrapper {
artist_id,
)
.fetch_all(&self.db_pool)
- .await;
+ .await
}
pub async fn select_songs_by_name(&self, name_raw: &str) -> Result<Vec<Song>, sqlx::Error> {
let name: String = format!("{}{}{}", "%", name_raw, "%");
- println!("ERROR HUNTING: {}", name);
- return sqlx::query_as!(
+ sqlx::query_as!(
Song,
"SELECT song.name, song.lyrics, song.id,
album.name as album_name, album.id as album_id,
@@ -127,26 +173,26 @@ impl DatabaseWrapper {
name,
)
.fetch_all(&self.db_pool)
- .await;
+ .await
}
pub async fn select_albums(&self) -> Result<Vec<Album>, sqlx::Error> {
- return sqlx::query_as!(
+ sqlx::query_as!(
Album,
- "SELECT album.name, album.id,
+ "SELECT album.name, album.id, album.cover,
artist.name as artist_name, artist.id as artist_id
FROM album
INNER JOIN artist ON album.artist_id = artist.id
",
)
.fetch_all(&self.db_pool)
- .await;
+ .await
}
pub async fn select_album_by_id(&self, id: &str) -> Result<Option<Album>, sqlx::Error> {
- return sqlx::query_as!(
+ sqlx::query_as!(
Album,
- "SELECT album.name, album.id,
+ "SELECT album.name, album.id, album.cover,
artist.name as artist_name, artist.id as artist_id
FROM album
INNER JOIN artist ON album.artist_id = artist.id
@@ -154,14 +200,14 @@ impl DatabaseWrapper {
id,
)
.fetch_optional(&self.db_pool)
- .await;
+ .await
}
pub async fn select_albums_by_name(&self, name_raw: &str) -> Result<Vec<Album>, sqlx::Error> {
let name: String = format!("{}{}{}", "%", name_raw, "%");
- return sqlx::query_as!(
+ sqlx::query_as!(
Album,
- "SELECT album.name, album.id,
+ "SELECT album.name, album.id, album.cover,
artist.name as artist_name, artist.id as artist_id
FROM album
INNER JOIN artist ON album.artist_id = artist.id
@@ -170,16 +216,16 @@ impl DatabaseWrapper {
name,
)
.fetch_all(&self.db_pool)
- .await;
+ .await
}
pub async fn select_albums_by_artist(
&self,
artist_id: &str,
) -> Result<Vec<Album>, sqlx::Error> {
- return sqlx::query_as!(
+ sqlx::query_as!(
Album,
- "SELECT album.name, album.id,
+ "SELECT album.name, album.id, album.cover,
artist.name as artist_name, artist.id as artist_id
FROM album
INNER JOIN artist ON album.artist_id = artist.id
@@ -187,21 +233,21 @@ impl DatabaseWrapper {
artist_id,
)
.fetch_all(&self.db_pool)
- .await;
+ .await
}
pub async fn select_artists(&self) -> Result<Vec<Artist>, sqlx::Error> {
- return sqlx::query_as!(
+ sqlx::query_as!(
Artist,
"SELECT name, id
FROM artist",
)
.fetch_all(&self.db_pool)
- .await;
+ .await
}
pub async fn select_artist_by_id(&self, id: &str) -> Result<Option<Artist>, sqlx::Error> {
- return sqlx::query_as!(
+ sqlx::query_as!(
Artist,
"SELECT name, id
FROM artist
@@ -209,13 +255,12 @@ impl DatabaseWrapper {
id,
)
.fetch_optional(&self.db_pool)
- .await;
+ .await
}
pub async fn select_artists_by_name(&self, name_raw: &str) -> Result<Vec<Artist>, sqlx::Error> {
let name: String = format!("{}{}{}", "%", name_raw, "%");
- println!("ERROR HUNTING: {}", name);
- return sqlx::query_as!(
+ sqlx::query_as!(
Artist,
"SELECT name, id
FROM artist
@@ -223,7 +268,7 @@ impl DatabaseWrapper {
name,
)
.fetch_all(&self.db_pool)
- .await;
+ .await
}
pub async fn search_results(
@@ -233,11 +278,11 @@ impl DatabaseWrapper {
Result<Vec<Album>, sqlx::Error>,
Result<Vec<Song>, sqlx::Error>,
) {
- return (
+ (
self.select_artists().await,
self.select_albums().await,
self.select_songs().await,
- );
+ )
}
pub async fn search_results_by_id(
@@ -248,11 +293,11 @@ impl DatabaseWrapper {
Result<Option<Album>, sqlx::Error>,
Result<Option<Song>, sqlx::Error>,
) {
- return (
+ (
self.select_artist_by_id(id).await,
self.select_album_by_id(id).await,
self.select_song_by_id(id).await,
- );
+ )
}
pub async fn search_results_by_name(
@@ -269,4 +314,180 @@ impl DatabaseWrapper {
self.select_songs_by_name(name).await,
);
}
+
+ pub async fn create_song(&self, data: SongPost) -> Result<MySqlQueryResult, sqlx::Error> {
+ if data.name.is_none() || data.album_id.is_none() {
+ return Err(sqlx::Error::RowNotFound);
+ }
+
+ if match self
+ .select_album_by_id(data.album_id.as_ref().unwrap())
+ .await
+ {
+ Ok(res) => res.is_none(),
+ Err(_) => true,
+ } {
+ return Err(sqlx::Error::RowNotFound);
+ }
+
+ sqlx::query!(
+ "INSERT INTO song (name, lyrics, album_id)
+ VALUE (?, ?, ?)",
+ data.name,
+ data.lyrics.unwrap_or(String::default()),
+ data.album_id,
+ )
+ .execute(&self.db_pool)
+ .await
+ }
+
+ pub async fn create_album(&self, data: AlbumPost) -> Result<MySqlQueryResult, sqlx::Error> {
+ if data.name.is_none() || data.artist_id.is_none() {
+ return Err(sqlx::Error::RowNotFound);
+ }
+
+ if match self
+ .select_artist_by_id(data.artist_id.as_ref().unwrap())
+ .await
+ {
+ Ok(res) => res.is_none(),
+ Err(_) => true,
+ } {
+ return Err(sqlx::Error::RowNotFound);
+ }
+
+ sqlx::query!(
+ "INSERT INTO album (name, cover, artist_id)
+ VALUE (?, ?, ?)",
+ data.name,
+ data.cover.unwrap_or(String::default()),
+ data.artist_id,
+ )
+ .execute(&self.db_pool)
+ .await
+ }
+
+ pub async fn create_artist(&self, data: ArtistPost) -> Result<MySqlQueryResult, sqlx::Error> {
+ if data.name.is_none() {
+ return Err(sqlx::Error::RowNotFound);
+ }
+ sqlx::query!(
+ "INSERT INTO artist (name)
+ VALUE (?)",
+ data.name
+ )
+ .execute(&self.db_pool)
+ .await
+ }
+
+ pub async fn edit_song(&self, data: SongPut) -> Result<MySqlQueryResult, sqlx::Error> {
+ if data.id.is_none() { return Err(sqlx::Error::RowNotFound); }
+ let og_song: Song = match self.select_song_by_id(data.id.as_ref().unwrap()).await {
+ Ok(res) => match res.is_some() {
+ true => res.unwrap(),
+ false => return Err(sqlx::Error::RowNotFound),
+ }
+ Err(_) => return Err(sqlx::Error::RowNotFound),
+ };
+ sqlx::query!(
+ "UPDATE song SET name=?, lyrics=? WHERE id=?",
+ data.name.unwrap_or(og_song.name.unwrap_or(String::default())),
+ data.lyrics.unwrap_or(og_song.lyrics.unwrap_or(String::default())),
+ data.id,
+ )
+ .execute(&self.db_pool)
+ .await
+ }
+
+ pub async fn edit_album(&self, data: AlbumPut) -> Result<MySqlQueryResult, sqlx::Error> {
+ if data.id.is_none() { return Err(sqlx::Error::RowNotFound); }
+ let og_album: Album = match self.select_album_by_id(data.id.as_ref().unwrap()).await {
+ Ok(res) => match res.is_some() {
+ true => res.unwrap(),
+ false => return Err(sqlx::Error::RowNotFound),
+ }
+ Err(_) => return Err(sqlx::Error::RowNotFound),
+ };
+ sqlx::query!(
+ "UPDATE album SET name=?, cover=? WHERE id=?",
+ data.name.unwrap_or(og_album.name.unwrap_or(String::default())),
+ data.cover.unwrap_or(og_album.cover.unwrap_or(String::default())),
+ data.id,
+ )
+ .execute(&self.db_pool)
+ .await
+ }
+
+ pub async fn edit_artist(&self, data: ArtistPut) -> Result<MySqlQueryResult, sqlx::Error> {
+ if data.id.is_none() { return Err(sqlx::Error::RowNotFound); }
+ let og_artist: Artist = match self.select_artist_by_id(data.id.as_ref().unwrap()).await {
+ Ok(res) => match res.is_some() {
+ true => res.unwrap(),
+ false => return Err(sqlx::Error::RowNotFound),
+ }
+ Err(_) => return Err(sqlx::Error::RowNotFound),
+ };
+ sqlx::query!(
+ "UPDATE artist SET name=? WHERE id=?",
+ data.name.unwrap_or(og_artist.name.unwrap_or(String::default())),
+ data.id,
+ )
+ .execute(&self.db_pool)
+ .await
+ }
+
+ pub async fn delete_song(&self, id: i32) -> Result<MySqlQueryResult, sqlx::Error> {
+ sqlx::query!(
+ "DELETE FROM song
+ WHERE id = ?",
+ id
+ )
+ .execute(&self.db_pool)
+ .await
+ }
+
+ pub async fn delete_album(&self, id: i32) -> Result<MySqlQueryResult, sqlx::Error> {
+ let _ = sqlx::query!(
+ "DELETE FROM song
+ WHERE album_id = ?",
+ id
+ )
+ .execute(&self.db_pool)
+ .await;
+
+ sqlx::query!(
+ "DELETE FROM album
+ WHERE id = ?",
+ id
+ )
+ .execute(&self.db_pool)
+ .await
+ }
+
+ pub async fn delete_artist(&self, id: i32) -> Result<MySqlQueryResult, sqlx::Error> {
+ let _ = sqlx::query!(
+ "DELETE song FROM song
+ INNER JOIN album ON song.album_id = album.id
+ WHERE album.artist_id = ?",
+ id
+ )
+ .execute(&self.db_pool)
+ .await;
+
+ let _ = sqlx::query!(
+ "DELETE FROM album
+ WHERE artist_id = ?",
+ id
+ )
+ .execute(&self.db_pool)
+ .await;
+
+ sqlx::query!(
+ "DELETE FROM artist
+ WHERE id = ?",
+ id
+ )
+ .execute(&self.db_pool)
+ .await
+ }
}
diff --git a/src/main.rs b/src/main.rs
index f55a17c..0bd3087 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -19,9 +19,11 @@ async fn main() -> std::io::Result<()> {
env_logger::init();
dotenv().ok();
- let db_raw = database::DatabaseWrapper::new()
- .await
- .expect("Something went wrong while creating DatabaseWrapper");
+ let db_raw = match database::DatabaseWrapper::new().await {
+ Ok(res) => res,
+ Err(_) => panic!("Error creating database wrapper"),
+ };
+
let db = Arc::new(db_raw);
let app_state = AppState { database: db };
@@ -30,9 +32,17 @@ async fn main() -> std::io::Result<()> {
.app_data(web::Data::new(app_state.clone()))
.route("/", web::get().to(root))
.service(routes::song::get_song)
- .service(routes::album::album)
+ .service(routes::song::post_song)
+ .service(routes::song::put_song)
+ .service(routes::song::delete_song)
+ .service(routes::album::get_album)
+ .service(routes::album::post_album)
+ .service(routes::album::put_album)
+ .service(routes::album::delete_album)
.service(routes::artist::get_artist)
.service(routes::artist::post_artist)
+ .service(routes::artist::put_artist)
+ .service(routes::artist::delete_artist)
.service(routes::search_results::search_results)
})
.bind(("127.0.0.1", 8000))?
diff --git a/src/routes/album.rs b/src/routes/album.rs
index dbf04f0..ab82e2e 100644
--- a/src/routes/album.rs
+++ b/src/routes/album.rs
@@ -1,6 +1,6 @@
-use crate::database::Album;
+use crate::database::{Album, AlbumPost, AlbumPut, Delete};
use crate::AppState;
-use actix_web::{get, web, HttpResponse};
+use actix_web::{delete, get, post, put, web, HttpResponse};
use serde::Deserialize;
#[derive(Deserialize)]
@@ -11,11 +11,11 @@ struct AlbumQueryOptions {
}
#[get("/album")]
-pub async fn album(
+pub async fn get_album(
app_state: web::Data<AppState>,
get_args: web::Query<AlbumQueryOptions>,
) -> HttpResponse {
- let default: String = String::from("");
+ let default = String::from("");
if get_args.id.is_some() {
let id: &str = get_args.id.as_ref().unwrap_or(&default);
@@ -44,3 +44,51 @@ pub async fn album(
Err(e) => HttpResponse::Ok().body(format!("{}", e)),
}
}
+
+#[post("/album")]
+pub async fn post_album(
+ app_state: web::Data<AppState>,
+ post_data: web::Json<AlbumPost>,
+) -> HttpResponse {
+ match app_state
+ .database
+ .create_album(post_data.into_inner())
+ .await
+ {
+ Ok(_) => HttpResponse::Ok().body("Post succeeded\n"),
+ Err(e) => HttpResponse::Ok().body(format!("{}", e)),
+ }
+}
+
+#[put("/album")]
+pub async fn put_album(
+ app_state: web::Data<AppState>,
+ post_data: web::Json<AlbumPut>,
+) -> HttpResponse {
+ match app_state.database.edit_album(post_data.into_inner()).await {
+ Ok(_) => HttpResponse::Ok().body("Put succeeded\n"),
+ Err(e) => HttpResponse::Ok().body(format!("{}", e)),
+ }
+}
+
+#[delete("/album")]
+pub async fn delete_album(
+ app_state: web::Data<AppState>,
+ post_data: web::Json<Delete>,
+) -> HttpResponse {
+ let id: i32 = post_data
+ .into_inner()
+ .id
+ .unwrap_or(String::default())
+ .parse::<i32>()
+ .unwrap_or(-1);
+
+ if id == -1 {
+ return HttpResponse::Ok().body("Invalid id value, code not executed\n");
+ }
+
+ match app_state.database.delete_album(id).await {
+ Ok(_) => HttpResponse::Ok().body("Deletion succeeded\n"),
+ Err(e) => HttpResponse::Ok().body(format!("There was an issue in the request:\n{}", e)),
+ }
+}
diff --git a/src/routes/artist.rs b/src/routes/artist.rs
index dc481f3..81ae773 100644
--- a/src/routes/artist.rs
+++ b/src/routes/artist.rs
@@ -1,6 +1,6 @@
-use crate::database::Artist;
+use crate::database::{Artist, ArtistPost, ArtistPut, Delete};
use crate::AppState;
-use actix_web::{get, post, web, HttpResponse};
+use actix_web::{delete, get, post, put, web, HttpResponse};
use serde::Deserialize;
#[derive(Deserialize)]
@@ -14,7 +14,7 @@ pub async fn get_artist(
app_state: web::Data<AppState>,
get_args: web::Query<ArtistQueryOptions>,
) -> HttpResponse {
- let default: String = String::from("");
+ let default = String::from("");
if get_args.id.is_some() {
let id: &str = get_args.id.as_ref().unwrap_or(&default);
@@ -43,10 +43,48 @@ pub async fn get_artist(
#[post("/artist")]
pub async fn post_artist(
-// app_state: web::Data<AppState>,
+ app_state: web::Data<AppState>,
+ post_data: web::Json<ArtistPost>,
) -> HttpResponse {
-// if get_args.body.is_some() {
-// HttpResponse::Ok().json("{}");
-// }
- HttpResponse::Ok().body("bad")
+ match app_state
+ .database
+ .create_artist(post_data.into_inner())
+ .await
+ {
+ Ok(_) => HttpResponse::Ok().body("Post succeeded\n"),
+ Err(e) => HttpResponse::Ok().body(format!("{}", e)),
+ }
+}
+
+#[put("/artist")]
+pub async fn put_artist(
+ app_state: web::Data<AppState>,
+ post_data: web::Json<ArtistPut>,
+) -> HttpResponse {
+ match app_state.database.edit_artist(post_data.into_inner()).await {
+ Ok(_) => HttpResponse::Ok().body("Put succeeded\n"),
+ Err(e) => HttpResponse::Ok().body(format!("{}", e)),
+ }
+}
+
+#[delete("/artist")]
+pub async fn delete_artist(
+ app_state: web::Data<AppState>,
+ post_data: web::Json<Delete>,
+) -> HttpResponse {
+ let id: i32 = post_data
+ .into_inner()
+ .id
+ .unwrap_or(String::default())
+ .parse::<i32>()
+ .unwrap_or(-1);
+
+ if id == -1 {
+ return HttpResponse::Ok().body("Invalid id value, code not executed\n");
+ }
+
+ match app_state.database.delete_artist(id).await {
+ Ok(_) => HttpResponse::Ok().body("Deletion succeeded\n\n"),
+ Err(e) => HttpResponse::Ok().body(format!("There was an issue in the request:\n{}", e)),
+ }
}
diff --git a/src/routes/song.rs b/src/routes/song.rs
index 97513f6..850c759 100644
--- a/src/routes/song.rs
+++ b/src/routes/song.rs
@@ -1,6 +1,6 @@
-use crate::database::Song;
+use crate::database::{Delete, Song, SongPost, SongPut};
use crate::AppState;
-use actix_web::{get, web, HttpResponse};
+use actix_web::{delete, get, post, put, web, HttpResponse};
use serde::Deserialize;
#[derive(Deserialize)]
@@ -16,7 +16,7 @@ pub async fn get_song(
app_state: web::Data<AppState>,
get_args: web::Query<SongQueryOptions>,
) -> HttpResponse {
- let default: String = String::from("");
+ let default = String::from("");
if get_args.id.is_some() {
let id: &str = get_args.id.as_ref().unwrap_or(&default);
@@ -51,10 +51,46 @@ pub async fn get_song(
}
}
-// #[post("/song")]
-// pub async fn post_song(
-// app_state: web::Data<AppState>,
-// get_args: web::Query<SongQueryOptions>,
-// ) -> HttpResponse {
-// HttpResponse::Ok().body("Post carried successfully")
-// }
+#[post("/song")]
+pub async fn post_song(
+ app_state: web::Data<AppState>,
+ post_data: web::Json<SongPost>,
+) -> HttpResponse {
+ match app_state.database.create_song(post_data.into_inner()).await {
+ Ok(_) => HttpResponse::Ok().body("Post succeeded\n"),
+ Err(e) => HttpResponse::Ok().body(format!("{}", e)),
+ }
+}
+
+#[put("/song")]
+pub async fn put_song(
+ app_state: web::Data<AppState>,
+ post_data: web::Json<SongPut>,
+) -> HttpResponse {
+ match app_state.database.edit_song(post_data.into_inner()).await {
+ Ok(_) => HttpResponse::Ok().body("Put succeeded\n"),
+ Err(e) => HttpResponse::Ok().body(format!("{}", e)),
+ }
+}
+
+#[delete("/song")]
+pub async fn delete_song(
+ app_state: web::Data<AppState>,
+ post_data: web::Json<Delete>,
+) -> HttpResponse {
+ let id: i32 = post_data
+ .into_inner()
+ .id
+ .unwrap_or(String::default())
+ .parse::<i32>()
+ .unwrap_or(-1);
+
+ if id == -1 {
+ return HttpResponse::Ok().body("Invalid id value, code not executed\n");
+ }
+
+ match app_state.database.delete_song(id).await {
+ Ok(_) => HttpResponse::Ok().body("Deletion succeeded\n"),
+ Err(e) => HttpResponse::Ok().body(format!("There was an issue in the request:\n{}", e)),
+ }
+}