Skip to content

Add solution for gin challenge-1-basic-routing by nbploc08#1430

Open
nbploc08 wants to merge 2 commits intoRezaSi:mainfrom
nbploc08:package-gin-challenge-1-basic-routing-nbploc08
Open

Add solution for gin challenge-1-basic-routing by nbploc08#1430
nbploc08 wants to merge 2 commits intoRezaSi:mainfrom
nbploc08:package-gin-challenge-1-basic-routing-nbploc08

Conversation

@nbploc08
Copy link

gin challenge-1-basic-routing Solution

Submitted by: @nbploc08
Package: gin
Challenge: challenge-1-basic-routing

Description

This PR contains my solution for gin challenge-1-basic-routing.

Changes

  • Added solution file to packages/gin/challenge-1-basic-routing/submissions/nbploc08/solution.go

Testing

  • Solution passes all test cases
  • Code follows Go best practices

Thank you for reviewing my submission! 🚀

Copilot AI review requested due to automatic review settings February 25, 2026 23:17
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 25, 2026

Walkthrough

A new Go solution file that implements a complete in-memory REST API using the Gin framework for user management. The solution includes CRUD operations, search functionality, two exported types (User and Response), multiple handler functions, and basic validation with appropriate HTTP status codes.

Changes

Cohort / File(s) Summary
Gin User Management API
packages/gin/challenge-1-basic-routing/submissions/nbploc08/solution.go
New file (+231 lines) implementing REST API with User and Response types, CRUD handlers (getAllUsers, getUserByID, createUser, updateUser, deleteUser), search functionality, helper validation/lookup functions, and in-memory user storage with seed data and ID counter.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

  • #805: Implements the same Gin-based in-memory user REST API with identical User/Response types and all CRUD/search handlers.
  • #834: Adds the same Gin solution with identical exported types and handler functions for the same challenge path.
  • #899: Implements near-identical Gin-based CRUD API with matching exported types, handlers, and helper functions.

Poem

🐰 A rabbit hops through routes with glee,
GET, POST, PUT—users dance in symphony!
In-memory lists and validation so fine,
REST API gardens where data align! 🌿
JSON responses—perfection divine!

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: adding a solution for the gin challenge-1-basic-routing challenge by the contributor nbploc08, which matches the file added and the PR's purpose.
Description check ✅ Passed The description is directly related to the changeset, explaining what challenge is being solved, which file was added, and confirming testing was completed.
Docstring Coverage ✅ Passed Docstring coverage is 88.89% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


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

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR adds a solution for the Gin challenge-1-basic-routing by implementing a basic User Management REST API with CRUD operations and search functionality. The solution demonstrates understanding of Gin routing, HTTP handlers, and JSON serialization, but contains several critical issues including race conditions, route ordering problems, and inconsistent API response formats.

Changes:

  • Implemented all required REST API endpoints (GET, POST, PUT, DELETE for users, plus search functionality)
  • Added helper functions for user validation and lookup
  • Included Vietnamese language comments throughout the code documenting implementation decisions
Comments suppressed due to low confidence (13)

packages/gin/challenge-1-basic-routing/submissions/nbploc08/solution.go:228

  • The error message "invalid user" is not sufficiently descriptive. It doesn't explain what makes the user invalid. Consider providing more specific error messages such as "Name, Email, and Age are required fields" or "Age must be greater than 0" to help API consumers understand what went wrong.
	if user.Name == "" || user.Email == "" || user.Age <= 0 {
		return errors.New("invalid user")

packages/gin/challenge-1-basic-routing/submissions/nbploc08/solution.go:206

  • The response format is inconsistent with other successful responses. Line 206 uses gin.H{"success": true, "data": matches} while other successful responses use the Response struct (e.g., lines 64-70, 91-96). For consistency, this should use Response{Success: true, Data: matches, Code: 200} to match the pattern used throughout the rest of the code.
	c.JSON(200, gin.H{"success": true, "data": matches}) // ✅ Fix: luôn trả 200, kể cả khi không có kết quả

packages/gin/challenge-1-basic-routing/submissions/nbploc08/solution.go:193

  • The error format is inconsistent. Line 192 uses gin.H{"error": "No search query"} while successful responses use the Response struct. For consistency, all error responses should use the same format. Consider using Response{Success: false, Error: "No search query", Code: 400} to match the pattern used in successful responses.
		c.JSON(400, gin.H{"error": "No search query"})
		return

packages/gin/challenge-1-basic-routing/submissions/nbploc08/solution.go:88

  • The error format is inconsistent. Line 87 uses gin.H{"error": "User not found"} while lines 91-96 use the Response struct. For consistency, all error responses should use the same format. Consider using Response{Success: false, Error: "User not found", Code: 404} to match the pattern used in successful responses.
		c.JSON(404, gin.H{"error": "User not found"})
		return

packages/gin/challenge-1-basic-routing/submissions/nbploc08/solution.go:147

  • The error format is inconsistent. Line 146 uses gin.H{"error": "User not found"} while successful responses use the Response struct. For consistency, all error responses should use the same format. Consider using Response{Success: false, Error: "User not found", Code: 404} to match the pattern used in successful responses.
		c.JSON(404, gin.H{"error": "User not found"})
		return

packages/gin/challenge-1-basic-routing/submissions/nbploc08/solution.go:168

  • The error format is inconsistent. Line 167 uses gin.H{"error": "Invalid ID"} while successful responses use the Response struct. For consistency, all error responses should use the same format. Consider using Response{Success: false, Error: "Invalid ID", Code: 400} to match the pattern used in successful responses.
		c.JSON(400, gin.H{"error": "Invalid ID"})
		return

packages/gin/challenge-1-basic-routing/submissions/nbploc08/solution.go:173

  • The error format is inconsistent. Line 172 uses gin.H{"error": "User not found"} while successful responses use the Response struct. For consistency, all error responses should use the same format. Consider using Response{Success: false, Error: "User not found", Code: 404} to match the pattern used in successful responses.
		c.JSON(404, gin.H{"error": "User not found"})
		return // ✅ Fix: phải return, không thì tiếp tục với i = -1 => panic

packages/gin/challenge-1-basic-routing/submissions/nbploc08/solution.go:82

  • The error format is inconsistent with other error responses in the file. Line 81 uses gin.H{"error": "Invalid ID"} while lines 91-96 use the Response struct. For consistency, all error responses should use the same format. Consider using Response{Success: false, Error: "Invalid ID", Code: 400} to match the pattern used in successful responses.
		c.JSON(400, gin.H{"error": "Invalid ID"})
		return

packages/gin/challenge-1-basic-routing/submissions/nbploc08/solution.go:108

  • The error format is inconsistent. Line 107 uses gin.H{"error": err.Error()} while line 118 uses the Response struct. For consistency, all error responses should use the same format. Consider using Response{Success: false, Error: err.Error(), Code: 400} to match the pattern used in successful responses.
		c.JSON(400, gin.H{"error": err.Error()})
		return

packages/gin/challenge-1-basic-routing/submissions/nbploc08/solution.go:113

  • The error format is inconsistent. Line 112 uses gin.H{"error": err.Error()} while line 118 uses the Response struct. For consistency, all error responses should use the same format. Consider using Response{Success: false, Error: err.Error(), Code: 400} to match the pattern used in successful responses.
		c.JSON(400, gin.H{"error": err.Error()})
		return // ✅ Fix: phải return, không thì tiếp tục append user lỗi

packages/gin/challenge-1-basic-routing/submissions/nbploc08/solution.go:138

  • The error format is inconsistent. Line 137 uses gin.H{"error": err.Error()} while successful responses use the Response struct. For consistency, all error responses should use the same format. Consider using Response{Success: false, Error: err.Error(), Code: 400} to match the pattern used in successful responses.
		c.JSON(400, gin.H{"error": err.Error()})
		return

packages/gin/challenge-1-basic-routing/submissions/nbploc08/solution.go:142

  • The error format is inconsistent. Line 141 uses gin.H{"error": vali.Error()} while successful responses use the Response struct. For consistency, all error responses should use the same format. Consider using Response{Success: false, Error: vali.Error(), Code: 400} to match the pattern used in successful responses.
		c.JSON(400, gin.H{"error": vali.Error()}) // ✅ Fix: dùng vali.Error() thay vì err.Error() (err ở đây là nil), thêm return
		return

packages/gin/challenge-1-basic-routing/submissions/nbploc08/solution.go:132

  • The error format is inconsistent. Line 131 uses gin.H{"error": "Invalid ID"} while successful responses use the Response struct. For consistency, all error responses should use the same format. Consider using Response{Success: false, Error: "Invalid ID", Code: 400} to match the pattern used in successful responses.
		c.JSON(400, gin.H{"error": "Invalid ID"})
		return // ✅ Fix: return để dừng, không chạy tiếp với userID rác

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +51 to +55
r.GET("/users/:id", getUserByID)
r.POST("/users", createUser)
r.PUT("/users/:id", updateUser)
r.DELETE("/users/:id", deleteUser)
r.GET("/users/search", searchUsers)
Copy link

Copilot AI Feb 25, 2026

Choose a reason for hiding this comment

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

Route order issue: The /users/search route is defined after /users/:id. In Gin, routes are matched in order, and /users/:id will match /users/search first, treating "search" as an ID parameter. The /users/search route should be defined before /users/:id to ensure the search endpoint works correctly. Move line 55 to before line 51.

Suggested change
r.GET("/users/:id", getUserByID)
r.POST("/users", createUser)
r.PUT("/users/:id", updateUser)
r.DELETE("/users/:id", deleteUser)
r.GET("/users/search", searchUsers)
r.GET("/users/search", searchUsers)
r.GET("/users/:id", getUserByID)
r.POST("/users", createUser)
r.PUT("/users/:id", updateUser)
r.DELETE("/users/:id", deleteUser)

Copilot uses AI. Check for mistakes.
Comment on lines +29 to +34
var users = []User{
{ID: 1, Name: "John Doe", Email: "john@example.com", Age: 30},
{ID: 2, Name: "Jane Smith", Email: "jane@example.com", Age: 25},
{ID: 3, Name: "Bob Wilson", Email: "bob@example.com", Age: 35},
}
var nextID = 4
Copy link

Copilot AI Feb 25, 2026

Choose a reason for hiding this comment

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

The global variables users and nextID are accessed and modified concurrently by multiple HTTP handlers without any synchronization mechanism. This creates race conditions where concurrent requests could corrupt data or cause inconsistent state. Consider using a mutex (sync.RWMutex) to protect access to these shared variables, or use a thread-safe data structure.

Copilot uses AI. Check for mistakes.
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: 4

🧹 Nitpick comments (3)
packages/gin/challenge-1-basic-routing/submissions/nbploc08/solution.go (3)

37-47: Clean up leftover TODO comments from the challenge template.

All routes and handlers are implemented, yet the original TODO stubs (lines 37–47, 59, 63, 75–77, 101–104, 124–127, 161–163, 186–188, 212–213, 224–226) remain. These are noise in a final submission.


113-113: Replace Vietnamese inline fix-notes with English or remove them.

Comments like // ✅ Fix: phải return, không thì tiếp tục append user lỗi (and similar throughout) are Vietnamese development notes that should be translated to English or removed before submission to a shared repository.


70-70: Prefer net/http status constants over raw integer literals.

Using http.StatusOK, http.StatusCreated, http.StatusBadRequest, and http.StatusNotFound instead of 200, 201, 400, 404 throughout is the idiomatic Go style and makes intent explicit.


ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a7ac764 and 11a0eb5.

📒 Files selected for processing (1)
  • packages/gin/challenge-1-basic-routing/submissions/nbploc08/solution.go

Comment on lines +29 to +34
var users = []User{
{ID: 1, Name: "John Doe", Email: "john@example.com", Age: 30},
{ID: 2, Name: "Jane Smith", Email: "jane@example.com", Age: 25},
{ID: 3, Name: "Bob Wilson", Email: "bob@example.com", Age: 35},
}
var nextID = 4
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

Global mutable state is not protected against concurrent access.

users and nextID are mutated by multiple handlers without a mutex. Gin itself is thread-safe, but handlers must avoid mutating shared state without synchronization; improper use of shared maps or slices can lead to panics or data corruption. Concurrent POST, PUT, DELETE, and GET requests (all served in separate goroutines) will race on the slice and counter.

🔒 Proposed fix — add a RWMutex
+import "sync"

 var users = []User{...}
 var nextID = 4
+var mu sync.RWMutex

Then guard writes with mu.Lock()/mu.Unlock() in createUser, updateUser, deleteUser, and reads with mu.RLock()/mu.RUnlock() in getAllUsers, getUserByID, searchUsers.

Comment on lines +49 to +56
r := gin.Default()
r.GET("/users", getAllUsers)
r.GET("/users/:id", getUserByID)
r.POST("/users", createUser)
r.PUT("/users/:id", updateUser)
r.DELETE("/users/:id", deleteUser)
r.GET("/users/search", searchUsers)
r.Run() // listens on 0.0.0.0:8080 by default
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

Route registration order causes a startup panic.

Registering GET /users/search (line 55) after GET /users/:id (line 51) will cause a panic: wildcard route conflicts with existing children at startup, because Gin's httprouter radix tree does not allow inserting a static child node once a wildcard has already been committed at the same path segment. The server will never start.

The wildcard route /users/:id conflicts with the static route /users/search because the router matches the prefixes one by one — not the complete route at once. The fix is to register the static route first:

🐛 Proposed fix — register /users/search before /users/:id
 r := gin.Default()
 r.GET("/users", getAllUsers)
-r.GET("/users/:id", getUserByID)
+r.GET("/users/search", searchUsers)
+r.GET("/users/:id", getUserByID)
 r.POST("/users", createUser)
 r.PUT("/users/:id", updateUser)
 r.DELETE("/users/:id", deleteUser)
-r.GET("/users/search", searchUsers)
 r.Run()
📝 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
r := gin.Default()
r.GET("/users", getAllUsers)
r.GET("/users/:id", getUserByID)
r.POST("/users", createUser)
r.PUT("/users/:id", updateUser)
r.DELETE("/users/:id", deleteUser)
r.GET("/users/search", searchUsers)
r.Run() // listens on 0.0.0.0:8080 by default
r := gin.Default()
r.GET("/users", getAllUsers)
r.GET("/users/search", searchUsers)
r.GET("/users/:id", getUserByID)
r.POST("/users", createUser)
r.PUT("/users/:id", updateUser)
r.DELETE("/users/:id", deleteUser)
r.Run() // listens on 0.0.0.0:8080 by default

Comment on lines +62 to +71
func getAllUsers(c *gin.Context) {
// TODO: Return all users
resp := Response{
Success: true,
Data: users,
Message: "ssss",
Code: 200,
}
c.JSON(200, resp)
}
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

Remove the debug string "ssss" from the response.

Message: "ssss" (line 67) is a leftover debug artifact that will cause test assertion failures on response body checks and will be returned to real clients.

🐛 Proposed fix
 resp := Response{
     Success: true,
     Data:    users,
-    Message: "ssss",
+    Message: "Get all users success",
     Code:    200,
 }
📝 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 getAllUsers(c *gin.Context) {
// TODO: Return all users
resp := Response{
Success: true,
Data: users,
Message: "ssss",
Code: 200,
}
c.JSON(200, resp)
}
func getAllUsers(c *gin.Context) {
// TODO: Return all users
resp := Response{
Success: true,
Data: users,
Message: "Get all users success",
Code: 200,
}
c.JSON(200, resp)
}

Comment on lines +222 to +230
// Helper function to validate user data
func validateUser(user User) error {
// TODO: Implement validation
// Check required fields: Name, Email
// Validate email format (basic check)
if user.Name == "" || user.Email == "" || user.Age <= 0 {
return errors.New("invalid user")
}
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 | 🟡 Minor

validateUser doesn't actually validate email format despite the comment.

The TODO/comment states "Validate email format (basic check)" but only a non-empty check is performed — "notanemail" would pass. Add at least a minimal strings.Contains(user.Email, "@") guard.

🐛 Proposed fix
-if user.Name == "" || user.Email == "" || user.Age <= 0 {
-    return errors.New("invalid user")
-}
+if user.Name == "" {
+    return errors.New("name is required")
+}
+if user.Email == "" || !strings.Contains(user.Email, "@") {
+    return errors.New("valid email is required")
+}
+if user.Age <= 0 {
+    return errors.New("age must be positive")
+}

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.

2 participants