From 8ee6ecde9a1e66e702df736f89cff27f21bf9323 Mon Sep 17 00:00:00 2001 From: Jeremy Mouton Date: Wed, 22 Oct 2025 01:52:04 +0200 Subject: [PATCH 01/18] Feat: Add test for pointer types in auto-generated parameters --- auto_params_test.go | 58 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/auto_params_test.go b/auto_params_test.go index aa6e57e..40dfe0d 100644 --- a/auto_params_test.go +++ b/auto_params_test.go @@ -236,3 +236,61 @@ func TestNoParametersWhenNoStruct(t *testing.T) { _, hasParams := getOp["parameters"] assert.False(t, hasParams, "Should not have parameters when no input struct") } + +func TestAutoParamsPointerTypesInline(t *testing.T) { + app := fiber.New() + oapi := New(app) + + type PointerTestInput struct { + ID string `path:"id" validate:"required"` + OptionalName *string `query:"optionalName"` + RequiredName string `query:"requiredName" validate:"required"` + OmitEmpty string `query:"omitEmpty" validate:"omitempty"` + } + + type PointerTestOutput struct { + Message string `json:"message"` + } + + type PointerTestError struct { + Code int `json:"code"` + } + + Get(oapi, "/pointer/:id", func(c *fiber.Ctx, input PointerTestInput) (PointerTestOutput, PointerTestError) { + return PointerTestOutput{Message: "ok"}, PointerTestError{} + }, OpenAPIOptions{ + OperationID: "testPointerTypes", + Summary: "Test pointer types in parameters", + }) + + spec := oapi.GenerateOpenAPISpec() + paths := spec["paths"].(map[string]interface{}) + pointerPath := paths["/pointer/{id}"].(map[string]interface{}) + getOp := pointerPath["get"].(map[string]interface{}) + parameters := getOp["parameters"].([]map[string]interface{}) + + // Should have 4 parameters + assert.Len(t, parameters, 4, "Should have 4 parameters") + + paramMap := make(map[string]map[string]interface{}) + for _, param := range parameters { + if name, ok := param["name"].(string); ok { + paramMap[name] = param + } + } + + // Check pointer type is optional and nullable + optionalNameParam := paramMap["optionalName"] + assert.Equal(t, false, optionalNameParam["required"], "Pointer types should be optional by default") + if schema, ok := optionalNameParam["schema"].(map[string]interface{}); ok { + assert.Equal(t, true, schema["nullable"], "Pointer types should be nullable") + } + + // Check required field + requiredNameParam := paramMap["requiredName"] + assert.Equal(t, true, requiredNameParam["required"], "Fields with validate:required should be required") + + // Check omitempty field + omitEmptyParam := paramMap["omitEmpty"] + assert.Equal(t, false, omitEmptyParam["required"], "Fields with omitempty should be optional") +} From 9a82d34e84522abaaa68e70c0a47819881b1aa45 Mon Sep 17 00:00:00 2001 From: Jeremy Mouton Date: Wed, 22 Oct 2025 01:52:09 +0200 Subject: [PATCH 02/18] Feat: Enhance parameter validation logic for path and query parameters --- common.go | 54 +++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 51 insertions(+), 3 deletions(-) diff --git a/common.go b/common.go index 91f53a9..78992c5 100644 --- a/common.go +++ b/common.go @@ -282,6 +282,7 @@ func extractParametersFromStruct(inputType reflect.Type) []map[string]interface{ // Process path parameters if pathTag := field.Tag.Get("path"); pathTag != "" { + // Path parameters are always required regardless of type param := map[string]interface{}{ "name": pathTag, "in": "path", @@ -294,7 +295,7 @@ func extractParametersFromStruct(inputType reflect.Type) []map[string]interface{ // Process query parameters if queryTag := field.Tag.Get("query"); queryTag != "" { - required := isFieldRequired(field) + required := isQueryFieldRequired(field) param := map[string]interface{}{ "name": queryTag, "in": "query", @@ -322,20 +323,62 @@ func getFieldDescription(field reflect.StructField, defaultDesc string) string { return fmt.Sprintf("%s: %s", defaultDesc, field.Name) } -// isFieldRequired checks if a field is required based on validation tags +// isFieldRequired checks if a field is required based on validation tags and type func isFieldRequired(field reflect.StructField) bool { validateTag := field.Tag.Get("validate") + + // If it's a pointer type, it's optional by default (unless explicitly required) + if field.Type.Kind() == reflect.Ptr { + // For pointer types, only required if explicitly marked as required + return strings.Contains(validateTag, "required") + } + + // For non-pointer types, check validation tags if validateTag == "" { + // No validation tag: assume required for path params, optional for query params + // This will be handled by the caller based on parameter type + return false + } + + // If has omitempty, it's optional + if strings.Contains(validateTag, "omitempty") { return false } + + // Check for explicit required validation + return strings.Contains(validateTag, "required") +} - // Check for required validation +// isQueryFieldRequired checks if a query parameter field is required +// Query parameters have different logic than path parameters: +// - Pointer types (*string, *int, etc.) are optional by default +// - Non-pointer types are optional by default unless explicitly marked as required +// - Fields with "omitempty" are optional +// - Fields with "required" are required +func isQueryFieldRequired(field reflect.StructField) bool { + validateTag := field.Tag.Get("validate") + + // If it's a pointer type, it's optional by default (unless explicitly required) + if field.Type.Kind() == reflect.Ptr { + return strings.Contains(validateTag, "required") + } + + // For non-pointer types in query parameters: + // - If has omitempty, it's optional + if strings.Contains(validateTag, "omitempty") { + return false + } + + // Check for explicit required validation return strings.Contains(validateTag, "required") } // getSchemaForType returns OpenAPI schema for a Go type func getSchemaForType(t reflect.Type) map[string]interface{} { schema := make(map[string]interface{}) + + // Check if original type was a pointer (indicating nullable) + isPointer := t.Kind() == reflect.Ptr // Handle pointer types if t.Kind() == reflect.Ptr { @@ -371,6 +414,11 @@ func getSchemaForType(t reflect.Type) map[string]interface{} { default: schema["type"] = "string" } + + // If the original type was a pointer, indicate it's nullable + if isPointer { + schema["nullable"] = true + } return schema } From 8f2c9d803034b5123737f7159e3d7d71dc2508b2 Mon Sep 17 00:00:00 2001 From: Jeremy Mouton Date: Wed, 22 Oct 2025 01:52:15 +0200 Subject: [PATCH 03/18] Refactor: Clean up whitespace in validation functions for consistency --- common.go | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/common.go b/common.go index 78992c5..40efa7b 100644 --- a/common.go +++ b/common.go @@ -326,25 +326,25 @@ func getFieldDescription(field reflect.StructField, defaultDesc string) string { // isFieldRequired checks if a field is required based on validation tags and type func isFieldRequired(field reflect.StructField) bool { validateTag := field.Tag.Get("validate") - + // If it's a pointer type, it's optional by default (unless explicitly required) if field.Type.Kind() == reflect.Ptr { // For pointer types, only required if explicitly marked as required return strings.Contains(validateTag, "required") } - + // For non-pointer types, check validation tags if validateTag == "" { // No validation tag: assume required for path params, optional for query params // This will be handled by the caller based on parameter type return false } - + // If has omitempty, it's optional if strings.Contains(validateTag, "omitempty") { return false } - + // Check for explicit required validation return strings.Contains(validateTag, "required") } @@ -357,18 +357,18 @@ func isFieldRequired(field reflect.StructField) bool { // - Fields with "required" are required func isQueryFieldRequired(field reflect.StructField) bool { validateTag := field.Tag.Get("validate") - + // If it's a pointer type, it's optional by default (unless explicitly required) if field.Type.Kind() == reflect.Ptr { return strings.Contains(validateTag, "required") } - + // For non-pointer types in query parameters: // - If has omitempty, it's optional if strings.Contains(validateTag, "omitempty") { return false } - + // Check for explicit required validation return strings.Contains(validateTag, "required") } @@ -376,7 +376,7 @@ func isQueryFieldRequired(field reflect.StructField) bool { // getSchemaForType returns OpenAPI schema for a Go type func getSchemaForType(t reflect.Type) map[string]interface{} { schema := make(map[string]interface{}) - + // Check if original type was a pointer (indicating nullable) isPointer := t.Kind() == reflect.Ptr @@ -414,7 +414,7 @@ func getSchemaForType(t reflect.Type) map[string]interface{} { default: schema["type"] = "string" } - + // If the original type was a pointer, indicate it's nullable if isPointer { schema["nullable"] = true From cff7d8dffa1d7c8c103caa370badf4ede2063894 Mon Sep 17 00:00:00 2001 From: Jeremy Mouton Date: Wed, 22 Oct 2025 01:52:21 +0200 Subject: [PATCH 04/18] Feat: Add optional pointer parameters to SearchInput and update endpoint test example --- _examples/auto_params/main.go | 34 +++++++++++++++++++++------------- 1 file changed, 21 insertions(+), 13 deletions(-) diff --git a/_examples/auto_params/main.go b/_examples/auto_params/main.go index 3151dd7..fc89d9b 100644 --- a/_examples/auto_params/main.go +++ b/_examples/auto_params/main.go @@ -15,6 +15,11 @@ type SearchInput struct { Age int `query:"age" validate:"omitempty,min=0,max=120"` Active bool `query:"active"` MinPrice float64 `query:"minPrice" validate:"omitempty,min=0"` + + // Pointer types - automatically optional and nullable + Category *string `query:"category"` + MaxResults *int `query:"maxResults"` + IncludeInactive *bool `query:"includeInactive"` } type SearchOutput struct { @@ -23,12 +28,13 @@ type SearchOutput struct { } type User struct { - ID int `json:"id"` - Name string `json:"name"` - Email string `json:"email"` - Age int `json:"age"` - Active bool `json:"active"` - Price float64 `json:"price"` + ID int `json:"id"` + Name string `json:"name"` + Email string `json:"email"` + Age int `json:"age"` + Active bool `json:"active"` + Price float64 `json:"price"` + Category *string `json:"category,omitempty"` } type ErrorResponse struct { @@ -44,12 +50,13 @@ func main() { fiberoapi.Get(oapi, "/users/:name", func(c *fiber.Ctx, input SearchInput) (SearchOutput, ErrorResponse) { // Simulate search results user := User{ - ID: 1, - Name: input.Name, - Email: input.Email, - Age: input.Age, - Active: input.Active, - Price: input.MinPrice, + ID: 1, + Name: input.Name, + Email: input.Email, + Age: input.Age, + Active: input.Active, + Price: input.MinPrice, + Category: input.Category, // Pointer field - can be nil } return SearchOutput{ @@ -86,7 +93,8 @@ func main() { fmt.Println("\n📖 Documentation disponible sur http://localhost:3000/docs") fmt.Println("📊 Spec OpenAPI JSON sur http://localhost:3000/openapi.json") - fmt.Println("🧪 Test de l'endpoint : http://localhost:3000/users/john?email=john@example.com&age=25&active=true&minPrice=10.5") + fmt.Println("🧪 Test de l'endpoint : http://localhost:3000/users/john?email=john@example.com&age=25&active=true&minPrice=10.5&category=electronics&maxResults=10") + fmt.Println("🔧 Paramètres optionnels (pointeurs) : category, maxResults, includeInactive") log.Fatal(app.Listen(":3000")) } From e45e2c1da4a6147515b931b58026b64002e2ea3c Mon Sep 17 00:00:00 2001 From: Jeremy Mouton Date: Wed, 22 Oct 2025 01:52:28 +0200 Subject: [PATCH 05/18] Refactor: Adjust formatting of pointer parameters in SearchInput for consistency --- _examples/auto_params/main.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/_examples/auto_params/main.go b/_examples/auto_params/main.go index fc89d9b..709c00a 100644 --- a/_examples/auto_params/main.go +++ b/_examples/auto_params/main.go @@ -15,11 +15,11 @@ type SearchInput struct { Age int `query:"age" validate:"omitempty,min=0,max=120"` Active bool `query:"active"` MinPrice float64 `query:"minPrice" validate:"omitempty,min=0"` - + // Pointer types - automatically optional and nullable - Category *string `query:"category"` - MaxResults *int `query:"maxResults"` - IncludeInactive *bool `query:"includeInactive"` + Category *string `query:"category"` + MaxResults *int `query:"maxResults"` + IncludeInactive *bool `query:"includeInactive"` } type SearchOutput struct { From dee3f5d691315e374604b47c367b98c4dd772876 Mon Sep 17 00:00:00 2001 From: Jeremy Mouton Date: Wed, 22 Oct 2025 01:56:21 +0200 Subject: [PATCH 06/18] Refactor: Clarify comments on path and query parameter requirements in extractParametersFromStruct --- common.go | 31 ++++--------------------------- 1 file changed, 4 insertions(+), 27 deletions(-) diff --git a/common.go b/common.go index 40efa7b..579107a 100644 --- a/common.go +++ b/common.go @@ -282,7 +282,8 @@ func extractParametersFromStruct(inputType reflect.Type) []map[string]interface{ // Process path parameters if pathTag := field.Tag.Get("path"); pathTag != "" { - // Path parameters are always required regardless of type + // Path parameters are always required regardless of type or validation tags + // This follows OpenAPI 3.0 specification where path parameters must be required param := map[string]interface{}{ "name": pathTag, "in": "path", @@ -295,6 +296,7 @@ func extractParametersFromStruct(inputType reflect.Type) []map[string]interface{ // Process query parameters if queryTag := field.Tag.Get("query"); queryTag != "" { + // Query parameters use specialized logic based on type and validation tags required := isQueryFieldRequired(field) param := map[string]interface{}{ "name": queryTag, @@ -323,34 +325,9 @@ func getFieldDescription(field reflect.StructField, defaultDesc string) string { return fmt.Sprintf("%s: %s", defaultDesc, field.Name) } -// isFieldRequired checks if a field is required based on validation tags and type -func isFieldRequired(field reflect.StructField) bool { - validateTag := field.Tag.Get("validate") - - // If it's a pointer type, it's optional by default (unless explicitly required) - if field.Type.Kind() == reflect.Ptr { - // For pointer types, only required if explicitly marked as required - return strings.Contains(validateTag, "required") - } - - // For non-pointer types, check validation tags - if validateTag == "" { - // No validation tag: assume required for path params, optional for query params - // This will be handled by the caller based on parameter type - return false - } - - // If has omitempty, it's optional - if strings.Contains(validateTag, "omitempty") { - return false - } - - // Check for explicit required validation - return strings.Contains(validateTag, "required") -} - // isQueryFieldRequired checks if a query parameter field is required // Query parameters have different logic than path parameters: +// - Path parameters are always required (handled separately) // - Pointer types (*string, *int, etc.) are optional by default // - Non-pointer types are optional by default unless explicitly marked as required // - Fields with "omitempty" are optional From 36acc1dcdc1a4d783cfedefd1327e05764346d7c Mon Sep 17 00:00:00 2001 From: Jeremy Mouton Date: Wed, 22 Oct 2025 02:03:45 +0200 Subject: [PATCH 07/18] Refactor: Replace direct pointer type check with isPointerType function in validateResourceAccess --- auth.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/auth.go b/auth.go index fa11547..ab8a78f 100644 --- a/auth.go +++ b/auth.go @@ -176,7 +176,7 @@ func validateResourceAccess(c *fiber.Ctx, authCtx *AuthContext, input interface{ inputValue := reflect.ValueOf(input) inputType := reflect.TypeOf(input) - if inputType.Kind() == reflect.Ptr { + if isPointerType(inputType) { inputValue = inputValue.Elem() inputType = inputType.Elem() } From add7edf89bfc1fb33669ee9a21d690c7f5b72236 Mon Sep 17 00:00:00 2001 From: Jeremy Mouton Date: Wed, 22 Oct 2025 02:03:49 +0200 Subject: [PATCH 08/18] Refactor: Simplify pointer type handling with utility functions in common.go --- common.go | 32 +++++++++++++++++++++++--------- 1 file changed, 23 insertions(+), 9 deletions(-) diff --git a/common.go b/common.go index 579107a..6772d0a 100644 --- a/common.go +++ b/common.go @@ -196,7 +196,7 @@ func validatePathParams[T any](path string) error { inputType := reflect.TypeOf(zero) // If the type is a pointer, get the element type - if inputType != nil && inputType.Kind() == reflect.Ptr { + if inputType != nil && isPointerType(inputType) { inputType = inputType.Elem() } @@ -263,9 +263,7 @@ func extractParametersFromStruct(inputType reflect.Type) []map[string]interface{ } // Handle pointer types - if inputType.Kind() == reflect.Ptr { - inputType = inputType.Elem() - } + inputType = dereferenceType(inputType) // Only process struct types if inputType.Kind() != reflect.Struct { @@ -325,6 +323,24 @@ func getFieldDescription(field reflect.StructField, defaultDesc string) string { return fmt.Sprintf("%s: %s", defaultDesc, field.Name) } +// isPointerType checks if a reflect.Type is a pointer type +func isPointerType(t reflect.Type) bool { + return t.Kind() == reflect.Ptr +} + +// isPointerField checks if a reflect.StructField is a pointer type +func isPointerField(field reflect.StructField) bool { + return isPointerType(field.Type) +} + +// dereferenceType removes pointer indirection from a type +func dereferenceType(t reflect.Type) reflect.Type { + if isPointerType(t) { + return t.Elem() + } + return t +} + // isQueryFieldRequired checks if a query parameter field is required // Query parameters have different logic than path parameters: // - Path parameters are always required (handled separately) @@ -336,7 +352,7 @@ func isQueryFieldRequired(field reflect.StructField) bool { validateTag := field.Tag.Get("validate") // If it's a pointer type, it's optional by default (unless explicitly required) - if field.Type.Kind() == reflect.Ptr { + if isPointerField(field) { return strings.Contains(validateTag, "required") } @@ -355,12 +371,10 @@ func getSchemaForType(t reflect.Type) map[string]interface{} { schema := make(map[string]interface{}) // Check if original type was a pointer (indicating nullable) - isPointer := t.Kind() == reflect.Ptr + isPointer := isPointerType(t) // Handle pointer types - if t.Kind() == reflect.Ptr { - t = t.Elem() - } + t = dereferenceType(t) switch t.Kind() { case reflect.String: From 1bd4c8ba50ab3cf05795e9cc7ce21efbf3db86e1 Mon Sep 17 00:00:00 2001 From: Jeremy Mouton Date: Wed, 22 Oct 2025 02:03:54 +0200 Subject: [PATCH 09/18] Refactor: Replace direct pointer dereferencing with dereferenceType utility function --- fiberoapi.go | 48 +++++++++++------------------------------------- 1 file changed, 11 insertions(+), 37 deletions(-) diff --git a/fiberoapi.go b/fiberoapi.go index fa61bfd..d507dda 100644 --- a/fiberoapi.go +++ b/fiberoapi.go @@ -241,10 +241,7 @@ func (o *OApiApp) GenerateOpenAPISpec() map[string]interface{} { // Add request body schema for POST/PUT methods if op.Method == "POST" || op.Method == "PUT" || op.Method == "PATCH" { if op.InputType != nil { - inputType := op.InputType - if inputType.Kind() == reflect.Ptr { - inputType = inputType.Elem() - } + inputType := dereferenceType(op.InputType) var schemaRef map[string]interface{} @@ -274,10 +271,7 @@ func (o *OApiApp) GenerateOpenAPISpec() map[string]interface{} { // Success response (200) if op.OutputType != nil { - outputType := op.OutputType - if outputType.Kind() == reflect.Ptr { - outputType = outputType.Elem() - } + outputType := dereferenceType(op.OutputType) var schemaRef map[string]interface{} @@ -341,9 +335,7 @@ func collectAllTypes(t reflect.Type, collected map[string]reflect.Type) { } // Handle pointers - if t.Kind() == reflect.Ptr { - t = t.Elem() - } + t = dereferenceType(t) typeName := getTypeName(t) if typeName == "" { @@ -418,9 +410,7 @@ func shouldGenerateSchemaForType(t reflect.Type) bool { } // Handle pointers - if t.Kind() == reflect.Ptr { - t = t.Elem() - } + t = dereferenceType(t) switch t.Kind() { case reflect.Struct: @@ -428,19 +418,13 @@ func shouldGenerateSchemaForType(t reflect.Type) bool { return t.NumField() > 0 case reflect.Map: // Generate schema for maps with complex value types - valueType := t.Elem() - if valueType.Kind() == reflect.Ptr { - valueType = valueType.Elem() - } + valueType := dereferenceType(t.Elem()) return valueType.Kind() == reflect.Struct || valueType.Kind() == reflect.Map || valueType.Kind() == reflect.Slice case reflect.Slice: // Generate schema for slices of complex types - elemType := t.Elem() - if elemType.Kind() == reflect.Ptr { - elemType = elemType.Elem() - } + elemType := dereferenceType(t.Elem()) return elemType.Kind() == reflect.Struct || elemType.Kind() == reflect.Map case reflect.Interface: @@ -474,9 +458,7 @@ func getTypeName(t reflect.Type) string { } // Handle pointers - if t.Kind() == reflect.Ptr { - t = t.Elem() - } + t = dereferenceType(t) // Handle different kinds of types switch t.Kind() { @@ -517,9 +499,7 @@ func getSimpleTypeName(t reflect.Type) string { } // Handle pointers - if t.Kind() == reflect.Ptr { - t = t.Elem() - } + t = dereferenceType(t) switch t.Kind() { case reflect.String: @@ -557,9 +537,7 @@ func generateSchema(t reflect.Type) map[string]interface{} { } // Handle pointers - if t.Kind() == reflect.Ptr { - t = t.Elem() - } + t = dereferenceType(t) schema := make(map[string]interface{}) @@ -681,9 +659,7 @@ func generateFieldSchema(t reflect.Type) map[string]interface{} { schema := make(map[string]interface{}) // Handle pointers - if t.Kind() == reflect.Ptr { - t = t.Elem() - } + t = dereferenceType(t) switch t.Kind() { case reflect.String: @@ -806,9 +782,7 @@ func isEmptyStruct(t reflect.Type) bool { return true } - if t.Kind() == reflect.Ptr { - t = t.Elem() - } + t = dereferenceType(t) return t.Kind() == reflect.Struct && t.NumField() == 0 } From 757a9690ccc5ad09fba2eace97f9e82b0902e2ab Mon Sep 17 00:00:00 2001 From: Jeremy Mouton Date: Wed, 22 Oct 2025 02:10:27 +0200 Subject: [PATCH 10/18] Update common.go Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- common.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/common.go b/common.go index 6772d0a..8ffa886 100644 --- a/common.go +++ b/common.go @@ -280,8 +280,9 @@ func extractParametersFromStruct(inputType reflect.Type) []map[string]interface{ // Process path parameters if pathTag := field.Tag.Get("path"); pathTag != "" { - // Path parameters are always required regardless of type or validation tags - // This follows OpenAPI 3.0 specification where path parameters must be required + // Path parameters are always required regardless of type or validation tags. + // This follows OpenAPI 3.0 specification where path parameters must be required, + // and is enforced here by explicitly setting "required": true in the parameter map below. param := map[string]interface{}{ "name": pathTag, "in": "path", From 5baa306202c9ed233669e3827b68bc57399e5489 Mon Sep 17 00:00:00 2001 From: Jeremy Mouton Date: Wed, 22 Oct 2025 02:10:35 +0200 Subject: [PATCH 11/18] Update auto_params_test.go Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- auto_params_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/auto_params_test.go b/auto_params_test.go index 40dfe0d..dd6afce 100644 --- a/auto_params_test.go +++ b/auto_params_test.go @@ -281,7 +281,7 @@ func TestAutoParamsPointerTypesInline(t *testing.T) { // Check pointer type is optional and nullable optionalNameParam := paramMap["optionalName"] - assert.Equal(t, false, optionalNameParam["required"], "Pointer types should be optional by default") + assert.False(t, optionalNameParam["required"], "Pointer types should be optional by default") if schema, ok := optionalNameParam["schema"].(map[string]interface{}); ok { assert.Equal(t, true, schema["nullable"], "Pointer types should be nullable") } From 542aa4e8cc38aa291930c53f7b8dfb0c0c218d15 Mon Sep 17 00:00:00 2001 From: Jeremy Mouton Date: Wed, 22 Oct 2025 02:10:47 +0200 Subject: [PATCH 12/18] Update auto_params_test.go Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- auto_params_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/auto_params_test.go b/auto_params_test.go index dd6afce..dc91898 100644 --- a/auto_params_test.go +++ b/auto_params_test.go @@ -288,7 +288,7 @@ func TestAutoParamsPointerTypesInline(t *testing.T) { // Check required field requiredNameParam := paramMap["requiredName"] - assert.Equal(t, true, requiredNameParam["required"], "Fields with validate:required should be required") + assert.True(t, requiredNameParam["required"], "Fields with validate:required should be required") // Check omitempty field omitEmptyParam := paramMap["omitEmpty"] From 626bf5778c24a1de2c3a0eef7c86a5ac4e609dcd Mon Sep 17 00:00:00 2001 From: Jeremy Mouton Date: Wed, 22 Oct 2025 02:10:57 +0200 Subject: [PATCH 13/18] Update auto_params_test.go Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- auto_params_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/auto_params_test.go b/auto_params_test.go index dc91898..6cb3a3c 100644 --- a/auto_params_test.go +++ b/auto_params_test.go @@ -292,5 +292,5 @@ func TestAutoParamsPointerTypesInline(t *testing.T) { // Check omitempty field omitEmptyParam := paramMap["omitEmpty"] - assert.Equal(t, false, omitEmptyParam["required"], "Fields with omitempty should be optional") + assert.False(t, omitEmptyParam["required"], "Fields with omitempty should be optional") } From edcd0d5fac1d3a33a8af7903486e7c01d8bcb6c4 Mon Sep 17 00:00:00 2001 From: Jeremy Mouton Date: Wed, 22 Oct 2025 02:12:17 +0200 Subject: [PATCH 14/18] Update auto_params_test.go Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- auto_params_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/auto_params_test.go b/auto_params_test.go index 6cb3a3c..63780ce 100644 --- a/auto_params_test.go +++ b/auto_params_test.go @@ -283,7 +283,7 @@ func TestAutoParamsPointerTypesInline(t *testing.T) { optionalNameParam := paramMap["optionalName"] assert.False(t, optionalNameParam["required"], "Pointer types should be optional by default") if schema, ok := optionalNameParam["schema"].(map[string]interface{}); ok { - assert.Equal(t, true, schema["nullable"], "Pointer types should be nullable") + assert.True(t, schema["nullable"], "Pointer types should be nullable") } // Check required field From 60f12d7ef0ebf81b386e74f62a87b2020477aff2 Mon Sep 17 00:00:00 2001 From: Jeremy Mouton Date: Wed, 22 Oct 2025 02:12:26 +0200 Subject: [PATCH 15/18] Update common.go Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- common.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common.go b/common.go index 8ffa886..07a391d 100644 --- a/common.go +++ b/common.go @@ -282,7 +282,7 @@ func extractParametersFromStruct(inputType reflect.Type) []map[string]interface{ if pathTag := field.Tag.Get("path"); pathTag != "" { // Path parameters are always required regardless of type or validation tags. // This follows OpenAPI 3.0 specification where path parameters must be required, - // and is enforced here by explicitly setting "required": true in the parameter map below. + // and is enforced here by explicitly setting "required": true at line 289. param := map[string]interface{}{ "name": pathTag, "in": "path", From 6924d3fb2e12da491ad2d0512216e72d247e2a98 Mon Sep 17 00:00:00 2001 From: Jeremy Mouton Date: Wed, 22 Oct 2025 02:13:39 +0200 Subject: [PATCH 16/18] Refactor: Simplify pointer type handling in getSchemaForType function --- common.go | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/common.go b/common.go index 07a391d..f2dca6d 100644 --- a/common.go +++ b/common.go @@ -371,10 +371,8 @@ func isQueryFieldRequired(field reflect.StructField) bool { func getSchemaForType(t reflect.Type) map[string]interface{} { schema := make(map[string]interface{}) - // Check if original type was a pointer (indicating nullable) - isPointer := isPointerType(t) - - // Handle pointer types + // Handle pointer types - dereference for type checking + originalType := t t = dereferenceType(t) switch t.Kind() { @@ -408,7 +406,7 @@ func getSchemaForType(t reflect.Type) map[string]interface{} { } // If the original type was a pointer, indicate it's nullable - if isPointer { + if isPointerType(originalType) { schema["nullable"] = true } From d013f516e00f52d4adf239715f09c4450b11c3ec Mon Sep 17 00:00:00 2001 From: Jeremy Mouton Date: Wed, 22 Oct 2025 02:15:54 +0200 Subject: [PATCH 17/18] Fix: Ensure pointer type parameters are correctly validated as optional and nullable --- auto_params_test.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/auto_params_test.go b/auto_params_test.go index 63780ce..57912a5 100644 --- a/auto_params_test.go +++ b/auto_params_test.go @@ -281,16 +281,16 @@ func TestAutoParamsPointerTypesInline(t *testing.T) { // Check pointer type is optional and nullable optionalNameParam := paramMap["optionalName"] - assert.False(t, optionalNameParam["required"], "Pointer types should be optional by default") + assert.False(t, optionalNameParam["required"].(bool), "Pointer types should be optional by default") if schema, ok := optionalNameParam["schema"].(map[string]interface{}); ok { - assert.True(t, schema["nullable"], "Pointer types should be nullable") + assert.True(t, schema["nullable"].(bool), "Pointer types should be nullable") } // Check required field requiredNameParam := paramMap["requiredName"] - assert.True(t, requiredNameParam["required"], "Fields with validate:required should be required") + assert.True(t, requiredNameParam["required"].(bool), "Fields with validate:required should be required") // Check omitempty field omitEmptyParam := paramMap["omitEmpty"] - assert.False(t, omitEmptyParam["required"], "Fields with omitempty should be optional") + assert.False(t, omitEmptyParam["required"].(bool), "Fields with omitempty should be optional") } From 81902886babb1790ad4206f82db7d285781465bc Mon Sep 17 00:00:00 2001 From: Jeremy Mouton Date: Wed, 22 Oct 2025 02:18:20 +0200 Subject: [PATCH 18/18] Update common.go Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- common.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common.go b/common.go index f2dca6d..900ae40 100644 --- a/common.go +++ b/common.go @@ -371,7 +371,7 @@ func isQueryFieldRequired(field reflect.StructField) bool { func getSchemaForType(t reflect.Type) map[string]interface{} { schema := make(map[string]interface{}) - // Handle pointer types - dereference for type checking + // Handle pointer types - preserve original to detect nullability, then dereference for type checking originalType := t t = dereferenceType(t)