Skip to content

Conversation

@labib99
Copy link
Contributor

@labib99 labib99 commented Oct 29, 2025

gin challenge-1-basic-routing Solution

Submitted by: @labib99
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/labib99/solution.go

Testing

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

Thank you for reviewing my submission! 🚀

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Oct 29, 2025

Walkthrough

Adds a new solution file for a Gin-based HTTP routing challenge, implementing a complete REST API with in-memory user storage, supporting CRUD operations and user search functionality with standardized JSON response handling.

Changes

Cohort / File(s) Change Summary
New Gin HTTP API Solution
packages/gin/challenge-1-basic-routing/submissions/labib99/solution.go
Adds complete HTTP API implementation with User and Response data structures, in-memory user storage, six endpoint handlers (GET /users, GET /users/:id, POST /users, PUT /users/:id, DELETE /users/:id, GET /users/search), and validation/lookup helper functions.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

  • Review each handler for consistent error handling and correct HTTP status codes
  • Verify email validation logic and case-insensitive search implementation
  • Confirm proper JSON marshaling/unmarshaling with struct tags

Possibly related PRs

Pre-merge checks

✅ Passed checks (3 passed)
Check name Status Explanation
Title Check ✅ Passed The pull request title "Add solution for gin challenge-1-basic-routing by labib99" directly and clearly describes the main change in the changeset: a new solution file has been added to the gin package for the challenge-1-basic-routing challenge. The title is specific, concise, and identifies both the challenge and the contributor, making it clear to teammates scanning the history what this PR accomplishes. The title is fully related to the substantial code addition documented in the summary.
Description Check ✅ Passed The pull request description is directly related to the changeset, providing context about the submission being a solution for the gin challenge-1-basic-routing challenge. The description clearly identifies the package, challenge, file location, and includes verification that the solution passes test cases. While structured as a template submission format, it conveys meaningful information about the purpose and contents of the changes without being vague or off-topic.
Docstring Coverage ✅ Passed Docstring coverage is 88.89% which is sufficient. The required threshold is 80.00%.

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: 2

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

3-9: Organize imports by grouping standard library and third-party packages.

Go convention separates standard library imports from third-party packages with a blank line for better readability.

Apply this diff:

 import (
 	"github.com/gin-gonic/gin"
+
 	"strconv"
 	"strings"
 	"errors"
 	"fmt"
 )

Should be:

 import (
+	"errors"
+	"fmt"
+	"strconv"
+	"strings"
+
 	"github.com/gin-gonic/gin"
-	"strconv"
-	"strings"
-	"errors"
-	"fmt"
 )

39-50: Route registration order may cause /users/search to match :id pattern.

The /users/search route (line 50) should be registered before /users/:id (line 42) to prevent "search" from being captured as an ID parameter. Gin's router may handle this correctly, but explicit ordering is safer and more maintainable.

Apply this diff:

 	// GET /users - Get all users
 	router.GET("/users", getAllUsers)
+	// GET /users/search - Search users by name
+	router.GET("/users/search", searchUsers)
 	// GET /users/:id - Get user by ID
 	router.GET("/users/:id", getUserByID)
 	// POST /users - Create new user
 	router.POST("/users", createUser)
 	// PUT /users/:id - Update user
 	router.PUT("/users/:id", updateUser)
 	// DELETE /users/:id - Delete user
 	router.DELETE("/users/:id", deleteUser)
-	// GET /users/search - Search users by name
-	router.GET("/users/search", searchUsers)

55-55: Remove completed TODO comment.

The TODO indicates handlers need implementation, but they're already complete.

Apply this diff:

-// TODO: Implement handler functions
-

57-64: Use named HTTP status constants instead of magic numbers.

Replace magic number 200 with http.StatusOK for better readability and maintainability.

Apply this diff:

+import "net/http"
+
 func getAllUsers(c *gin.Context) {
-	c.JSON(200, Response{
+	c.JSON(http.StatusOK, Response{
 	    Success: true,
 	    Data: users,
-	    Code: 200,
+	    Code: http.StatusOK,
 	})
 }

Apply this pattern to all handlers (lines 59-260).


81-92: Remove nil data from error response.

When a user is not found, including Data: user (which is nil) in the error response is confusing and unnecessary.

Apply this diff:

 	user, index := findUserByID(userId)
 	if index == -1 {
 		c.JSON(404, Response{
 			Success: false,
-			Data: user,
 			Message: "User not found",
 			Error: "User not found",
 			Code: 404,
 		})
 
 		return
 	}

125-128: Use idiomatic increment operator.

In Go, nextID++ is more idiomatic than nextID += 1.

Apply this diff:

 	// Add user to storage
 	user.ID = nextID
-	nextID += 1
+	nextID++
 	users = append(users, user)

247-252: Maintain consistency by returning User values instead of pointers.

The searchUsers handler returns []*User while other endpoints return []User or User. Additionally, storing pointers to slice elements is risky if the underlying slice is reallocated during concurrent operations.

Apply this diff:

-	usersFound := []*User{}
+	usersFound := []User{}
 	for idx, usr := range users {
 		if userName := strings.ToLower(usr.Name); strings.Contains(userName, lname) {
-			usersFound = append(usersFound, &users[idx])
+			usersFound = append(usersFound, usr)
 		}
 	}

265-265: Remove unnecessary parentheses in condition.

The parentheses around usr.ID == id are unnecessary in Go.

Apply this diff:

-		if (usr.ID == id) {
+		if usr.ID == id {
 			return &users[idx], idx
 		}

272-284: Consider validating Age field to prevent negative values.

The validateUser function validates Name and Email but doesn't check if Age is negative. Depending on requirements, you may want to add:

 	if !strings.Contains(user.Email, "@") {
         return errors.New("invalid email format")
     }
+	if user.Age < 0 {
+		return errors.New("age cannot be negative")
+	}
 	return nil
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9726d4c and 7d75296.

📒 Files selected for processing (1)
  • packages/gin/challenge-1-basic-routing/submissions/labib99/solution.go (1 hunks)
🔇 Additional comments (2)
packages/gin/challenge-1-basic-routing/submissions/labib99/solution.go (2)

11-26: LGTM!

The type definitions are well-structured with appropriate JSON tags for API responses.


177-185: Verify partial update semantics, especially for Age field.

The condition if user.Age > 0 prevents updating a user's age to 0. If this is intentional (treating 0 as "no update"), ensure it aligns with requirements. If legitimate zero values should be supported, consider using pointers in the update request to distinguish between "not provided" and "set to zero".

Similar consideration applies to Name and Email (can't clear them to empty strings).

Comment on lines +28 to +34
// In-memory storage
var users = []User{
{ID: 1, Name: "John Doe", Email: "[email protected]", Age: 30},
{ID: 2, Name: "Jane Smith", Email: "[email protected]", Age: 25},
{ID: 3, Name: "Bob Wilson", Email: "[email protected]", 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 | 🔴 Critical

Critical: Race condition on shared global state.

The global users slice and nextID variable are accessed concurrently by multiple HTTP handlers without synchronization. Gin handles each request in a separate goroutine, causing data races that can lead to:

  • Lost updates or duplicate IDs
  • Slice corruption during concurrent append operations
  • Inconsistent reads

Wrap access to shared state with a mutex:

+import "sync"
+
 // In-memory storage
+var mu sync.RWMutex
 var users = []User{
 	{ID: 1, Name: "John Doe", Email: "[email protected]", Age: 30},
 	{ID: 2, Name: "Jane Smith", Email: "[email protected]", Age: 25},
 	{ID: 3, Name: "Bob Wilson", Email: "[email protected]", Age: 35},
 }
 var nextID = 4

Then protect all reads with mu.RLock()/mu.RUnlock() and all writes with mu.Lock()/mu.Unlock() in handler functions.

📝 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
// In-memory storage
var users = []User{
{ID: 1, Name: "John Doe", Email: "[email protected]", Age: 30},
{ID: 2, Name: "Jane Smith", Email: "[email protected]", Age: 25},
{ID: 3, Name: "Bob Wilson", Email: "[email protected]", Age: 35},
}
var nextID = 4
import "sync"
// In-memory storage
var mu sync.RWMutex
var users = []User{
{ID: 1, Name: "John Doe", Email: "[email protected]", Age: 30},
{ID: 2, Name: "Jane Smith", Email: "[email protected]", Age: 25},
{ID: 3, Name: "Bob Wilson", Email: "[email protected]", Age: 35},
}
var nextID = 4
🤖 Prompt for AI Agents
In packages/gin/challenge-1-basic-routing/submissions/labib99/solution.go around
lines 28 to 34, the global users slice and nextID are accessed concurrently by
HTTP handlers causing data races; define a package-level sync.RWMutex (e.g., var
mu sync.RWMutex) next to users/nextID, then wrap all handler reads of
users/nextID with mu.RLock()/mu.RUnlock() and all mutations (append, remove,
update, and incrementing nextID) with mu.Lock()/mu.Unlock() so concurrent
requests are serialized safely.

// PUT /users/:id - Update user
router.PUT("/users/:id", updateUser)
// DELETE /users/:id - Delete user
router.DELETE("users/:id", deleteUser)
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 leading slash in DELETE route path.

The DELETE route is missing the leading "/" which will cause incorrect route matching.

Apply this diff:

-	router.DELETE("users/:id", deleteUser)
+	router.DELETE("/users/:id", deleteUser)
📝 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
router.DELETE("users/:id", deleteUser)
router.DELETE("/users/:id", deleteUser)
🤖 Prompt for AI Agents
In packages/gin/challenge-1-basic-routing/submissions/labib99/solution.go around
line 48 the DELETE route path is missing the leading "/" which prevents correct
route matching; update the route registration to use an absolute path by
changing the route string to "/users/:id" (i.e., router.DELETE("/users/:id",
deleteUser)) and save/rebuild to verify routing works.

@RezaSi RezaSi merged commit 643d14c into RezaSi:main Oct 31, 2025
5 checks passed
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