Skip to content

feat: add handler wrapper#6

Merged
itsLeonB merged 6 commits into
mainfrom
dev
Jan 9, 2026
Merged

feat: add handler wrapper#6
itsLeonB merged 6 commits into
mainfrom
dev

Conversation

@itsLeonB

@itsLeonB itsLeonB commented Dec 17, 2025

Copy link
Copy Markdown
Owner

Summary by CodeRabbit

  • New Features

    • Example HTTP server and demo handlers for success, errors, and panic recovery.
    • New middlewares: authentication, permission checks, CORS, rate limiting, and enhanced logging.
  • Improvements

    • Unified structured JSON error responses and improved error recovery/logging.
    • Response envelope now uses "data" and an errors array; added JSON binding helper.
  • Tests

    • Expanded unit and integration tests covering middleware, server utilities, rate limiting, and responses.
  • Chores

    • Updated ignore patterns and added a build target; pre-push script runs build step.
  • Breaking Changes

    • Public package/type/function renames and API signature changes.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai

coderabbitai Bot commented Dec 17, 2025

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

Reorganizes packages into pkg/{middleware,response,server}, renames public APIs (ginkgo → middleware/response/server; HttpServer→Http; WrapHandler→Handler), reimplements middleware (auth, CORS, permission, rate limit, logging, error handling), updates response envelope, adds example app, adjusts CI/Makefile, updates go.mod, and moves many tests from root test/ into package-scoped tests.

Changes

Cohort / File(s) Summary
Config & Build
\.gitignore``, Makefile, ``scripts/git-pre-push.sh``
Add bin/, tmp/ ignores and enable .vscode/; add build Make target and update test/test-coverage targets to use ./...; add pre-push build step.
Go module
go.mod
Update dependencies: bump/add multiple modules (ungerr, ezutil, testify, golang.org/x/time, etc.), remove eris, adjust indirect deps.
Removed legacy root files
Deleted: errors.go, extract_token.go, middlewares.go
Remove legacy global errors, token extraction utilities, and previous middleware implementation (functionality reimplemented under pkg/middleware).
New middleware package
pkg/middleware/... (middlewares.go,error.go,logging.go,auth.go,cors.go,permission.go,ratelimit.go)
Introduces MiddlewareProvider and reimplements error middleware (panic analysis, appError mapping), logging, CORS, auth (Bearer extraction + tokenCheckFunc), permission checks, and per-IP rate limiting with eviction. Package rename/repacks existing middleware logic.
Server package changes
pkg/server/http_server.go, pkg/server/utils.go
Rename package server; type HttpServerHttp, constructor NewHttpServerNew; replace WrapHandler with Handler(successCode int, handler func(ctx)(any,error)); add generic BindJSON[T].
Response package changes
pkg/response/responses.go
Rename package response; change JSON envelope (remove Message → use Data any, Errors → []error), remove WithData/WithError helpers; adjust QueryOptions tags.
Example app
example/main.go, example/server.go, example/handler.go
Add example Gin app wiring server, middleware, and handlers demonstrating success, errors, wrapped/unwrapped errors, and panic recovery.
Tests removed (root)
test/* (multiple files)
Remove many legacy tests under test/ (cors, error middleware suites, logging, middlewares, responses, utils, http_server, extract_token, errors).
Package-scoped tests added/updated
pkg/middleware/*_test.go, pkg/response/responses_test.go, pkg/server/*_test.go, pkg/server/utils_test.go
Add comprehensive tests for middleware (auth, error, logging, permission, ratelimit, cors), response shapes/pagination, and server/utils helpers; adapt tests to new APIs.
CI workflow
.github/workflows/ci.yml
Remove nested test package verify/build steps.

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant Gin as "Gin Engine"
  participant Auth as "MiddlewareProvider.NewAuthMiddleware"
  participant Rate as "MiddlewareProvider.NewRateLimitMiddleware"
  participant Handler as "Application Handler"
  participant ServerResp as "pkg/response"

  Client->>Gin: HTTP request (Authorization header)
  Gin->>Rate: pass through rate-limit middleware
  Rate-->>Gin: allow / reject (429)
  Gin->>Auth: extract token & call tokenCheckFunc
  Auth-->>Gin: set ctx values or abort (401/500)
  Gin->>Handler: invoke business handler
  Handler-->>Gin: return (data, error)
  Gin->>ServerResp: build JSON envelope (NewResponse/NewErrorResponse)
  ServerResp-->>Client: HTTP response
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~75 minutes

Possibly related PRs

  • feat: add handler wrapper #5 — Related to handler-wrapper refactor: renames/refactors WrapHandlerHandler and changes handler return semantics.
  • chore: bump ezutil to v2 #3 — Related dependency and import updates for ezutil/module paths used in middleware and server packages.
  • initial pkg #1 — Large refactor/migration overlap: reorganizes middleware, error/response, server utilities, and many moved/removed files.

Poem

🐰 I hopped through pkg and tidy ground,

Middlewares reborn, new routes were found.
Tokens checked, and limits keep,
Panics logged before we sleep.
A little rabbit applauds the sound!

🚥 Pre-merge checks | ✅ 1 | ❌ 2
❌ Failed checks (2 warnings)
Check name Status Explanation Resolution
Title check ⚠️ Warning The title 'feat: add handler wrapper' directly relates to the new Handler function in pkg/server/utils.go, which wraps HTTP handlers. However, this PR contains extensive refactoring across multiple packages (renaming packages from 'ginkgo' to specific names like 'middleware', 'response', 'server'), significant middleware restructuring, dependency updates, and test migrations—changes far broader than just adding a handler wrapper. Revise the title to reflect the broader scope of changes, such as 'refactor: restructure package organization and rename to domain-specific packages' or break this into smaller, focused PRs addressing specific concerns (handler wrapper, package reorganization, middleware migration).
Docstring Coverage ⚠️ Warning Docstring coverage is 16.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

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

✨ Finishing touches
  • 📝 Generate docstrings

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (1)
utils.go (1)

57-63: Consider refactoring to reduce code duplication.

The BindJSON function duplicates logic from BindRequest. While the specialized API is convenient, you could eliminate duplication by implementing it as:

func BindJSON[T any](ctx *gin.Context) (T, error) {
	return BindRequest[T](ctx, binding.JSON)
}

This would maintain the same API while reusing the existing BindRequest logic.

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 546295e and 35c0e44.

📒 Files selected for processing (2)
  • responses.go (1 hunks)
  • utils.go (2 hunks)
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: itsLeonB
Repo: itsLeonB/ginkgo PR: 1
File: responses.go:31-35
Timestamp: 2025-08-27T10:05:59.429Z
Learning: The `omitzero` JSON tag was introduced in Go 1.24, allowing struct fields to be omitted during JSON marshaling if their value is zero (as determined by an IsZero() method if present, otherwise based on the zero value for the type). This is different from `omitempty` which omits empty values.
📚 Learning: 2025-08-27T10:05:59.429Z
Learnt from: itsLeonB
Repo: itsLeonB/ginkgo PR: 1
File: responses.go:31-35
Timestamp: 2025-08-27T10:05:59.429Z
Learning: The `omitzero` JSON tag was introduced in Go 1.24, allowing struct fields to be omitted during JSON marshaling if their value is zero (as determined by an IsZero() method if present, otherwise based on the zero value for the type). This is different from `omitempty` which omits empty values.

Applied to files:

  • responses.go
🧬 Code graph analysis (1)
utils.go (1)
responses.go (1)
  • JSONResponse (30-35)
🔇 Additional comments (1)
responses.go (1)

31-31: LGTM! Consistent field marshaling behavior.

The omitzero tag aligns with the other fields in the struct and enables the Message field to be omitted when empty, which works well with the new Handler function in utils.go that creates responses without explicit messages.

Based on learnings, omitzero was introduced in Go 1.24 and provides appropriate zero-value omission semantics.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (9)
Makefile (1)

7-7: Minor formatting inconsistency in help text.

The spacing before the dash appears inconsistent compared to other help entries (mixing tabs and spaces).

🔎 Proposed fix for consistent spacing
-	@echo "  test                     	  - Run all tests"
+	@echo "  test                         - Run all tests"
pkg/middleware/logging.go (1)

36-61: Consider consolidating duplicate log calls.

The logging logic duplicates the format string between the case with an error message and without. A minor refactor could reduce this duplication.

🔎 Proposed refactor
 		if statusCode >= 400 {
-			errorMsg := ""
-			if len(ctx.Errors) > 0 {
-				errorMsg = ctx.Errors.String()
-			}
-
-			if errorMsg != "" {
-				mp.logger.Errorf(
-					"[HTTP] method=%s path=%s status=%d duration=%s client_ip=%s error=%s",
-					method,
-					fullPath,
-					statusCode,
-					elapsed,
-					clientIP,
-					errorMsg,
-				)
-			} else {
-				mp.logger.Errorf(
-					"[HTTP] method=%s path=%s status=%d duration=%s client_ip=%s",
-					method,
-					fullPath,
-					statusCode,
-					elapsed,
-					clientIP,
-				)
-			}
+			baseMsg := "[HTTP] method=%s path=%s status=%d duration=%s client_ip=%s"
+			args := []any{method, fullPath, statusCode, elapsed, clientIP}
+			if len(ctx.Errors) > 0 {
+				mp.logger.Errorf(baseMsg+" error=%s", append(args, ctx.Errors.String())...)
+			} else {
+				mp.logger.Errorf(baseMsg, args...)
+			}
 		} else {
example/server.go (1)

20-20: Consider adding logging middleware for observability.

The example only uses the error middleware. For a complete demonstration, consider also adding mp.NewLoggingMiddleware() to show how request logging integrates with error handling.

🔎 Proposed addition
 	r.Use(mp.NewErrorMiddleware())
+	r.Use(mp.NewLoggingMiddleware())
pkg/middleware/permission.go (2)

16-20: Consider validating input parameters for consistency with other middlewares.

Other middlewares in this package (CORS, Auth) validate their inputs and fail-fast on invalid configuration. This middleware doesn't validate roleContextKey, requiredPermission, or permissionMap during construction, which could lead to confusing runtime behavior (e.g., empty roleContextKey would always fail at line 23).

🔎 Proposed validation
 func (mp *MiddlewareProvider) NewPermissionMiddleware(
 	roleContextKey string,
 	requiredPermission string,
 	permissionMap map[string][]string,
 ) gin.HandlerFunc {
+	if roleContextKey == "" {
+		mp.logger.Fatal("roleContextKey cannot be empty")
+	}
+	if requiredPermission == "" {
+		mp.logger.Fatal("requiredPermission cannot be empty")
+	}
+	if permissionMap == nil {
+		mp.logger.Fatal("permissionMap cannot be nil")
+	}
 	return func(ctx *gin.Context) {

Based on learnings, this codebase uses fail-fast design for configuration validation.


23-27: The mp.logger field is unused in this middleware.

Unlike other middlewares, this one doesn't use the logger for any debug or trace logging. While not critical, logging permission checks could aid debugging in production.

pkg/middleware/auth.go (3)

12-16: Docstring mentions unsupported strategies.

The comment mentions "header" or "cookie" strategies, but the implementation only supports "Bearer". Update the documentation to reflect the actual supported strategy.

🔎 Suggested docstring fix
 // NewAuthMiddleware creates an authentication middleware for Gin.
-// It extracts a token using the given strategy (e.g., "header" or "cookie") via internal.ExtractToken,
+// It extracts a token using the given strategy (e.g., "Bearer") via extractToken,
 // calls tokenCheckFunc to validate the token and retrieve user data,
 // stores user data in the Gin context, and aborts the request on errors.
 // Returns a Gin HandlerFunc for authentication handling.

58-66: Consider returning a single error instead of mixed (token, errMsg, error) tuple.

The extractToken function returns both an error message string and an error, which creates an awkward API. The caller must check both err != nil and errMsg != "" separately. Consider consolidating into a single error return using typed errors (e.g., ungerr.UnauthorizedError).

🔎 Suggested refactor
-func extractToken(ctx *gin.Context, authStrategy string) (string, string, error) {
+func extractToken(ctx *gin.Context, authStrategy string) (string, error) {
 	switch authStrategy {
 	case "Bearer":
-		token, errMsg := extractBearerToken(ctx)
-		return token, errMsg, nil
+		return extractBearerToken(ctx)
 	default:
-		return "", "", eris.Errorf("unsupported auth strategy: %s", authStrategy)
+		return "", eris.Errorf("unsupported auth strategy: %s", authStrategy)
 	}
 }

-func extractBearerToken(ctx *gin.Context) (string, string) {
+func extractBearerToken(ctx *gin.Context) (string, error) {
 	token := ctx.GetHeader("Authorization")
 	if token == "" {
-		return "", "missing token"
+		return "", ungerr.UnauthorizedError("missing token")
 	}

 	isValid, token := validateAndExtractBearerToken(token)
 	if !isValid {
-		return "", "invalid token"
+		return "", ungerr.UnauthorizedError("invalid token")
 	}

-	return token, ""
+	return token, nil
 }

82-96: ConstantTimeCompare on "bearer" prefix has limited security benefit.

Using subtle.ConstantTimeCompare for comparing the "bearer" prefix doesn't prevent timing attacks on the actual token value—only on the prefix comparison. The real token validation happens in tokenCheckFunc. This is harmless but provides little security value here.

pkg/server/http_server.go (1)

15-20: Type naming is terse but follows Go conventions.

Renaming HttpServer to Http within the server package results in usage like server.Http, which is acceptable. However, Http alone doesn't convey it's a server struct. Consider if Server would be clearer (usage: server.Server or server.New() *Server).

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 35c0e44 and 69425ce.

⛔ Files ignored due to path filters (1)
  • go.sum is excluded by !**/*.sum
📒 Files selected for processing (28)
  • .gitignore
  • Makefile
  • errors.go
  • example/handler.go
  • example/main.go
  • example/server.go
  • extract_token.go
  • go.mod
  • middlewares.go
  • pkg/middleware/auth.go
  • pkg/middleware/cors.go
  • pkg/middleware/error.go
  • pkg/middleware/logging.go
  • pkg/middleware/middlewares.go
  • pkg/middleware/permission.go
  • pkg/response/responses.go
  • pkg/server/http_server.go
  • pkg/server/utils.go
  • test/cors_middleware_test.go
  • test/error_middleware_comprehensive_test.go
  • test/error_middleware_test.go
  • test/errors_test.go
  • test/extract_token_test.go
  • test/http_server_test.go
  • test/logging_middleware_test.go
  • test/middlewares_test.go
  • test/responses_test.go
  • test/utils_test.go
💤 Files with no reviewable changes (13)
  • test/responses_test.go
  • errors.go
  • test/http_server_test.go
  • test/extract_token_test.go
  • test/errors_test.go
  • test/logging_middleware_test.go
  • test/error_middleware_test.go
  • test/error_middleware_comprehensive_test.go
  • test/utils_test.go
  • extract_token.go
  • middlewares.go
  • test/cors_middleware_test.go
  • test/middlewares_test.go
🧰 Additional context used
🧠 Learnings (5)
📓 Common learnings
Learnt from: itsLeonB
Repo: itsLeonB/ginkgo PR: 1
File: middlewares.go:23-28
Timestamp: 2025-08-27T09:29:56.699Z
Learning: In the ginkgo package, the NewCorsMiddleware function intentionally uses log.Fatalf for CORS configuration validation as a fail-fast design choice rather than degrading gracefully.
Learnt from: itsLeonB
Repo: itsLeonB/ginkgo PR: 1
File: error_middleware.go:3-16
Timestamp: 2025-08-27T09:12:03.396Z
Learning: In the ginkgo package error middleware, unwrapped errors (including known types like validator.ValidationErrors and json.SyntaxError) are intentionally treated as 500 Internal Server Errors to enforce the use of eris.Wrap for consistent stack trace collection and better observability.
📚 Learning: 2025-08-27T09:29:56.699Z
Learnt from: itsLeonB
Repo: itsLeonB/ginkgo PR: 1
File: middlewares.go:23-28
Timestamp: 2025-08-27T09:29:56.699Z
Learning: In the ginkgo package, the NewCorsMiddleware function intentionally uses log.Fatalf for CORS configuration validation as a fail-fast design choice rather than degrading gracefully.

Applied to files:

  • pkg/middleware/logging.go
  • pkg/middleware/cors.go
  • example/handler.go
  • pkg/middleware/error.go
  • pkg/middleware/middlewares.go
  • pkg/server/utils.go
  • pkg/server/http_server.go
📚 Learning: 2025-08-27T09:32:11.080Z
Learnt from: itsLeonB
Repo: itsLeonB/ginkgo PR: 1
File: middlewares.go:39-44
Timestamp: 2025-08-27T09:32:11.080Z
Learning: In the ginkgo package, the NewAuthMiddleware function intentionally uses log.Fatalf for tokenCheckFunc validation as a fail-fast design choice, similar to the CORS middleware pattern.

Applied to files:

  • pkg/middleware/logging.go
  • pkg/middleware/cors.go
  • example/handler.go
  • pkg/middleware/error.go
  • pkg/middleware/auth.go
  • pkg/middleware/middlewares.go
  • pkg/server/utils.go
  • pkg/server/http_server.go
  • pkg/middleware/permission.go
📚 Learning: 2025-08-27T09:18:34.994Z
Learnt from: itsLeonB
Repo: itsLeonB/ginkgo PR: 1
File: http_server.go:22-37
Timestamp: 2025-08-27T09:18:34.994Z
Learning: In the ginkgo package, the NewHttpServer constructor intentionally uses log.Fatal for parameter validation (nil server, invalid timeout, nil logger) as a fail-fast design choice rather than returning errors.

Applied to files:

  • pkg/middleware/logging.go
  • example/handler.go
  • pkg/middleware/error.go
  • pkg/server/utils.go
  • example/server.go
  • pkg/server/http_server.go
📚 Learning: 2025-08-27T09:12:03.396Z
Learnt from: itsLeonB
Repo: itsLeonB/ginkgo PR: 1
File: error_middleware.go:3-16
Timestamp: 2025-08-27T09:12:03.396Z
Learning: In the ginkgo package error middleware, unwrapped errors (including known types like validator.ValidationErrors and json.SyntaxError) are intentionally treated as 500 Internal Server Errors to enforce the use of eris.Wrap for consistent stack trace collection and better observability.

Applied to files:

  • pkg/middleware/logging.go
  • example/handler.go
  • pkg/middleware/error.go
  • pkg/middleware/middlewares.go
  • pkg/server/utils.go
🧬 Code graph analysis (6)
pkg/middleware/cors.go (1)
pkg/middleware/middlewares.go (1)
  • MiddlewareProvider (10-12)
example/handler.go (1)
pkg/server/utils.go (1)
  • Handler (99-107)
pkg/middleware/auth.go (1)
pkg/middleware/middlewares.go (1)
  • MiddlewareProvider (10-12)
pkg/server/utils.go (1)
pkg/response/responses.go (1)
  • JSONResponse (30-34)
example/server.go (3)
pkg/server/http_server.go (2)
  • Http (15-20)
  • New (22-37)
pkg/middleware/middlewares.go (1)
  • NewMiddlewareProvider (14-19)
pkg/server/utils.go (1)
  • Handler (99-107)
pkg/middleware/permission.go (1)
pkg/middleware/middlewares.go (1)
  • MiddlewareProvider (10-12)
🔇 Additional comments (23)
.gitignore (1)

10-11: LGTM!

Standard and appropriate ignore patterns for a Go project. The additions of bin/ and tmp/ align well with the package restructuring in this PR, and uncommenting .vscode/ keeps IDE-specific files out of the repository.

Also applies to: 34-34

Makefile (1)

18-38: No tests currently exist; scope expansion is appropriate for restructured package layout.

The verification confirms no test files exist in the repository (no _test.go files found, and the test/ directory has been removed). The Makefile scope change from ./test/... to ./... is correctly aligned with the package restructuring and follows standard Go practice for testing across multiple packages. This allows future tests to be added throughout the pkg/ and example/ directories as needed.

go.mod (1)

10-10: Pre-release dependency in use.

Using v0.2.0-pre2 (a pre-release version) of ungerr may introduce instability or breaking changes before the final release. Consider pinning to a stable release version if available, or ensure adequate testing covers this dependency's behavior.

example/main.go (1)

1-6: LGTM!

Clean and minimal entry point for the example application. The separation of setup logic into setup() keeps main() focused on orchestration.

pkg/middleware/logging.go (1)

1-1: Package rename looks good.

The package name change from ginkgo to middleware aligns with the new modular package structure.

example/server.go (1)

39-39: Passing nil for shutdownFunc is acceptable.

Per the server.New implementation in pkg/server/http_server.go, a nil shutdownFunc triggers a warning log but continues execution. This is fine for an example application.

pkg/middleware/cors.go (1)

8-23: LGTM!

The CORS middleware implementation follows the established fail-fast pattern for validation errors (consistent with learnings) while gracefully falling back to defaults when no config is provided. The validation check before creating the middleware is a good defensive practice.

example/handler.go (1)

12-52: LGTM! Comprehensive error scenario coverage.

These handlers effectively demonstrate the different error handling paths:

  • Success case
  • ungerr wrapped errors (Unknown, Wrap, BadRequestError)
  • Unwrapped errors (intentionally treated as 500 per error middleware design)
  • Known error types
  • Panic recovery

This provides good examples for testing the server.Handler wrapper behavior.

pkg/middleware/middlewares.go (2)

14-19: LGTM!

The constructor correctly uses log.Fatal for the nil logger check since the injected logger isn't available yet at that point. This follows the fail-fast pattern established in the codebase.


21-23: Function newErrorMiddleware is properly defined.

The function exists in pkg/middleware/error.go:37 with the correct signature func newErrorMiddleware(logger ezutil.Logger) gin.HandlerFunc, returning middleware.handle as expected.

pkg/server/utils.go (2)

58-64: LGTM - Clean convenience wrapper.

The BindJSON function provides a consistent API alongside BindRequest. The error wrapping with eris is appropriate for stack trace collection, aligning with the project's error handling philosophy.


99-106: Handler wrapper design is clean; nil responses are properly handled by omitzero.

The Data field in JSONResponse uses the json:"data,omitzero" tag, which is supported by Go 1.25.0. When resp is nil, the data field will be omitted from the JSON output rather than serialized as null. This provides a clean separation of concerns and proper handling of endpoints that return no data.

pkg/middleware/auth.go (1)

21-23: Fail-fast validation is consistent with project patterns.

Based on learnings, using log.Fatalf for tokenCheckFunc validation is an intentional fail-fast design choice, consistent with NewCorsMiddleware and other constructors.

pkg/server/http_server.go (2)

22-37: Constructor validation is well-structured.

The fail-fast design using log.Fatal and logger.Fatal for critical parameters is consistent with project patterns per learnings. The validation order correctly checks logger first (using standard log.Fatal) before using the logger for subsequent checks.


39-67: ServeGracefully implementation is solid.

The graceful shutdown logic properly handles signal capture, context timeout, and resource cleanup. The only change is the receiver type rename, which doesn't affect functionality.

pkg/middleware/error.go (4)

23-30: The errorObject type correctly implements the error interface.

The struct provides structured error data with Code and Detail fields, and the Error() method satisfies the error interface for compatibility with []error in JSONResponse.


76-85: UnknownError handling logic looks correct.

The flow properly distinguishes between:

  1. UnknownError with an unwrappable original error → logs original error type
  2. UnknownError without wrapped content → logs the unknown error message

Both paths correctly mask and return InternalServerError, consistent with the project's error handling philosophy per learnings.


122-141: Good consolidated logging for unwrapped errors.

The logUnwrappedError function provides comprehensive debugging information in a single log entry, making it easier to identify where errors need proper wrapping. The message clearly guides developers to use ungerr.Wrap() or ungerr.AppError.


144-198: Comprehensive panic recovery with detailed analysis.

The panic handling provides excellent observability with:

  • Type-specific analysis for common panic scenarios
  • Full stack trace inclusion
  • Request context (method, path, handler)
  • Proper fallback when response is already written
pkg/response/responses.go (4)

8-9: Form tags are correct for query parameter binding.

Using form tags instead of query is the correct approach for Gin's ShouldBindQuery and related binding methods.


38-42: Simplified NewResponse API is clean.

Directly accepting data instead of a message with chained methods reduces boilerplate. The change aligns with the updated Handler wrapper in pkg/server/utils.go.


46-50: Variadic error signature provides flexibility.

The variadic ...error signature allows passing single or multiple errors, which is useful for validation scenarios that may produce multiple errors.


30-34: The errors field correctly serializes structured error objects.

Go's JSON marshaler respects struct field tags when marshaling concrete types through interfaces. When errorObject instances are placed in the []error field, they serialize to {code, detail} objects with their fields preserved, not as strings. The current implementation correctly maintains the error structure during JSON serialization.

Comment thread pkg/middleware/error.go

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

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 (12)
pkg/middleware/error_test.go (3)

21-41: Remove duplicate code and unused variables.

The test logic is correct, but there are code quality issues:

  • Line 27 duplicates line 24 (redundant request creation)
  • Lines 22-24 create a context that's never used since line 31 creates a router
  • Lines 26-30 contain confusing comments that don't add clarity
🔎 Proposed cleanup
 	t.Run("app error", func(t *testing.T) {
 		w := httptest.NewRecorder()
-		c, _ := gin.CreateTestContext(w)
-		c.Request = httptest.NewRequest("GET", "/", nil)
-
-		// Setup handlers properly to simulate middleware chain
-		c.Request = httptest.NewRequest("GET", "/", nil)
+		req := httptest.NewRequest("GET", "/", nil)
 
-		// We manually execute the middleware logic sequence since gin.CreateTestContext doesn't easily chaining without router
-		// Better approach: use a router
 		r := gin.New()
 		r.Use(mw)
 		r.GET("/", func(c *gin.Context) {
 			_ = c.Error(ungerr.NotFoundError("resource not found"))
 		})
 
-		r.ServeHTTP(w, c.Request)
+		r.ServeHTTP(w, req)
 
 		assert.Equal(t, http.StatusNotFound, w.Code)
 		assert.Contains(t, w.Body.String(), "Not Found")
 	})

43-58: Remove unused context creation.

The test correctly verifies that raw errors return 500 (consistent with the learned behavior that unwrapped errors are intentionally treated as Internal Server Errors). However, lines 44-46 create an unused context.

🔎 Proposed cleanup
 	t.Run("raw error conversion", func(t *testing.T) {
 		w := httptest.NewRecorder()
-		c, _ := gin.CreateTestContext(w)
-		c.Request = httptest.NewRequest("GET", "/", nil)
+		req := httptest.NewRequest("GET", "/", nil)
 
 		r := gin.New()
 		r.Use(mw)
 		r.GET("/", func(c *gin.Context) {
 			_ = c.Error(errors.New("something broke"))
 		})
 
-		r.ServeHTTP(w, c.Request)
+		r.ServeHTTP(w, req)
 
 		assert.Equal(t, http.StatusInternalServerError, w.Code)
 		assert.Contains(t, w.Body.String(), "Internal Server Error")
 	})

Based on learnings, this correctly tests that unwrapped errors are intentionally treated as 500 Internal Server Errors.


60-75: Remove unused context creation.

The test correctly verifies panic recovery returns 500 Internal Server Error. However, lines 61-63 create an unused context.

🔎 Proposed cleanup
 	t.Run("panic recovery", func(t *testing.T) {
 		w := httptest.NewRecorder()
-		c, _ := gin.CreateTestContext(w)
-		c.Request = httptest.NewRequest("GET", "/", nil)
+		req := httptest.NewRequest("GET", "/", nil)
 
 		r := gin.New()
 		r.Use(mw)
 		r.GET("/", func(c *gin.Context) {
 			panic("oops")
 		})
 
-		r.ServeHTTP(w, c.Request)
+		r.ServeHTTP(w, req)
 
 		assert.Equal(t, http.StatusInternalServerError, w.Code)
 		assert.Contains(t, w.Body.String(), "Internal Server Error")
 	})
pkg/response/responses_test.go (3)

11-21: Consider adding test cases for edge cases.

The current test covers the happy path well. For more comprehensive coverage, consider adding test cases for:

  • nil data input
  • Empty data structures
  • Verification of omitzero behavior on the Data field when marshaled to JSON

23-30: Consider testing variadic behavior with multiple errors.

Since NewErrorResponse accepts variadic errors (err ...error), consider adding test cases for:

  • Multiple errors: NewErrorResponse(err1, err2, err3)
  • Zero errors: NewErrorResponse() (edge case)

This would provide more comprehensive coverage of the function's API contract.


32-76: Pagination test logic is correct.

The test cases cover important scenarios:

  • Middle page pagination with correct HasNextPage/HasPrevPage flags
  • Zero limit edge case
  • Last page scenario
  • IsZero method

For additional coverage, consider adding a test for the first page scenario (page 1) to verify that HasPrevPage is false and HasNextPage is true.

pkg/server/http_server_test.go (1)

12-36: Strong test coverage for the constructor.

The test effectively covers the main scenarios: successful initialization with all parameters and handling of nil shutdownFunc. The subtest organization is clear and the assertions appropriately verify the internal state.

💡 Optional: Add logger field assertion for completeness

Consider verifying that the logger field is properly set in both subtests:

 		assert.NotNil(t, s)
 		assert.Equal(t, srv, s.srv)
 		assert.Equal(t, timeout, s.timeout)
+		assert.Equal(t, logger, s.logger)
 		assert.NotNil(t, s.shutdownFunc)

And similarly in the "nil shutdown func" subtest:

 		assert.NotNil(t, s)
+		assert.Equal(t, logger, s.logger)
 		assert.Nil(t, s.shutdownFunc)
pkg/server/utils_test.go (1)

76-84: Add Content-Type header for more realistic test.

The test should set the Content-Type header to "application/json" to better reflect real-world HTTP requests and ensure the binding behavior is validated under realistic conditions.

🔎 Suggested improvement
 	t.Run("valid json", func(t *testing.T) {
 		w := httptest.NewRecorder()
 		c, _ := gin.CreateTestContext(w)
 		c.Request = httptest.NewRequest("POST", "/", bytes.NewBufferString(`{"name":"test"}`))
+		c.Request.Header.Set("Content-Type", "application/json")
 
 		val, err := BindJSON[TestStruct](c)
 		assert.NoError(t, err)
 		assert.Equal(t, "test", val.Name)
 	})
pkg/middleware/logging_test.go (2)

20-21: Remove the developer comment.

This appears to be a temporary implementation note that doesn't add value for future maintainers.

🔎 Proposed fix
-	// Corrected: No arguments locally, it seems from logging.go content
 	mw := mp.NewLoggingMiddleware()

15-47: Consider verifying actual logging behavior.

The tests validate that the middleware doesn't crash and returns a 200 status, but they don't verify that the middleware performs its primary function—logging request details. Consider enhancing the tests to:

  • Capture and assert on log output (method, path, status code, duration)
  • Verify the logger was called with expected parameters
  • For POST requests, confirm that the request body was logged

This would provide stronger confidence that the logging middleware works correctly beyond just not panicking.

Example approach using a mock logger

You could use a mock logger or capture log output to verify behavior:

// Example: Add a test recorder to capture logs
type testLogRecorder struct {
    logs []string
}

func (r *testLogRecorder) Log(msg string) {
    r.logs = append(r.logs, msg)
}

// Then in your test:
recorder := &testLogRecorder{}
// Use recorder to verify expected log entries were created
pkg/middleware/permission_test.go (1)

48-67: Consider adding error assertions for consistency.

The "unknown role" and "missing role" subtests only check c.IsAborted() but don't verify c.Errors like the "no permission" test does. Adding assert.NotEmpty(t, c.Errors) would improve consistency and catch regressions in error recording.

🔎 Proposed fix for consistency
 	t.Run("unknown role", func(t *testing.T) {
 		w := httptest.NewRecorder()
 		c, _ := gin.CreateTestContext(w)
 		c.Request = httptest.NewRequest("POST", "/", nil)
 		c.Set("role", "guest")

 		mw(c)

 		assert.True(t, c.IsAborted())
+		assert.NotEmpty(t, c.Errors)
 	})

 	t.Run("missing role", func(t *testing.T) {
 		w := httptest.NewRecorder()
 		c, _ := gin.CreateTestContext(w)
 		c.Request = httptest.NewRequest("POST", "/", nil)

 		mw(c)

 		assert.True(t, c.IsAborted())
+		assert.NotEmpty(t, c.Errors)
 	})
pkg/middleware/ratelimit_test.go (1)

57-67: Consider handling JSON unmarshal error and using safer type assertions.

The JSON unmarshal error is silently ignored, and the type assertions on lines 60 and 62 could panic if the response structure changes unexpectedly.

🔎 Proposed fix for safer response parsing
 		var response map[string]interface{}
-		_ = json.Unmarshal(w2.Body.Bytes(), &response)
+		err := json.Unmarshal(w2.Body.Bytes(), &response)
+		assert.NoError(t, err, "failed to unmarshal response")

 		errors, ok := response["errors"].([]interface{})
-		if ok && len(errors) > 0 {
-			errObj := errors[0].(map[string]interface{})
+		if assert.True(t, ok && len(errors) > 0, "expected errors in response") {
+			errObj, ok := errors[0].(map[string]interface{})
+			assert.True(t, ok, "expected error object")
 			assert.Equal(t, "Too Many Requests", errObj["code"])
 			assert.Equal(t, "rate limit exceeded", errObj["detail"])
-		} else {
-			assert.Fail(t, "expected errors in response")
 		}
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 69425ce and e525b99.

⛔ Files ignored due to path filters (1)
  • go.sum is excluded by !**/*.sum
📒 Files selected for processing (12)
  • Makefile
  • example/server.go
  • go.mod
  • pkg/middleware/auth_test.go
  • pkg/middleware/error_test.go
  • pkg/middleware/logging_test.go
  • pkg/middleware/permission_test.go
  • pkg/middleware/ratelimit.go
  • pkg/middleware/ratelimit_test.go
  • pkg/response/responses_test.go
  • pkg/server/http_server_test.go
  • pkg/server/utils_test.go
🚧 Files skipped from review as they are similar to previous changes (2)
  • example/server.go
  • go.mod
🧰 Additional context used
🧠 Learnings (5)
📓 Common learnings
Learnt from: itsLeonB
Repo: itsLeonB/ginkgo PR: 1
File: middlewares.go:23-28
Timestamp: 2025-08-27T09:29:56.699Z
Learning: In the ginkgo package, the NewCorsMiddleware function intentionally uses log.Fatalf for CORS configuration validation as a fail-fast design choice rather than degrading gracefully.
Learnt from: itsLeonB
Repo: itsLeonB/ginkgo PR: 1
File: error_middleware.go:3-16
Timestamp: 2025-08-27T09:12:03.396Z
Learning: In the ginkgo package error middleware, unwrapped errors (including known types like validator.ValidationErrors and json.SyntaxError) are intentionally treated as 500 Internal Server Errors to enforce the use of eris.Wrap for consistent stack trace collection and better observability.
📚 Learning: 2025-08-27T09:32:11.080Z
Learnt from: itsLeonB
Repo: itsLeonB/ginkgo PR: 1
File: middlewares.go:39-44
Timestamp: 2025-08-27T09:32:11.080Z
Learning: In the ginkgo package, the NewAuthMiddleware function intentionally uses log.Fatalf for tokenCheckFunc validation as a fail-fast design choice, similar to the CORS middleware pattern.

Applied to files:

  • pkg/middleware/error_test.go
  • pkg/middleware/permission_test.go
  • pkg/middleware/ratelimit.go
  • pkg/middleware/ratelimit_test.go
  • pkg/middleware/auth_test.go
  • pkg/middleware/logging_test.go
📚 Learning: 2025-08-27T09:29:56.699Z
Learnt from: itsLeonB
Repo: itsLeonB/ginkgo PR: 1
File: middlewares.go:23-28
Timestamp: 2025-08-27T09:29:56.699Z
Learning: In the ginkgo package, the NewCorsMiddleware function intentionally uses log.Fatalf for CORS configuration validation as a fail-fast design choice rather than degrading gracefully.

Applied to files:

  • pkg/middleware/error_test.go
  • pkg/middleware/ratelimit.go
  • pkg/middleware/auth_test.go
  • pkg/middleware/logging_test.go
📚 Learning: 2025-08-27T09:12:03.396Z
Learnt from: itsLeonB
Repo: itsLeonB/ginkgo PR: 1
File: error_middleware.go:3-16
Timestamp: 2025-08-27T09:12:03.396Z
Learning: In the ginkgo package error middleware, unwrapped errors (including known types like validator.ValidationErrors and json.SyntaxError) are intentionally treated as 500 Internal Server Errors to enforce the use of eris.Wrap for consistent stack trace collection and better observability.

Applied to files:

  • pkg/middleware/error_test.go
📚 Learning: 2025-08-27T09:18:34.994Z
Learnt from: itsLeonB
Repo: itsLeonB/ginkgo PR: 1
File: http_server.go:22-37
Timestamp: 2025-08-27T09:18:34.994Z
Learning: In the ginkgo package, the NewHttpServer constructor intentionally uses log.Fatal for parameter validation (nil server, invalid timeout, nil logger) as a fail-fast design choice rather than returning errors.

Applied to files:

  • pkg/server/http_server_test.go
🧬 Code graph analysis (8)
pkg/server/utils_test.go (1)
pkg/server/utils.go (4)
  • GetPathParam (15-29)
  • GetRequiredPathParam (34-43)
  • BindJSON (58-64)
  • GetFromContext (69-83)
pkg/middleware/error_test.go (2)
pkg/middleware/middlewares.go (1)
  • NewMiddlewareProvider (14-19)
pkg/server/http_server.go (1)
  • New (22-37)
pkg/middleware/permission_test.go (1)
pkg/middleware/middlewares.go (1)
  • NewMiddlewareProvider (14-19)
pkg/middleware/ratelimit.go (2)
pkg/middleware/middlewares.go (1)
  • MiddlewareProvider (10-12)
pkg/response/responses.go (1)
  • NewErrorResponse (46-50)
pkg/server/http_server_test.go (1)
pkg/server/http_server.go (1)
  • New (22-37)
pkg/middleware/ratelimit_test.go (1)
pkg/middleware/middlewares.go (1)
  • NewMiddlewareProvider (14-19)
pkg/middleware/auth_test.go (2)
pkg/middleware/middlewares.go (1)
  • NewMiddlewareProvider (14-19)
pkg/server/http_server.go (1)
  • New (22-37)
pkg/middleware/logging_test.go (1)
pkg/middleware/middlewares.go (1)
  • NewMiddlewareProvider (14-19)
🔇 Additional comments (18)
pkg/middleware/error_test.go (1)

15-19: LGTM!

The test setup correctly initializes the logger, middleware provider, and error middleware following the expected patterns.

Makefile (1)

18-38: Test path migration to ./pkg/... is complete and properly implemented.

Verification confirms all 8 test files have been successfully migrated to the new package structure (middleware, response, and server subdirectories), with no tests orphaned in the old ./test/ directory. The Makefile changes align correctly with the actual repository organization. The restructuring follows Go conventions for co-locating tests with package code.

pkg/response/responses_test.go (1)

1-9: LGTM!

Package declaration and imports are appropriate for the test file.

pkg/server/http_server_test.go (2)

1-10: LGTM: Clean package and import structure.

The package declaration and imports are appropriate for the test file, with all dependencies properly declared.


38-41: Good documentation of testing limitations.

The comment appropriately explains why fatal exit paths aren't tested and acknowledges the technical constraints. Based on learnings, the fail-fast design using log.Fatal/logger.Fatal is an intentional choice, so this limitation is acceptable and well-documented.

pkg/server/utils_test.go (3)

12-45: LGTM! Comprehensive test coverage for path parameter handling.

The test cases cover all critical paths: successful parsing, missing parameters, and type conversion failures. Assertions correctly validate all return values.


47-67: LGTM! Adequate coverage for required parameter validation.

Tests cover both success and error scenarios appropriately.


96-125: LGTM! Thorough coverage for context value retrieval.

Tests comprehensively cover success, missing value, and type assertion failure scenarios with appropriate assertions.

pkg/middleware/auth_test.go (4)

14-38: LGTM! Comprehensive test for success path.

The success test correctly verifies that the middleware passes with a valid Bearer token, context is not aborted, and user data is properly stored in the context.


40-55: LGTM! Missing token scenario covered.

The test correctly verifies that requests without an Authorization header result in an aborted context with errors.


57-91: LGTM! Error scenarios well covered.

Both "user not found" and "check error" subtests properly validate that the middleware aborts and records errors when the token check fails or returns an error.


93-109: LGTM! Unsupported strategy handling verified.

The test correctly verifies that using an unsupported auth strategy ("Basic" when only "Bearer" is supported) results in proper abort behavior.

pkg/middleware/permission_test.go (2)

12-23: LGTM! Well-structured test setup.

The test correctly initializes the middleware provider with a logger and defines a clear permission map for testing different role scenarios.


25-34: LGTM! Happy path correctly verified.

The test properly verifies that an admin role with write permission is allowed through without aborting.

pkg/middleware/ratelimit_test.go (1)

16-34: LGTM! First request allowed test is correct.

The test properly verifies that the first request from an IP passes through without being rate-limited.

pkg/middleware/ratelimit.go (3)

13-23: LGTM! Clean struct definitions.

The visitor and rateLimiter structs are well-defined with appropriate fields for tracking rate limits per IP.


35-47: LGTM! Thread-safe visitor management.

The mutex protection is correctly applied around map access, and the limiter is properly created or updated on each access.


62-84: LGTM! Middleware implementation is correct.

The middleware properly extracts client IP, checks rate limits, and returns appropriate 429 responses with structured error objects when limits are exceeded.

Comment thread pkg/middleware/logging_test.go
Comment thread pkg/middleware/ratelimit.go

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In @go.mod:
- Around line 9-12: The go.mod contains two invalid dependencies
(github.com/itsLeonB/ezutil/v2 v2.2.1 and github.com/itsLeonB/ungerr v0.2.0);
verify the correct module paths and versions on GitHub/pkg.go.dev for
github.com/itsLeonB/ezutil (confirm whether the module uses /v2 in path and pick
an existing tag) and for ungerr (confirm exact module name or remove if unused),
update or remove those require lines accordingly, update any import paths in the
code that reference those modules to match the corrected module path/version (or
remove usages), then run go mod tidy and go mod download to regenerate go.sum
and ensure the remaining dependencies (github.com/stretchr/testify and
golang.org/x/time) resolve cleanly.

In @pkg/middleware/permission.go:
- Around line 21-38: The role validation currently returns ungerr.Unknownf
(which maps to HTTP 500); update the two checks that use ungerr.Unknownf (the
missing-role check using role := ctx.GetString(roleContextKey) and the
unknown-role lookup against permissionMap[role]) to use
ungerr.ForbiddenError(...) instead so access-control failures return 403 like
the existing permission denial (ctx.Error(ungerr.ForbiddenError("..."))). Ensure
you replace both ungerr.Unknownf calls with ungerr.ForbiddenError and keep the
same descriptive messages.
🧹 Nitpick comments (6)
pkg/server/utils.go (1)

58-64: Consider delegating to BindRequest to reduce duplication.

The BindJSON function duplicates the error handling pattern from BindRequest. You could simplify this by delegating to the existing function.

♻️ Proposed refactor
 func BindJSON[T any](ctx *gin.Context) (T, error) {
-	var zero T
-	if err := ctx.ShouldBindJSON(&zero); err != nil {
-		return zero, ungerr.Wrap(err, "failed to bind JSON request")
-	}
-	return zero, nil
+	return BindRequest[T](ctx, binding.JSON)
 }
pkg/server/utils_test.go (2)

48-68: Consider adding test for invalid type conversion.

The test covers valid and missing parameter scenarios but doesn't test the case where parsing fails (e.g., GetRequiredPathParam[int](c, "id") with id="abc"). Since GetRequiredPathParam calls ezutil.Parse[T], which can return an error, this scenario should be covered.

📝 Suggested additional test case
t.Run("invalid type", func(t *testing.T) {
	w := httptest.NewRecorder()
	c, _ := gin.CreateTestContext(w)
	c.Params = gin.Params{{Key: "id", Value: "abc"}}

	_, err := GetRequiredPathParam[int](c, "id")
	assert.Error(t, err)
})

187-213: Consider verifying response body structure in success test.

The success test checks the status code but doesn't verify the response body structure. Since Handler wraps responses in response.JSONResponse{Data: resp}, it would be valuable to verify this envelope structure.

📝 Enhanced success test
 t.Run("success", func(t *testing.T) {
 	w := httptest.NewRecorder()
 	c, _ := gin.CreateTestContext(w)
 
 	handler := Handler(200, func(ctx *gin.Context) (any, error) {
 		return map[string]string{"message": "success"}, nil
 	})
 
 	handler(c)
 	assert.Equal(t, 200, w.Code)
+	assert.Contains(t, w.Body.String(), `"data"`)
+	assert.Contains(t, w.Body.String(), `"message":"success"`)
 })
pkg/middleware/permission.go (1)

15-19: Validate middleware configuration at construction time (fail fast), not per-request.

Right now roleContextKey == "", requiredPermission == "", or permissionMap == nil will quietly turn into runtime denials (or confusing errors) on every request. Given your existing fail-fast pattern in other middleware/provider constructors (per learnings), consider validating these inputs before returning the handler.

Proposed change
 package middleware

 import (
+	"log"
 	"slices"

 	"github.com/gin-gonic/gin"
 	"github.com/itsLeonB/ungerr"
 )
@@
 func (mp *MiddlewareProvider) NewPermissionMiddleware(
 	roleContextKey string,
 	requiredPermission string,
 	permissionMap map[string][]string,
 ) gin.HandlerFunc {
+	if roleContextKey == "" {
+		log.Fatal("roleContextKey cannot be empty")
+	}
+	if requiredPermission == "" {
+		log.Fatal("requiredPermission cannot be empty")
+	}
+	if permissionMap == nil {
+		log.Fatal("permissionMap cannot be nil")
+	}
 	return func(ctx *gin.Context) {
 		role := ctx.GetString(roleContextKey)
 		if role == "" {
pkg/middleware/middlewares_test.go (1)

10-25: Consider adding a subprocess test for NewMiddlewareProvider(nil) to lock in the fail-fast contract.

Since NewMiddlewareProvider uses log.Fatal (exits), verifying it requires an exec.Command-style test. Optional, but it prevents accidental future softening of this behavior. (Based on learnings, this fail-fast pattern is intentional.)

pkg/middleware/cors_test.go (1)

11-28: Non-nil assertions are low-signal; prefer asserting actual CORS headers via httptest.

Consider spinning up a tiny gin.Engine, installing the middleware, issuing an OPTIONS / preflight request, and asserting the expected Access-Control-* headers for both default config and custom config.

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e525b99 and 8e4cc23.

⛔ Files ignored due to path filters (1)
  • go.sum is excluded by !**/*.sum
📒 Files selected for processing (10)
  • .github/workflows/ci.yml
  • Makefile
  • go.mod
  • pkg/middleware/auth.go
  • pkg/middleware/cors_test.go
  • pkg/middleware/middlewares_test.go
  • pkg/middleware/permission.go
  • pkg/server/utils.go
  • pkg/server/utils_test.go
  • scripts/git-pre-push.sh
💤 Files with no reviewable changes (1)
  • .github/workflows/ci.yml
🚧 Files skipped from review as they are similar to previous changes (1)
  • pkg/middleware/auth.go
🧰 Additional context used
🧠 Learnings (4)
📓 Common learnings
Learnt from: itsLeonB
Repo: itsLeonB/ginkgo PR: 1
File: middlewares.go:23-28
Timestamp: 2025-08-27T09:29:56.699Z
Learning: In the ginkgo package, the NewCorsMiddleware function intentionally uses log.Fatalf for CORS configuration validation as a fail-fast design choice rather than degrading gracefully.
Learnt from: itsLeonB
Repo: itsLeonB/ginkgo PR: 1
File: middlewares.go:39-44
Timestamp: 2025-08-27T09:32:11.080Z
Learning: In the ginkgo package, the NewAuthMiddleware function intentionally uses log.Fatalf for tokenCheckFunc validation as a fail-fast design choice, similar to the CORS middleware pattern.
📚 Learning: 2025-08-27T09:29:56.699Z
Learnt from: itsLeonB
Repo: itsLeonB/ginkgo PR: 1
File: middlewares.go:23-28
Timestamp: 2025-08-27T09:29:56.699Z
Learning: In the ginkgo package, the NewCorsMiddleware function intentionally uses log.Fatalf for CORS configuration validation as a fail-fast design choice rather than degrading gracefully.

Applied to files:

  • pkg/middleware/cors_test.go
  • pkg/middleware/middlewares_test.go
  • go.mod
📚 Learning: 2025-08-27T09:32:11.080Z
Learnt from: itsLeonB
Repo: itsLeonB/ginkgo PR: 1
File: middlewares.go:39-44
Timestamp: 2025-08-27T09:32:11.080Z
Learning: In the ginkgo package, the NewAuthMiddleware function intentionally uses log.Fatalf for tokenCheckFunc validation as a fail-fast design choice, similar to the CORS middleware pattern.

Applied to files:

  • pkg/middleware/cors_test.go
  • pkg/middleware/middlewares_test.go
  • pkg/middleware/permission.go
📚 Learning: 2025-08-27T09:18:34.994Z
Learnt from: itsLeonB
Repo: itsLeonB/ginkgo PR: 1
File: http_server.go:22-37
Timestamp: 2025-08-27T09:18:34.994Z
Learning: In the ginkgo package, the NewHttpServer constructor intentionally uses log.Fatal for parameter validation (nil server, invalid timeout, nil logger) as a fail-fast design choice rather than returning errors.

Applied to files:

  • pkg/server/utils.go
🧬 Code graph analysis (4)
pkg/server/utils_test.go (1)
pkg/server/utils.go (7)
  • GetPathParam (15-29)
  • GetRequiredPathParam (34-43)
  • BindJSON (58-64)
  • GetFromContext (69-83)
  • BindRequest (48-56)
  • GetAndParseFromContext (88-97)
  • Handler (99-107)
pkg/middleware/cors_test.go (1)
pkg/middleware/middlewares.go (1)
  • NewMiddlewareProvider (14-19)
pkg/server/utils.go (1)
pkg/response/responses.go (1)
  • JSONResponse (30-34)
pkg/middleware/middlewares_test.go (1)
pkg/middleware/middlewares.go (1)
  • NewMiddlewareProvider (14-19)
🪛 checkmake (0.2.2)
Makefile

[warning] 3-3: Target body for "help" exceeds allowed length of 5 (11).

(maxbodylength)


[warning] 1-1: Missing required phony target "all"

(minphony)


[warning] 1-1: Missing required phony target "clean"

(minphony)

🔇 Additional comments (9)
pkg/server/utils.go (2)

1-9: LGTM! Package reorganization looks good.

The package rename from ginkgo to server and updated import paths align well with the PR's objective to reorganize packages into pkg/{middleware,response,server}.


99-107: Verify error handling middleware is configured.

The Handler wrapper delegates error responses to middleware by calling ctx.Error(err) without sending a response directly. Error handling middleware in pkg/middleware/error.go is properly registered and converts these context errors into appropriate HTTP responses.

pkg/server/utils_test.go (1)

13-46: LGTM! Comprehensive test coverage.

The test covers all three critical scenarios: valid parameter parsing, missing parameters, and invalid type conversion.

scripts/git-pre-push.sh (1)

11-16: LGTM! Excellent addition to the pre-push workflow.

Adding a build validation step between linting and testing is a sound practice. This ensures compilation succeeds before running tests, catching build-breaking changes early in the development workflow.

Makefile (3)

19-21: LGTM! Clean implementation of the build target.

The new build target provides a straightforward way to compile all packages, which integrates well with the pre-push hook validation.


24-24: LGTM! Test path expansion aligns with package reorganization.

Changing from ./test/... to ./... expands test coverage to all packages, which correctly reflects the reorganization into pkg/{middleware,response,server} structure mentioned in the PR summary.

Also applies to: 28-28, 50-50


30-46: LGTM! Per-package coverage is well-implemented.

The updated coverage targets generate individual coverage reports for each package under ./pkg/..., which is excellent for modular projects. The implementation correctly:

  • Uses go list ./pkg/... to discover packages
  • Transforms package paths to safe filenames using tr '/' '-'
  • Generates both text and HTML reports per package
pkg/middleware/permission.go (1)

4-5: Confirm Go version supports slices (stdlib).

This won’t compile if the module Go version is < 1.21.

Also applies to: 35-36

pkg/middleware/middlewares_test.go (1)

10-17: Good basic sanity test of provider construction.

Covers the happy path and confirms logger wiring.

Comment thread go.mod
Comment thread pkg/middleware/permission.go
@itsLeonB itsLeonB merged commit b35b9d7 into main Jan 9, 2026
10 checks passed
This was referenced Mar 2, 2026
Merged
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