diff --git a/Controllers/QuoteController.cs b/Controllers/QuoteController.cs index 4ab53b0..d743572 100644 --- a/Controllers/QuoteController.cs +++ b/Controllers/QuoteController.cs @@ -75,7 +75,7 @@ public class QuotesController : ControllerBase // GET /api/v1/quotes/{id} /// - /// [AUTH] Get specified quote summary + /// [AUTHED] Get specified quote summary /// /// The quote id in question /// A quote: id, quote content and author, imageUrl and categories if successful, otherwise: error message @@ -221,51 +221,111 @@ public class QuotesController : ControllerBase return Ok(dto); } - + // DELETE /api/v1/quotes/{id} + /// + /// [AUTHED] Delete a quote + /// + /// + /// Deletes a quote, granted it exists.
+ ///
+ /// + /// Is this the best practice? Marking the quote as hidden is also an option. + /// ~eee4 + ///
+ /// Json with status + /// Quote id which will be deleted + /// Returned on valid request + /// Returned when no such quote exists [HttpDelete("{id}")] - [ProducesResponseType(204)] - [ProducesResponseType(typeof(ErrorDTO), 404)] [Authorize] [EnableCors] + [ProducesResponseType(204)] + [ProducesResponseType(typeof(ErrorDTO), 404)] public async Task DeleteQuote(int id) { - var quote = await _db.Quotes - .FirstOrDefaultAsync(q => q.Id == id); - if(quote==null) return NotFound(new { status = "error", error_msg = "Quote not found" }); + // (Attempt to) find the quote + Quote? quote = await _db.Quotes + .FirstOrDefaultAsync(q => q.Id == id); + // Failed? + if (quote == null) + return NotFound(new { status = "error", error_msg = "Quote not found" }); + + // If succeded, remove the quote _db.Quotes.Remove(quote); await _db.SaveChangesAsync(); - return Ok(); + + // ====================================================================== // + // Important! // + // Is this the best we can do? Won't marking the quote as "hidden" // + // be better than explicitly deleting it? ~eee4 // + // ====================================================================== // + + // Return ok + return Ok(new { Status = "ok" }); } + // PATCH /api/v1/quotes/{id} + /// + /// [AUTHED] Modify an existing quote + /// + /// + /// Modifies an existing quote. + ///

+ /// Warning! + /// We don't check the user id which created the quote. + /// In case of single-user instances, this should not be a problem. + /// This might become one, if we want users with non-admin roles; + /// that would need some proper ACL checks here (with the help of GUHF). + ///

+ /// Important! + /// Image handling works the same as with creating new quote. + /// This means that images not present in the DB will be added automatically. + ///

+ /// Important! + /// "categories = null" is not the same as "categories = []"! + /// While "categories = null" will not alter the quote's categories, + /// "categories = []" will (and in turn, empty each and every present category)!
+ /// Be careful when handling user-provided categories! + ///
+ /// Newly modified quote as a DTO + /// Quote to be modified + /// Updated quote form data + /// Returned on valid request + /// Returned when request text or author is empty (or whitespace) + /// Returned when no such quote exists [HttpPatch("{id}")] [Authorize] [EnableCors] [ProducesResponseType(typeof(QuoteShortDTO), 200)] [ProducesResponseType(typeof(ErrorDTO), 400)] [ProducesResponseType(typeof(ErrorDTO), 404)] - public async Task EditQuote(int id, [FromBody] QuoteShortDTO updatedQuote) { - var Quote = await _db.Quotes + // Try to find the quote in question + Quote? quote = await _db.Quotes .Include(q => q.QuoteCategories) - // include image? .FirstOrDefaultAsync(q => q.Id == id); - if (Quote == null) return NotFound(new { status = "error", error_msg = "Quote not found" }); + // Failed? + if (quote == null) + return NotFound(new { status = "error", error_msg = "Quote not found" }); + + // Is quote contents or author empty? if (string.IsNullOrWhiteSpace(updatedQuote.Text) || string.IsNullOrWhiteSpace(updatedQuote.Author)) - { return BadRequest(new ErrorDTO { Status = "error", Error_msg = "Text and author are required." }); - } - Quote.Text = updatedQuote.Text; - Quote.Author = updatedQuote.Author; - Quote.LastUpdatedAt = DateTime.UtcNow; + // Alter the quote's content + quote.Text = updatedQuote.Text; + quote.Author = updatedQuote.Author; + quote.LastUpdatedAt = DateTime.UtcNow; + // Try to find the image inside the DB Image? image = null; if (!string.IsNullOrEmpty(updatedQuote.ImageUrl)) { image = await _db.Images.FirstOrDefaultAsync(i => i.Url == updatedQuote.ImageUrl); + // Failed? Just insert it yourself if (image == null) { image = new Image { Url = updatedQuote.ImageUrl }; @@ -273,22 +333,29 @@ public class QuotesController : ControllerBase await _db.SaveChangesAsync(); } } - Quote.Image = image; + quote.Image = image; + // Don't touch categories if they are explicitly null if (updatedQuote.Categories == null) { } + // If they aren't else if (updatedQuote.Categories.Any()) { - var categoriesFromDb = await _db.Categories + // Get all the categories associated with a quote from DB + List categoriesFromDb = await _db.Categories .Where(c => updatedQuote.Categories.Contains(c.Name)) .ToListAsync(); - // Dodaj nowe kategorie, których nie ma w bazie - var existingNames = categoriesFromDb + // Determine which ones are already present, and which to add + IEnumerable existingNames = categoriesFromDb .Select(c => c.Name); - var newNames = updatedQuote.Categories.Except(existingNames).ToList(); + List newNames = updatedQuote.Categories + .Except(existingNames) + .ToList(); + // For all the categories not present foreach (var name in newNames) { + // Add them to the DB var newCat = new Category { Name = name, Description = string.Empty, @@ -298,25 +365,28 @@ public class QuotesController : ControllerBase categoriesFromDb.Add(newCat); } + // If any categories were added, save changes if (newNames.Any()) await _db.SaveChangesAsync(); - // Przypisz cytatowi nowe kategorie - Quote.QuoteCategories = categoriesFromDb + // Assign all the new categories to the quote + quote.QuoteCategories = categoriesFromDb .Select(cat => new QuoteCategory { CategoryId = cat.Id, - QuoteId = Quote.Id + QuoteId = quote.Id }) .ToList(); } else { - // Jeśli brak kategorii w dto, czyścić przypisane kategorie? - Quote.QuoteCategories.Clear(); + // No categories (empty list) inside DTO? + // Clear them all! + quote.QuoteCategories.Clear(); } + // Save changes, return new quote as a DTO await _db.SaveChangesAsync(); - return Ok(Quote.ToQuoteShortDTO()); + return Ok(quote.ToQuoteShortDTO()); } }