Skip to content

Feature/challenge 9#886

Open
brenoamin wants to merge 2 commits intoRezaSi:mainfrom
brenoamin:feature/challenge-9
Open

Feature/challenge 9#886
brenoamin wants to merge 2 commits intoRezaSi:mainfrom
brenoamin:feature/challenge-9

Conversation

@brenoamin
Copy link
Contributor

Overview

This project implements a RESTful API in Go for managing books. The API supports full CRUD operations, search functionality, proper error handling, and uses an in-memory data store. The solution follows a clean architecture with clear separation between handler, service, and repository layers.


What Was Implemented

API Endpoints

  • GET /api/books — Retrieve all books
  • GET /api/books/{id} — Retrieve a book by ID
  • POST /api/books — Create a new book
  • PUT /api/books/{id} — Update an existing book
  • DELETE /api/books/{id} — Delete a book
  • GET /api/books/search?author={author} — Search books by author
  • GET /api/books/search?title={title} — Search books by title

Core Components

Domain Model

  • Implemented the Book struct with all required fields.
  • JSON tags applied to ensure correct API responses.

Repository Layer

  • Implemented an in-memory repository using a map[string]*Book.
  • Added thread-safety using sync.RWMutex.
  • Implemented all methods defined in BookRepository.

Service Layer

  • Implemented BookService to encapsulate business logic.
  • Delegates persistence operations to the repository.
  • Acts as a clean boundary between handlers and data access.

Handler Layer

  • Implemented HTTP handlers for all required endpoints.
  • Correctly extracted path parameters using gorilla/mux.
  • Split complex handlers (e.g. search) into smaller, focused methods.
  • Ensured handlers return after writing error responses to avoid multiple writes.

Error Handling & Status Codes

  • 200 OK — Successful GET, PUT, DELETE
  • 201 Created — Successful POST
  • 400 Bad Request — Invalid input or missing parameters
  • 404 Not Found — Resource not found
  • 500 Internal Server Error — Unexpected server errors

All responses are returned in JSON format.


Validation

  • Required fields are validated on book creation.
  • Search endpoint validates that at least one query parameter (title or author) is provided.
  • Non-existing resources return proper 404 responses.

Architecture Notes

  • Clear separation of concerns (Handler → Service → Repository).
  • No router creation inside handlers; routing is configured once at startup.
  • Handlers remain thin and focused on HTTP concerns.

Testing

  • The implementation satisfies all provided test cases.
  • Verified locally using:
    go test -v

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 16, 2025

Walkthrough

This pull request adds two new Go solutions: a simple program for Challenge 1 that reads two integers, computes their sum, and prints the result; and a comprehensive REST API for Challenge 9 that implements a Book Management system with repository, service, and handler layers using in-memory storage.

Changes

Cohort / File(s) Change Summary
Challenge 1 Solution
challenge-1/submissions/brenoamin/solution-template.go
Adds a main function that reads two integers from stdin in "%d, %d" format, handles errors, and computes their sum via the exported Sum(a int, b int) int function, then prints the result.
Challenge 9 REST API
challenge-9/submissions/brenoamin/solution-template.go
Introduces a complete Book Management REST API with: Book data model, BookRepository interface and InMemoryBookRepository implementation for thread-safe CRUD operations, BookService interface and DefaultBookService for business logic delegation, BookHandler for HTTP routing and endpoint handlers (GET/POST/PUT/DELETE on /api/books), error handling utilities, and HTTP server initialization on port 8080.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

  • Challenge 9's multi-layer architecture (handler → service → repository) introduces several new types and methods that require verification of interface contracts, thread-safety of the in-memory store, HTTP routing correctness, and consistency of CRUD operations across layers.
  • Challenge 1's simplicity is offset by the heterogeneity of changes (one trivial addition vs. one substantial API implementation).

Possibly related PRs

Pre-merge checks

❌ Failed checks (1 warning, 1 inconclusive)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 62.50% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
Title check ❓ Inconclusive The PR title 'Feature/challenge 9' is vague and does not clearly convey the main change; it lacks specificity about implementing a Book Management API. Use a more descriptive title like 'Implement RESTful Book Management API with CRUD operations' to clearly communicate the main feature being added.
✅ Passed checks (1 passed)
Check name Status Explanation
Description check ✅ Passed The PR description is comprehensive and directly related to the changeset, providing detailed overview of the implemented Book Management API with endpoints, components, error handling, and architecture.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 8

🧹 Nitpick comments (2)
challenge-9/submissions/brenoamin/solution-template.go (2)

154-160: Optional: Service method adds no value.

The GetBookByID service method simply delegates to the repository and re-wraps the same error. Consider whether this layer adds meaningful business logic, or if handlers could call the repository directly for simple CRUD operations.


100-110: Consider case-insensitive search.

The search methods use strings.Contains for case-sensitive substring matching. Depending on requirements, you may want case-insensitive search for better user experience.

Example using case-insensitive comparison:

if strings.Contains(strings.ToLower(book.Author), strings.ToLower(author)) {
    books = append(books, book)
}

Also applies to: 112-123

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 774fc04 and a48aa72.

📒 Files selected for processing (2)
  • challenge-1/submissions/brenoamin/solution-template.go (1 hunks)
  • challenge-9/submissions/brenoamin/solution-template.go (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
challenge-9/submissions/brenoamin/solution-template.go (2)
challenge-13/submissions/Gandook/solution-template.go (1)
  • ID (144-144)
challenge-9/submissions/mick4711/solution-template.go (5)
  • GetAllBooks (18-18)
  • GetBookByID (20-20)
  • CreateBook (19-19)
  • UpdateBook (21-21)
  • DeleteBook (22-22)
🔇 Additional comments (3)
challenge-1/submissions/brenoamin/solution-template.go (2)

7-19: LGTM!

The main function correctly reads two integers, handles potential input errors, and computes their sum.


21-24: LGTM!

The Sum function is correctly implemented with a clear signature and straightforward logic.

challenge-9/submissions/brenoamin/solution-template.go (1)

72-86: Critical: Missing return statements after error conditions.

The Create method has two error conditions (lines 76 and 79) that return errors but don't exit the function. This allows execution to continue, potentially adding invalid or duplicate books to the repository.

Apply this diff to fix:

 func (r *InMemoryBookRepository) Create(book *Book) error {
 	r.mu.Lock()
 	defer r.mu.Unlock()
 	if book.Title == "" || book.Author == "" || book.ISBN == "" || book.PublishedYear == 0 || book.Description == "" {
 		return errors.New("book is missing required data")
 	}
 	if _, ok := r.books[book.ID]; ok {
 		return errors.New("book already exists")
 	}
 	if book.ID == "" {
 		book.ID = uuid.NewString()
 	}
 	r.books[book.ID] = book
 	return nil
 }

Note: The current code already has return statements on lines 76 and 79, but they are part of error creation. The logic is actually correct - I apologize for the confusion. However, ensure these paths are tested.

Likely an incorrect or invalid review comment.

Comment on lines +88 to +93
func (r *InMemoryBookRepository) Update(id string, book *Book) error {
r.mu.Lock()
defer r.mu.Unlock()
r.books[id] = book
return nil
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Update should verify book existence before modification.

The Update method unconditionally assigns the book to the map without checking if it exists. This could silently create new entries when updating non-existent books, bypassing validation that only Create performs.

Apply this diff:

 func (r *InMemoryBookRepository) Update(id string, book *Book) error {
 	r.mu.Lock()
 	defer r.mu.Unlock()
+	if _, ok := r.books[id]; !ok {
+		return errors.New("book not found")
+	}
 	r.books[id] = book
 	return nil
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
func (r *InMemoryBookRepository) Update(id string, book *Book) error {
r.mu.Lock()
defer r.mu.Unlock()
r.books[id] = book
return nil
}
func (r *InMemoryBookRepository) Update(id string, book *Book) error {
r.mu.Lock()
defer r.mu.Unlock()
if _, ok := r.books[id]; !ok {
return errors.New("book not found")
}
r.books[id] = book
return nil
}
🤖 Prompt for AI Agents
In challenge-9/submissions/brenoamin/solution-template.go around lines 88 to 93,
the Update method currently overwrites r.books[id] without checking existence;
modify it to first lock (keep r.mu.Lock()/defer r.mu.Unlock()), then check if
the book exists with something like: if _, ok := r.books[id]; !ok { return
fmt.Errorf("book not found") } (or return the package's predefined NotFound
error if one exists), and only then assign r.books[id] = book; this prevents
silently creating new entries when updating non-existent books.

Comment on lines +202 to +215
func (h *BookHandler) HandleBooks(w http.ResponseWriter, r *http.Request) {
// Use the path and method to determine the appropriate action
// Call the service methods accordingly
// Return appropriate status codes and JSON responses
router := mux.NewRouter()
router.HandleFunc("/api/books", h.getAllBooks).Methods("GET")
router.HandleFunc("/api/books", h.createBook).Methods("POST")
router.HandleFunc("/api/books/search", h.searchBooksBy).Methods("GET")
router.HandleFunc("/api/books/{id}", h.getBookById).Methods("GET")
router.HandleFunc("/api/books/{id}", h.updateBook).Methods("PUT")
router.HandleFunc("/api/books/{id}", h.deleteBook).Methods("DELETE")

router.ServeHTTP(w, r)
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Critical: Router created on every request causes severe performance degradation.

This method creates a new mux.Router and re-registers all routes on every single HTTP request. Router setup should happen once at application startup, not per-request.

Refactor to create the router once in main:

-// HandleBooks processes the book-related endpoints
-func (h *BookHandler) HandleBooks(w http.ResponseWriter, r *http.Request) {
-	// Use the path and method to determine the appropriate action
-	// Call the service methods accordingly
-	// Return appropriate status codes and JSON responses
-	router := mux.NewRouter()
-	router.HandleFunc("/api/books", h.getAllBooks).Methods("GET")
-	router.HandleFunc("/api/books", h.createBook).Methods("POST")
-	router.HandleFunc("/api/books/search", h.searchBooksBy).Methods("GET")
-	router.HandleFunc("/api/books/{id}", h.getBookById).Methods("GET")
-	router.HandleFunc("/api/books/{id}", h.updateBook).Methods("PUT")
-	router.HandleFunc("/api/books/{id}", h.deleteBook).Methods("DELETE")
-
-	router.ServeHTTP(w, r)
-}
+// SetupRoutes configures all book-related routes on the provided router
+func (h *BookHandler) SetupRoutes(router *mux.Router) {
+	router.HandleFunc("/api/books", h.getAllBooks).Methods("GET")
+	router.HandleFunc("/api/books", h.createBook).Methods("POST")
+	router.HandleFunc("/api/books/search", h.searchBooksBy).Methods("GET")
+	router.HandleFunc("/api/books/{id}", h.getBookById).Methods("GET")
+	router.HandleFunc("/api/books/{id}", h.updateBook).Methods("PUT")
+	router.HandleFunc("/api/books/{id}", h.deleteBook).Methods("DELETE")
+}

Then update main:

 func main() {
 	// Initialize the repository, service, and handler
 	repo := NewInMemoryBookRepository()
 	service := NewBookService(repo)
 	handler := NewBookHandler(service)
 
 	// Create a new router and register endpoints
-	http.HandleFunc("/api/books", handler.HandleBooks)
-	http.HandleFunc("/api/books/", handler.HandleBooks)
+	router := mux.NewRouter()
+	handler.SetupRoutes(router)
 
 	// Start the server
 	log.Println("Server starting on :8080")
-	if err := http.ListenAndServe(":8080", nil); err != nil {
+	if err := http.ListenAndServe(":8080", router); err != nil {
 		log.Fatalf("Failed to start server: %v", err)
 	}
 }

Committable suggestion skipped: line range outside the PR's diff.

Comment on lines +217 to +223
func (h *BookHandler) getAllBooks(w http.ResponseWriter, r *http.Request) {
books, err := h.Service.GetAllBooks()
if err != nil {
writeJsonError(w, http.StatusInternalServerError, err)
}
writeJsonResponse(w, http.StatusOK, books)
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Critical: Missing return after error response.

Line 220 writes an error response but execution continues to line 222, which writes a success response. This results in attempting to write the response twice and sending incorrect status codes.

Apply this diff:

 func (h *BookHandler) getAllBooks(w http.ResponseWriter, r *http.Request) {
 	books, err := h.Service.GetAllBooks()
 	if err != nil {
 		writeJsonError(w, http.StatusInternalServerError, err)
+		return
 	}
 	writeJsonResponse(w, http.StatusOK, books)
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
func (h *BookHandler) getAllBooks(w http.ResponseWriter, r *http.Request) {
books, err := h.Service.GetAllBooks()
if err != nil {
writeJsonError(w, http.StatusInternalServerError, err)
}
writeJsonResponse(w, http.StatusOK, books)
}
func (h *BookHandler) getAllBooks(w http.ResponseWriter, r *http.Request) {
books, err := h.Service.GetAllBooks()
if err != nil {
writeJsonError(w, http.StatusInternalServerError, err)
return
}
writeJsonResponse(w, http.StatusOK, books)
}
🤖 Prompt for AI Agents
In challenge-9/submissions/brenoamin/solution-template.go around lines 217 to
223, the handler writes an error response but does not stop execution, so it
continues and writes a success response as well; fix this by returning
immediately after writeJsonError (or otherwise prevent further execution, e.g.,
use an else) so that when h.Service.GetAllBooks() returns an error the function
exits and only the error response is sent.

Comment on lines +224 to +234
func (h *BookHandler) createBook(w http.ResponseWriter, r *http.Request) {
var book Book
if err := json.NewDecoder(r.Body).Decode(&book); err != nil {
writeJsonError(w, http.StatusInternalServerError, err)
} else {
if err := h.Service.CreateBook(&book); err != nil {
writeJsonError(w, http.StatusBadRequest, err)
}
writeJsonResponse(w, http.StatusCreated, book)
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Critical: Missing return statements after error responses.

Lines 227 and 230 write error responses but don't return, allowing execution to reach line 232 which writes a success response. This causes multiple response writes and incorrect behavior.

Apply this diff:

 func (h *BookHandler) createBook(w http.ResponseWriter, r *http.Request) {
 	var book Book
 	if err := json.NewDecoder(r.Body).Decode(&book); err != nil {
 		writeJsonError(w, http.StatusInternalServerError, err)
+		return
 	} else {
 		if err := h.Service.CreateBook(&book); err != nil {
 			writeJsonError(w, http.StatusBadRequest, err)
+			return
 		}
 		writeJsonResponse(w, http.StatusCreated, book)
 	}
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
func (h *BookHandler) createBook(w http.ResponseWriter, r *http.Request) {
var book Book
if err := json.NewDecoder(r.Body).Decode(&book); err != nil {
writeJsonError(w, http.StatusInternalServerError, err)
} else {
if err := h.Service.CreateBook(&book); err != nil {
writeJsonError(w, http.StatusBadRequest, err)
}
writeJsonResponse(w, http.StatusCreated, book)
}
}
func (h *BookHandler) createBook(w http.ResponseWriter, r *http.Request) {
var book Book
if err := json.NewDecoder(r.Body).Decode(&book); err != nil {
writeJsonError(w, http.StatusInternalServerError, err)
return
} else {
if err := h.Service.CreateBook(&book); err != nil {
writeJsonError(w, http.StatusBadRequest, err)
return
}
writeJsonResponse(w, http.StatusCreated, book)
}
}
🤖 Prompt for AI Agents
In challenge-9/submissions/brenoamin/solution-template.go around lines 224 to
234, the handler writes error responses but doesn't return, so execution
continues and the success response is also written; after each writeJsonError
call (both the JSON decode error and the CreateBook error) add an immediate
return so the function stops and only writes the success response when no errors
occurred.

Comment on lines +236 to +248
func (h *BookHandler) searchBooksBy(w http.ResponseWriter, r *http.Request) {
title := r.URL.Query().Get("title")
author := r.URL.Query().Get("author")

if title == "" && author == "" {
writeJsonError(w, http.StatusBadRequest, errors.New("provide title or author"))
}
if title != "" {
h.searchBooksByTitle(w, title)
} else if author != "" {
h.searchBooksByAuthor(w, author)
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Critical: Missing return after error response.

Line 241 writes an error response but execution continues to lines 243-247, potentially writing additional responses.

Apply this diff:

 func (h *BookHandler) searchBooksBy(w http.ResponseWriter, r *http.Request) {
 	title := r.URL.Query().Get("title")
 	author := r.URL.Query().Get("author")
 
 	if title == "" && author == "" {
 		writeJsonError(w, http.StatusBadRequest, errors.New("provide title or author"))
+		return
 	}
 	if title != "" {
 		h.searchBooksByTitle(w, title)
 	} else if author != "" {
 		h.searchBooksByAuthor(w, author)
 	}
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
func (h *BookHandler) searchBooksBy(w http.ResponseWriter, r *http.Request) {
title := r.URL.Query().Get("title")
author := r.URL.Query().Get("author")
if title == "" && author == "" {
writeJsonError(w, http.StatusBadRequest, errors.New("provide title or author"))
}
if title != "" {
h.searchBooksByTitle(w, title)
} else if author != "" {
h.searchBooksByAuthor(w, author)
}
}
func (h *BookHandler) searchBooksBy(w http.ResponseWriter, r *http.Request) {
title := r.URL.Query().Get("title")
author := r.URL.Query().Get("author")
if title == "" && author == "" {
writeJsonError(w, http.StatusBadRequest, errors.New("provide title or author"))
return
}
if title != "" {
h.searchBooksByTitle(w, title)
} else if author != "" {
h.searchBooksByAuthor(w, author)
}
}
🤖 Prompt for AI Agents
In challenge-9/submissions/brenoamin/solution-template.go around lines 236 to
248 the handler writes a BadRequest error when both title and author are empty
but does not return, so execution falls through and may write additional
responses; after the writeJsonError(w, http.StatusBadRequest,
errors.New("provide title or author")) call add an immediate return to stop
further processing and ensure no other response is written.

Comment on lines +268 to +277
func (h *BookHandler) getBookById(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
id := vars["id"]

book, err := h.Service.GetBookByID(id)
if err != nil {
writeJsonError(w, http.StatusNotFound, err)
}
writeJsonResponse(w, http.StatusOK, book)
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Critical: Missing return after error response.

Line 274 writes an error response but line 276 continues to write a success response, causing multiple response writes.

Apply this diff:

 func (h *BookHandler) getBookById(w http.ResponseWriter, r *http.Request) {
 	vars := mux.Vars(r)
 	id := vars["id"]
 
 	book, err := h.Service.GetBookByID(id)
 	if err != nil {
 		writeJsonError(w, http.StatusNotFound, err)
+		return
 	}
 	writeJsonResponse(w, http.StatusOK, book)
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
func (h *BookHandler) getBookById(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
id := vars["id"]
book, err := h.Service.GetBookByID(id)
if err != nil {
writeJsonError(w, http.StatusNotFound, err)
}
writeJsonResponse(w, http.StatusOK, book)
}
func (h *BookHandler) getBookById(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
id := vars["id"]
book, err := h.Service.GetBookByID(id)
if err != nil {
writeJsonError(w, http.StatusNotFound, err)
return
}
writeJsonResponse(w, http.StatusOK, book)
}
🤖 Prompt for AI Agents
In challenge-9/submissions/brenoamin/solution-template.go around lines 268 to
277, the handler writes an error response when GetBookByID fails but does not
return, so it proceeds to write a success response as well; modify the control
flow to stop after writing the error (e.g., add a return immediately after
writeJsonError or use an else branch) so only one response is written and the
handler exits on error.

Comment on lines +279 to +291
func (h *BookHandler) updateBook(w http.ResponseWriter, r *http.Request) {
book := &Book{}
err := json.NewDecoder(r.Body).Decode(book)
if err != nil {
writeJsonError(w, http.StatusInternalServerError, err)
}

err = h.Service.UpdateBook(book.ID, book)
if err != nil {
writeJsonError(w, http.StatusNotFound, err)
}
writeJsonResponse(w, http.StatusOK, book)
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Critical: Multiple issues in updateBook handler.

Three critical problems:

  1. Line 283: Writes error response but doesn't return
  2. Line 286: Uses book.ID from request body instead of path parameter id from mux.Vars (line 270 pattern), creating ID mismatch risk
  3. Line 288: Writes error response but doesn't return

Apply this diff:

 func (h *BookHandler) updateBook(w http.ResponseWriter, r *http.Request) {
+	vars := mux.Vars(r)
+	id := vars["id"]
+	
 	book := &Book{}
 	err := json.NewDecoder(r.Body).Decode(book)
 	if err != nil {
 		writeJsonError(w, http.StatusInternalServerError, err)
+		return
 	}
 
-	err = h.Service.UpdateBook(book.ID, book)
+	err = h.Service.UpdateBook(id, book)
 	if err != nil {
 		writeJsonError(w, http.StatusNotFound, err)
+		return
 	}
 	writeJsonResponse(w, http.StatusOK, book)
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
func (h *BookHandler) updateBook(w http.ResponseWriter, r *http.Request) {
book := &Book{}
err := json.NewDecoder(r.Body).Decode(book)
if err != nil {
writeJsonError(w, http.StatusInternalServerError, err)
}
err = h.Service.UpdateBook(book.ID, book)
if err != nil {
writeJsonError(w, http.StatusNotFound, err)
}
writeJsonResponse(w, http.StatusOK, book)
}
func (h *BookHandler) updateBook(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
id := vars["id"]
book := &Book{}
err := json.NewDecoder(r.Body).Decode(book)
if err != nil {
writeJsonError(w, http.StatusInternalServerError, err)
return
}
err = h.Service.UpdateBook(id, book)
if err != nil {
writeJsonError(w, http.StatusNotFound, err)
return
}
writeJsonResponse(w, http.StatusOK, book)
}
🤖 Prompt for AI Agents
In challenge-9/submissions/brenoamin/solution-template.go around lines 279 to
291, the updateBook handler currently decodes the body then calls UpdateBook
with book.ID and writes errors without returning; change it to (1) extract the
"id" path param from mux.Vars, parse/convert it to the expected ID type, and use
that parsed id when calling h.Service.UpdateBook instead of book.ID, (2) after
writing any JSON error response (decode failure or service error) immediately
return to stop further processing, and (3) adjust status codes for a bad request
on decode/invalid id and keep NotFound for UpdateBook errors, finally write the
successful JSON response only after all checks pass.

Comment on lines +293 to +301
func (h *BookHandler) deleteBook(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
id := vars["id"]
err := h.Service.DeleteBook(id)
if err != nil {
writeJsonError(w, http.StatusNotFound, err)
}
writeJsonResponse(w, http.StatusOK, nil)
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Critical: Missing return after error response.

Line 298 writes an error response but line 300 continues to write a success response, causing multiple response writes.

Apply this diff:

 func (h *BookHandler) deleteBook(w http.ResponseWriter, r *http.Request) {
 	vars := mux.Vars(r)
 	id := vars["id"]
 	err := h.Service.DeleteBook(id)
 	if err != nil {
 		writeJsonError(w, http.StatusNotFound, err)
+		return
 	}
 	writeJsonResponse(w, http.StatusOK, nil)
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
func (h *BookHandler) deleteBook(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
id := vars["id"]
err := h.Service.DeleteBook(id)
if err != nil {
writeJsonError(w, http.StatusNotFound, err)
}
writeJsonResponse(w, http.StatusOK, nil)
}
func (h *BookHandler) deleteBook(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
id := vars["id"]
err := h.Service.DeleteBook(id)
if err != nil {
writeJsonError(w, http.StatusNotFound, err)
return
}
writeJsonResponse(w, http.StatusOK, nil)
}
🤖 Prompt for AI Agents
In challenge-9/submissions/brenoamin/solution-template.go around lines 293 to
301, the handler writes an error response when DeleteBook returns an error but
then continues and writes a success response as well; update the control flow so
that after calling writeJsonError you immediately return from the handler (or
otherwise prevent the success response from being written), ensuring only one
HTTP response is sent per request.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant