diff --git a/Controllers/AlbumController.cs b/Controllers/AlbumController.cs index c887945..bf66ce7 100644 --- a/Controllers/AlbumController.cs +++ b/Controllers/AlbumController.cs @@ -13,6 +13,7 @@ namespace Icarus.Controllers { [Route("api/album")] [ApiController] + // TODO: Secure the HTTP endpoint routes with Auth0 grants. #39 public class AlbumController : ControllerBase { #region Fields diff --git a/Controllers/ArtistController.cs b/Controllers/ArtistController.cs index ed5ee84..b0e18e3 100644 --- a/Controllers/ArtistController.cs +++ b/Controllers/ArtistController.cs @@ -8,11 +8,13 @@ using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Logging; using Icarus.Models; +using Icarus.Models.Context; namespace Icarus.Controllers { [Route("api/artist")] [ApiController] + // TODO: Secure the HTTP endpoint routes with Auth0 grants. #39 public class ArtistController : ControllerBase { #region Fields @@ -36,19 +38,45 @@ namespace Icarus.Controllers [HttpGet] public IActionResult Get() { - List artists = new List(); - // TODO: Implement functionality + ArtistStoreContext artistStoreContext = HttpContext + .RequestServices + .GetService(typeof(ArtistStoreContext)) as ArtistStoreContext; - return Ok(artists); + var artists = artistStoreContext.GetArtists(); + + if (artists.Count > 0) + { + return Ok(artists); + } + else + { + return NotFound(); + } } [HttpGet("{id}")] public IActionResult Get(int id) { - Artist artist = new Artist(); - // TODO: Implement functionality + Artist artist = new Artist + { + ArtistId = id + }; + + ArtistStoreContext artistStoreContext = HttpContext + .RequestServices + .GetService(typeof(ArtistStoreContext)) as ArtistStoreContext; - return Ok(artist); + if (artistStoreContext.DoesArtistExist(artist)) + { + artist = artistStoreContext.GetArtist(artist); + + + return Ok(artist); + } + else + { + return NotFound(); + } } #endregion } diff --git a/Controllers/LoginController.cs b/Controllers/LoginController.cs index 79da1ec..611e5f4 100644 --- a/Controllers/LoginController.cs +++ b/Controllers/LoginController.cs @@ -38,10 +38,17 @@ namespace Icarus.Controllers #region HTTP endpoints public IActionResult Post([FromBody] User user) { + // TODO: Secure this HTTP endpoint. #38 + // Currently there is no check done to determine whether or not the + // user's password sent with the request matches the password stored + // in the database. In fact there is not check if the user credentials + // sent with this request even exist in the database. I knowingly left + // this bug in here for the sole purpose of making it easier to test + // but it should now be addressed. + UserStoreContext context = HttpContext - .RequestServices - .GetService(typeof(UserStoreContext)) - as UserStoreContext; + .RequestServices + .GetService(typeof(UserStoreContext)) as UserStoreContext; user = context.RetrieveUser(user); Console.WriteLine($"Username: {user.Username}"); diff --git a/Controllers/Managers/SongManager.cs b/Controllers/Managers/SongManager.cs index 8b6deaf..7b3a4b0 100644 --- a/Controllers/Managers/SongManager.cs +++ b/Controllers/Managers/SongManager.cs @@ -490,24 +490,33 @@ namespace Icarus.Controllers.Managers { _results = new DataTable(); } + private void SaveSongToDatabase(Song song, MusicStoreContext songStore, AlbumStoreContext albumStore, ArtistStoreContext artistStore) { - song.AlbumId = 1; + _logger.Info("Starting process to save the song to the database"); + + SaveAlbumToDatabase(ref song, albumStore); + SaveArtistToDatabase(ref song, artistStore); + + _logger.Info($"Song;\nTitle {song.Title}\nAlbum {song.AlbumTitle}\nAlbum Id {song.AlbumId}\nArtist {song.ArtistId}"); + + songStore.SaveSong(song); + } + private void SaveAlbumToDatabase(ref Song song, AlbumStoreContext albumStore) + { + _logger.Info("Starting process to save the album record of the song to the database"); + var album = new Album(); + album.Title = song.AlbumTitle; album.AlbumArtist = song.Artist; + if (!albumStore.DoesAlbumExist(song)) { album.SongCount = 1; - _logger.Info("Saving album to database"); - _logger.Info($"Ablum song count before retrieving from store: {album.SongCount}"); - _logger.Info($"Ablum Id before retrieving from store: {album.AlbumId}"); albumStore.SaveAlbum(album); - _logger.Info("Album suuccessfully saved to database"); album = albumStore.GetAlbum(song); - _logger.Info($"Ablum song count after retrieving from store: {album.SongCount}"); - _logger.Info($"Ablum Id after retrieving from store: {album.AlbumId}"); } else { @@ -517,12 +526,34 @@ namespace Icarus.Controllers.Managers albumStore.UpdateAlbum(album); } - - song.AlbumId = album.AlbumId; - _logger.Info($"Song;\nTitle {song.Title}\nAlbum {song.AlbumTitle}\nAlbum Id {song.AlbumId}"); - songStore.SaveSong(song); - _logger.Info("Successfully saved song to database"); + song.AlbumId = album.AlbumId; + } + private void SaveArtistToDatabase(ref Song song, ArtistStoreContext artistStore) + { + _logger.Info("Starting process to save the artist record of the song to the database"); + + var artist = new Artist(); + + artist.Name = song.Artist; + artist.SongCount = 1; + + if (!artistStore.DoesArtistExist(song)) + { + artist.SongCount = 1; + artistStore.SaveArtist(artist); + artist = artistStore.GetArtist(song); + } + else + { + var artistRetrieved = artistStore.GetArtist(song); + artist.ArtistId = artistRetrieved.ArtistId; + artist.SongCount = artistRetrieved.SongCount + 1; + + artistStore.UpdateArtist(artist); + } + + song.ArtistId = artist.ArtistId; } private async Task PopulateSongDetails() diff --git a/Controllers/Managers/TokenManager.cs b/Controllers/Managers/TokenManager.cs index 1cb517e..9af7cbc 100644 --- a/Controllers/Managers/TokenManager.cs +++ b/Controllers/Managers/TokenManager.cs @@ -43,14 +43,15 @@ namespace Icarus.Controllers.Managers var tokenRequest = RetrieveTokenRequest(); var tokenObject = JsonConvert.SerializeObject(tokenRequest); request.AddParameter("application/json; charset=utf-8", - tokenObject, ParameterType.RequestBody); + tokenObject, ParameterType.RequestBody); + request.RequestFormat = DataFormat.Json; IRestResponse response = client.Execute(request); var tokenResult = JsonConvert - .DeserializeObject(response.Content); + .DeserializeObject(response.Content); return new LoginResult { @@ -81,9 +82,9 @@ namespace Icarus.Controllers.Managers _grantType = "client_credentials"; _url = $"https://{_config["Auth0:Domain"]}"; - //PrintCredentials(); } + #region Testing Methods // For testing purposes private void PrintCredentials() { @@ -94,6 +95,7 @@ namespace Icarus.Controllers.Managers Console.WriteLine($"Url: {_url}"); } #endregion + #endregion #region Classes diff --git a/Controllers/RegisterController.cs b/Controllers/RegisterController.cs index dfc7f58..8844c5c 100644 --- a/Controllers/RegisterController.cs +++ b/Controllers/RegisterController.cs @@ -14,10 +14,10 @@ using Icarus.Models.Context; namespace Icarus.Controllers { - [Route("api/register")] - [ApiController] - public class RegisterController : ControllerBase - { + [Route("api/register")] + [ApiController] + public class RegisterController : ControllerBase + { #region Fields private IConfiguration _config; #endregion @@ -30,14 +30,13 @@ namespace Icarus.Controllers #region Constructor public RegisterController(IConfiguration config) { - _config = config; + _config = config; } #endregion - - [HttpPost] - public void Post([FromBody] User user) - { + [HttpPost] + public void Post([FromBody] User user) + { Console.WriteLine($"Username: {user.Username}"); Console.WriteLine($"Password: {user.Password}"); @@ -46,10 +45,11 @@ namespace Icarus.Controllers user.EmailVerified = false; Console.WriteLine($"Hashed Password: {user.Password}"); - UserStoreContext context = HttpContext.RequestServices - .GetService(typeof(UserStoreContext)) - as UserStoreContext; + UserStoreContext context = HttpContext + .RequestServices + .GetService(typeof(UserStoreContext)) as UserStoreContext; + context.SaveUser(user); - } - } + } + } } diff --git a/Controllers/SongCompressedDataControllers.cs b/Controllers/SongCompressedDataControllers.cs index 672a57c..a03f4f2 100644 --- a/Controllers/SongCompressedDataControllers.cs +++ b/Controllers/SongCompressedDataControllers.cs @@ -17,50 +17,50 @@ using Icarus.Models.Context; namespace Icarus.Controllers { - [Route("api/song/compressed/data")] - [ApiController] - public class SongCompressedDataController : ControllerBase - { - #region Fields - private IConfiguration _config; - private SongManager _songMgr; - private string _songTempDir; - private string _archiveDir; - #endregion - - - #region Properties - #endregion - - - #region Constructor - public SongCompressedDataController(IConfiguration config) + [Route("api/song/compressed/data")] + [ApiController] + public class SongCompressedDataController : ControllerBase { - _config = config; - _songTempDir = _config.GetValue("TemporaryMusicPath"); - _archiveDir = _config.GetValue("ArchivePath"); - } - #endregion + #region Fields + private IConfiguration _config; + private SongManager _songMgr; + private string _songTempDir; + private string _archiveDir; + #endregion - #region API Routes - [HttpGet("{id}")] + #region Properties + #endregion + + + #region Constructor + public SongCompressedDataController(IConfiguration config) + { + _config = config; + _songTempDir = _config.GetValue("TemporaryMusicPath"); + _archiveDir = _config.GetValue("ArchivePath"); + } + #endregion + + + #region API Routes + [HttpGet("{id}")] [Authorize("download:songs")] - public async Task Get(int id) - { - MusicStoreContext context = HttpContext.RequestServices - .GetService(typeof(MusicStoreContext)) - as MusicStoreContext; + public async Task Get(int id) + { + MusicStoreContext context = HttpContext + .RequestServices + .GetService(typeof(MusicStoreContext)) as MusicStoreContext; - SongCompression cmp = new SongCompression(_archiveDir); + SongCompression cmp = new SongCompression(_archiveDir); - Console.WriteLine($"Archive directory root: {_archiveDir}"); + Console.WriteLine($"Archive directory root: {_archiveDir}"); - Console.WriteLine("Starting process of retrieving comrpessed song"); - SongData song = await cmp.RetrieveCompressedSong(context.GetSong(id)); + Console.WriteLine("Starting process of retrieving comrpessed song"); + SongData song = await cmp.RetrieveCompressedSong(context.GetSong(id)); - return File(song.Data, "application/x-msdownload", cmp.CompressedSongFilename); - } - #endregion - } + return File(song.Data, "application/x-msdownload", cmp.CompressedSongFilename); + } + #endregion + } } diff --git a/Controllers/SongController.cs b/Controllers/SongController.cs index c7ec954..78f1dbf 100644 --- a/Controllers/SongController.cs +++ b/Controllers/SongController.cs @@ -18,6 +18,7 @@ namespace Icarus.Controllers { [Route("api/song")] [ApiController] + // TODO: Secure the HTTP endpoint routes with Auth0 grants. #39 public class SongController : ControllerBase { #region Fields @@ -44,7 +45,7 @@ namespace Icarus.Controllers [HttpGet] [Authorize("read:song_details")] - public ActionResult> Get() + public IActionResult> Get() { List songs = new List(); Console.WriteLine("Attemtping to retrieve songs"); @@ -56,19 +57,33 @@ namespace Icarus.Controllers songs = context.GetAllSongs(); - return songs; + if (songs.Count > 0) + { + return Ok(songs); + } + else + { + return NotFound(); + } } [HttpGet("{id}")] - public ActionResult Get(int id) + public IActionResult Get(int id) { MusicStoreContext context = HttpContext .RequestServices .GetService(typeof(MusicStoreContext)) as MusicStoreContext; Song song = context.GetSong(id); - - return song; + + if (song.Id != 0) + { + return Ok(song); + } + else + { + return NotFound(); + } } @@ -99,17 +114,5 @@ namespace Icarus.Controllers return Ok(songResult); } - - [HttpDelete("{id}")] - public IActionResult Delete(int id) - { - MusicStoreContext context = HttpContext - .RequestServices - .GetService(typeof(MusicStoreContext)) as MusicStoreContext; - - context.DeleteSong(id); - - return Ok(); - } - } + } } diff --git a/Models/Context/AlbumStoreContext.cs b/Models/Context/AlbumStoreContext.cs index 9a8a47c..d37b2cc 100644 --- a/Models/Context/AlbumStoreContext.cs +++ b/Models/Context/AlbumStoreContext.cs @@ -279,6 +279,8 @@ namespace Icarus.Models.Context private List ParseData(MySqlDataReader reader) { List albums = new List(); + _logger.Info("Retrieving album records"); + while (reader.Read()) { var id = Convert.ToInt32(reader["AlbumId"]); @@ -294,12 +296,15 @@ namespace Icarus.Models.Context }); } + _logger.Info("Album records retrieved"); + return albums; } private Album ParseSingleData(MySqlDataReader reader) { Album album = new Album(); _logger.Info("Retrieving single album record"); + while (reader.Read()) { var id = Convert.ToInt32(reader["AlbumId"]); diff --git a/Models/Context/ArtistStoreContext.cs b/Models/Context/ArtistStoreContext.cs index 2fef7bc..79d09da 100644 --- a/Models/Context/ArtistStoreContext.cs +++ b/Models/Context/ArtistStoreContext.cs @@ -63,6 +63,7 @@ namespace Icarus.Models.Context { try { + _logger.Info("Checking to see if Artist exists"); using (MySqlConnection conn = GetConnection()) { conn.Open(); @@ -70,7 +71,7 @@ namespace Icarus.Models.Context var query = "SELECT * FROM Artist WHERE ArtistId=@ArtistId"; using (MySqlCommand cmd = new MySqlCommand(query, conn)) { - cmd.Parameters.AddWithValue("@AlbumId", artist.ArtistId); + cmd.Parameters.AddWithValue("@ArtistId", artist.ArtistId); using (var reader = cmd.ExecuteReader()) { @@ -90,17 +91,21 @@ namespace Icarus.Models.Context var msg = ex.Message; _logger.Error(msg, "An error occurred"); } + + _logger.Info("Could not successfully retrieve Artist"); + return false; } public bool DoesArtistExist(Song song) { try { + _logger.Info("Checking to see if Artist exists"); using (MySqlConnection conn = GetConnection()) { conn.Open(); - var query = "SELECT * FROM Album WHERE Name=@Name"; + var query = "SELECT * FROM Artist WHERE Name=@Name"; using (MySqlCommand cmd = new MySqlCommand(query, conn)) { cmd.Parameters.AddWithValue("@Name", song.Artist); @@ -123,6 +128,9 @@ namespace Icarus.Models.Context var msg = ex.Message; _logger.Error(msg, "An error occurred"); } + + _logger.Info("Could not successfully retrieve Artist"); + return false; } @@ -131,6 +139,7 @@ namespace Icarus.Models.Context { try { + _logger.Info("Saving artist record"); using (MySqlConnection conn = GetConnection()) { conn.Open(); @@ -145,6 +154,8 @@ namespace Icarus.Models.Context cmd.ExecuteNonQuery(); } } + + _logger.Info("Artist record has successfully been saved"); } catch (Exception ex) { @@ -156,11 +167,24 @@ namespace Icarus.Models.Context { try { + _logger.Info("Updating artist record"); using (MySqlConnection conn = GetConnection()) { conn.Open(); - // TODO: Implement functionality + var query = "UPDATE Artist SET Name=@Name, SongCount" + + "=@SongCount WHERE ArtistId=@ArtistId"; + + using (MySqlCommand cmd = new MySqlCommand(query, conn)) + { + cmd.Parameters.AddWithValue("@Name", artist.Name); + cmd.Parameters.AddWithValue("@SongCount", artist.SongCount); + cmd.Parameters.AddWithValue("@ArtistId", artist.ArtistId); + + cmd.ExecuteNonQuery(); + } } + + _logger.Info("Artist record has successfully been saved"); } catch (Exception ex) { @@ -173,6 +197,7 @@ namespace Icarus.Models.Context { try { + _logger.Info("Retrieving artist record from the database"); using (MySqlConnection conn = GetConnection()) { conn.Open(); @@ -181,6 +206,7 @@ namespace Icarus.Models.Context using (MySqlCommand cmd = new MySqlCommand(query, conn)) { cmd.Parameters.AddWithValue("@ArtistId", artist.ArtistId); + using (var reader = cmd.ExecuteReader()) { artist = ParseSingleData(reader); @@ -201,14 +227,16 @@ namespace Icarus.Models.Context Artist artist = new Artist(); try { + _logger.Info("Retrieving artist record from the database"); using (MySqlConnection conn = GetConnection()) { conn.Open(); - var query = "SELECT * FROM Artist WHERE ArtistId=@ArtistId"; + var query = "SELECT * FROM Artist WHERE Name=@Name"; using (MySqlCommand cmd = new MySqlCommand(query, conn)) { - cmd.Parameters.AddWithValue("@ArtistId", song.ArtistId); + cmd.Parameters.AddWithValue("@Name", song.Artist); + using (var reader = cmd.ExecuteReader()) { artist = ParseSingleData(reader); @@ -243,6 +271,8 @@ namespace Icarus.Models.Context }); } + _logger.Info("Artist records retrieved"); + return artists; } @@ -260,6 +290,8 @@ namespace Icarus.Models.Context artist.SongCount = songCount; } + _logger.Info("Single artist record retrieved"); + return artist; } #endregion diff --git a/Models/Context/MusicStoreContext.cs b/Models/Context/MusicStoreContext.cs index 0d37075..3096701 100644 --- a/Models/Context/MusicStoreContext.cs +++ b/Models/Context/MusicStoreContext.cs @@ -30,13 +30,15 @@ namespace Icarus.Models.Context { try { + _logger.Info("Saving song to the database"); using (MySqlConnection conn = GetConnection()) { conn.Open(); string query = "INSERT INTO Song(Title, AlbumTitle, Artist," + - " Year, Genre, Duration, Filename, SongPath, AlbumId) " + - "VALUES(@Title, @AlbumTitle, @Artist, @Year, @Genre, " + - "@Duration, @Filename, @SongPath, @AlbumId)"; + " Year, Genre, Duration, Filename, SongPath, AlbumId, " + + "ArtistId) VALUES(@Title, @AlbumTitle, @Artist, @Year, " + + "@Genre, @Duration, @Filename, @SongPath, @AlbumId, " + + "@ArtistId)"; using (MySqlCommand cmd = new MySqlCommand(query, conn)) { cmd.Parameters.AddWithValue("@Title", song.Title); @@ -48,10 +50,12 @@ namespace Icarus.Models.Context cmd.Parameters.AddWithValue("@Filename", song.Filename); cmd.Parameters.AddWithValue("@SongPath", song.SongPath); cmd.Parameters.AddWithValue("@AlbumId", song.AlbumId); + cmd.Parameters.AddWithValue("@ArtistId", song.ArtistId); cmd.ExecuteNonQuery(); } } + _logger.Info("Successfully saved song to the database"); } catch(Exception ex) { @@ -125,8 +129,10 @@ namespace Icarus.Models.Context public List GetAllSongs() { List songs = new List(); + try { + _logger.Info("Retrieving songs from the database"); using (MySqlConnection conn = GetConnection()) { conn.Open(); @@ -134,6 +140,8 @@ namespace Icarus.Models.Context MySqlCommand cmd = new MySqlCommand(query, conn); using (var reader = cmd.ExecuteReader()) { + songs = ParseData(reader); + /** while (reader.Read()) { songs.Add(new Song @@ -149,6 +157,7 @@ namespace Icarus.Models.Context SongPath = reader["SongPath"].ToString() }); } + */ } } } @@ -169,6 +178,7 @@ namespace Icarus.Models.Context try { + _logger.Info("Retrieving song from database"); using (MySqlConnection conn = GetConnection()) { conn.Open(); @@ -179,6 +189,8 @@ namespace Icarus.Models.Context using (var reader = cmd.ExecuteReader()) { + song = ParseSingleData(reader); + /** while (reader.Read()) { song = new Song @@ -194,6 +206,7 @@ namespace Icarus.Models.Context SongPath = reader["SongPath"].ToString() }; } + */ } } _logger.Info("Song found");