Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 31 additions & 13 deletions app/http/controllers/validation_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,12 @@ air
********************************/

type User struct {
Context string `json:"context" form:"context"`
Name string `json:"name" form:"name"`
Date *carbon.DateTime `json:"date" form:"date"`
Context string `json:"context" form:"context"`
Name string `json:"name" form:"name"`
Date *carbon.DateTime `json:"date" form:"date"`
Age int `json:"age" form:"age"`
Items []requests.ValidationItem `json:"items" form:"items"`
Meta map[string]any `json:"meta" form:"meta"`
}

type ValidationController struct {
Expand All @@ -52,10 +55,13 @@ func NewValidationController() *ValidationController {

func (r *ValidationController) Json(ctx http.Context) http.Response {
ctx.WithValue("ctx", "context")
validator, err := ctx.Request().Validate(map[string]string{
"context": "required",
"name": "required",
"date": "required|date",
validator, err := ctx.Request().Validate(map[string]any{
"context": "required",
"name": "required",
"date": "required|date",
"items.*.name": "sometimes|required|string",
"meta": "sometimes|map",
"meta.name": "sometimes|required|string",
}, validation.PrepareForValidation(func(ctx context.Context, data contractsvalidation.Data) error {
if c, exist := data.Get("context"); exist {
// Test getting value from context: ValidationController.Request
Expand Down Expand Up @@ -84,11 +90,16 @@ func (r *ValidationController) Json(ctx http.Context) http.Response {
})
}

return ctx.Response().Success().Json(http.Json{
response := http.Json{
"context": user.Context,
"name": user.Name,
"date": user.Date.ToDateTimeString(),
})
"age": user.Age,
"items": user.Items,
"meta": user.Meta,
}

return ctx.Response().Success().Json(response)
Comment on lines +93 to +102
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Potential nil pointer dereference on user.Date.

At line 96, user.Date.ToDateTimeString() is called without checking if Date is nil. Since Date is a pointer type (*carbon.DateTime), this could cause a panic if the validation passes but the date field is somehow not populated during binding.

🛡️ Proposed defensive check
 	response := http.Json{
 		"context": user.Context,
 		"name":    user.Name,
-		"date":    user.Date.ToDateTimeString(),
+		"date":    "",
 		"age":     user.Age,
 		"items":   user.Items,
 		"meta":    user.Meta,
 	}
+	if user.Date != nil {
+		response["date"] = user.Date.ToDateTimeString()
+	}

 	return ctx.Response().Success().Json(response)
📝 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
response := http.Json{
"context": user.Context,
"name": user.Name,
"date": user.Date.ToDateTimeString(),
})
"age": user.Age,
"items": user.Items,
"meta": user.Meta,
}
return ctx.Response().Success().Json(response)
response := http.Json{
"context": user.Context,
"name": user.Name,
"date": "",
"age": user.Age,
"items": user.Items,
"meta": user.Meta,
}
if user.Date != nil {
response["date"] = user.Date.ToDateTimeString()
}
return ctx.Response().Success().Json(response)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/http/controllers/validation_controller.go` around lines 93 - 102, The
code builds a response using user.Date.ToDateTimeString() without guarding
against a nil pointer; update the response construction in validation controller
(where response, user and ctx.Response().Success().Json are used) to nil-check
user.Date before calling ToDateTimeString() — e.g. determine a safe date value
(empty string or nil) by checking if user.Date != nil and only calling
ToDateTimeString() when non-nil, then include that safe value in the "date"
field of the response.

}

func (r *ValidationController) Request(ctx http.Context) http.Response {
Expand All @@ -108,22 +119,28 @@ func (r *ValidationController) Request(ctx http.Context) http.Response {
})
}

return ctx.Response().Success().Json(http.Json{
response := http.Json{
"context": validationCreate.Context,
"name": validationCreate.Name,
"tags": validationCreate.Tags,
"scores": validationCreate.Scores,
"items": validationCreate.Items,
"meta": validationCreate.Meta,
"date": validationCreate.Date.ToDateTimeString(),
"code": validationCreate.Code,
})
"age": validationCreate.Age,
}

return ctx.Response().Success().Json(response)
}

func (r *ValidationController) Form(ctx http.Context) http.Response {
ctx.WithValue("ctx", "context")
validator, err := facades.Validation().Make(ctx, map[string]any{
"context": ctx.Request().Input("context"),
"name": ctx.Request().Input("name"),
}, map[string]string{
"age": ctx.Request().Input("age"),
}, map[string]any{
"context": "required",
"name": "required",
}, validation.PrepareForValidation(func(ctx context.Context, data contractsvalidation.Data) error {
Expand Down Expand Up @@ -157,6 +174,7 @@ func (r *ValidationController) Form(ctx http.Context) http.Response {
return ctx.Response().Success().Json(http.Json{
"context": user.Context,
"name": user.Name,
"age": user.Age,
})
}

Expand All @@ -176,7 +194,7 @@ func (r *ValidationController) Upload(ctx http.Context) http.Response {
}))
}

validator, err := ctx.Request().Validate(map[string]string{
validator, err := ctx.Request().Validate(map[string]any{
"f": rule,
}, options...)
if err != nil {
Expand Down
19 changes: 15 additions & 4 deletions app/http/controllers/validation_controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import (
"github.com/goravel/gin"
"github.com/stretchr/testify/mock"
"github.com/stretchr/testify/suite"

"goravel/app/http/requests"
)

type ValidationControllerTestSuite struct {
Expand All @@ -35,17 +37,23 @@ func (s *ValidationControllerTestSuite) TestJson() {
mockValidator := mockFactory.ValidationValidator()
mockContext.EXPECT().WithValue("ctx", "context").Once()
mockContext.EXPECT().Request().Return(mockRequest).Once()
mockRequest.EXPECT().Validate(map[string]string{
"context": "required",
"name": "required",
"date": "required|date",
mockRequest.EXPECT().Validate(map[string]any{
"context": "required",
"name": "required",
"date": "required|date",
"items.*.name": "sometimes|required|string",
"meta": "sometimes|map",
"meta.name": "sometimes|required|string",
}, mock.AnythingOfType("validation.Option")).Return(mockValidator, nil).Once()
mockValidator.EXPECT().Fails().Return(false).Once()
var user User
mockValidator.EXPECT().Bind(&user).Run(func(user any) {
user.(*User).Context = "ctx_context"
user.(*User).Name = "Goravel"
user.(*User).Date = carbon.NewDateTime(carbon.Parse("2024-07-08 22:34:31"))
user.(*User).Age = 1
user.(*User).Items = []requests.ValidationItem{{Name: "item1"}}
user.(*User).Meta = map[string]any{"source": "api"}
}).Return(nil).Once()
mockContext.EXPECT().Response().Return(mockResponse).Once()
mockResponseStatus := mockFactory.ResponseStatus()
Expand All @@ -56,6 +64,9 @@ func (s *ValidationControllerTestSuite) TestJson() {
"context": "ctx_context",
"name": "Goravel",
"date": "2024-07-08 22:34:31",
"age": 1,
"items": []requests.ValidationItem{{Name: "item1"}},
"meta": map[string]any{"source": "api"},
}).Return(resp).Once()

s.Equal(resp, NewValidationController().Json(mockContext))
Expand Down
4 changes: 2 additions & 2 deletions app/http/requests/user_create.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ func (r *UserCreate) Authorize(ctx http.Context) error {
return nil
}

func (r *UserCreate) Rules(ctx http.Context) map[string]string {
return map[string]string{
func (r *UserCreate) Rules(ctx http.Context) map[string]any {
return map[string]any{
"name": "required",
}
}
42 changes: 26 additions & 16 deletions app/http/requests/validation_create.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,26 +8,36 @@ import (
)

type ValidationCreate struct {
Context string `form:"context" json:"context"`
Name string `form:"name" json:"name"`
Tags []string `form:"tags" json:"tags"`
Scores []int `form:"scores" json:"scores"`
Date carbon.Carbon `form:"date" json:"date"`
Code int `form:"code" json:"code"`
Context string `form:"context" json:"context"`
Name string `form:"name" json:"name"`
Tags []string `form:"tags" json:"tags"`
Scores []int `form:"scores" json:"scores"`
Items []ValidationItem `form:"items" json:"items"`
Meta map[string]any `form:"meta" json:"meta"`
Date carbon.Carbon `form:"date" json:"date"`
Code int `form:"code" json:"code"`
Age int `form:"age" json:"age"`
}

type ValidationItem struct {
Name string `form:"name" json:"name"`
}

func (r *ValidationCreate) Authorize(ctx http.Context) error {
return nil
}

func (r *ValidationCreate) Rules(ctx http.Context) map[string]string {
return map[string]string{
"name": "required",
"context": "required",
"tags.*": "required|string",
"scores.*": "required|int",
"date": "required|date",
"code": `required|regex:^\d{4,6}$`,
func (r *ValidationCreate) Rules(ctx http.Context) map[string]any {
return map[string]any{
"name": "required",
"context": "required",
"tags.*": "required|string",
"scores.*": "required|int",
"items.*.name": "sometimes|required|string",
"meta": "sometimes|map",
"meta.name": "sometimes|required|string",
"date": "required|date",
"code": `required|regex:^\d{4,6}$`,
}
}

Expand Down Expand Up @@ -55,8 +65,8 @@ func (r *ValidationCreate) PrepareForValidation(ctx http.Context, data validatio
return nil
}

func (r *ValidationCreate) Filters(ctx http.Context) map[string]string {
return map[string]string{
func (r *ValidationCreate) Filters(ctx http.Context) map[string]any {
return map[string]any{
"name": "trim",
}
}
14 changes: 7 additions & 7 deletions app/rules/exists.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,20 +9,20 @@ import (
)

/**
* exists 验证一个值在某个表中的字段中存在,相较于Laravel,支持同时判断多个字段
* exists verify a value exists in a table field, compared to Laravel, support judging multiple fields at the same time
* 用法:exists:表名称,字段名称,字段名称,字段名称
* Usage: exists:table_name,field_name,field_name,field_name
* 例子:exists:users,phone,email
* Example: exists:users,phone,email
* custom_exists 验证一个值在某个表中的字段中存在,相较于Laravel,支持同时判断多个字段
* custom_exists verify a value exists in a table field, compared to Laravel, support judging multiple fields at the same time
* custom_exists:表名称,字段名称,字段名称,字段名称
* Usage: custom_exists:table_name,field_name,field_name,field_name
* custom_exists:users,phone,email
* Example: custom_exists:users,phone,email
*/

type Exists struct {
}

// Signature The name of the rule.
func (receiver *Exists) Signature() string {
return "exists"
return "custom_exists"
}

// Passes Determine if the validation rule passes.
Expand Down
57 changes: 0 additions & 57 deletions app/rules/not_exists.go

This file was deleted.

1 change: 0 additions & 1 deletion bootstrap/rules.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,5 @@ import (
func Rules() []validation.Rule {
return []validation.Rule{
&rules.Exists{},
&rules.NotExists{},
}
}
Loading
Loading