Skip to content

fix(validation): preserve wildcard slice shape, nil traversal, and message priority#1417

Merged
hwbrzzl merged 3 commits into
masterfrom
bowen/fix-validation
Mar 22, 2026
Merged

fix(validation): preserve wildcard slice shape, nil traversal, and message priority#1417
hwbrzzl merged 3 commits into
masterfrom
bowen/fix-validation

Conversation

@hwbrzzl
Copy link
Copy Markdown
Contributor

@hwbrzzl hwbrzzl commented Mar 20, 2026

Summary

  • Validated() for wildcard fields now returns the original slice shape ([]any, []int, etc.) instead of a string-keyed map, converting back to the typed source slice when safe.
  • Dot-path traversal and wildcard expansion no longer panic on nil intermediates or typed slices; reflection-based traversal handles any slice element type.
  • Explicit error message overrides now take priority over custom rule Message() defaults; integer, ucFirst, and lcFirst filter aliases are added.

Relate goravel/example#112

Why

Before this fix, validating any slice field with a wildcard rule (e.g. scores.*) caused Validated() to return map[string]any{"0": 1, "1": 2} instead of the original slice. Traversal through nil values or typed slices like []int also panicked during wildcard expansion.

// Before: data["scores"] was map[string]any{"0": 1, "1": 2}
// After:  data["scores"] is []int{1, 2}
validator, _ := validation.Make(ctx,
    map[string]any{"scores": []int{1, 2}},
    map[string]any{"scores.*": "required|integer"},
)
data := validator.Validated()
scores := data["scores"].([]int) // now works correctly

Custom rule Message() previously overrode any user-supplied message unconditionally, making per-field or per-rule message overrides impossible via WithMessages. The priority order is now: explicit field.rule message → explicit rule message → custom rule Message() default.

// Before: the custom rule's Message() was always used, ignoring WithMessages
// After:  the explicit WithMessages override takes priority
validator, _ := validation.Make(ctx,
    map[string]any{"user": nil},
    map[string]any{"user.name": "required"},
    validation.WithMessages(map[string]string{
        "user.name.required": "user.name is required",
    }),
)
errors := validator.Errors().Get("user.name")
// errors["required"] == "user.name is required"

@hwbrzzl hwbrzzl requested a review from a team as a code owner March 20, 2026 14:44
@hwbrzzl
Copy link
Copy Markdown
Contributor Author

hwbrzzl commented Mar 20, 2026

Context for reviewers: the core behavioral change is Validated()/ValidatedData() now reconstructs wildcard paths using slice containers (including typed slices when safe), instead of map-like index keys. Existing non-wildcard and map-key behavior is preserved and covered by tests.

@hwbrzzl hwbrzzl marked this pull request as draft March 20, 2026 14:46
@hwbrzzl hwbrzzl changed the title fix(validation): preserve validated wildcard output shape fix: preserve validated wildcard output shape Mar 20, 2026
@hwbrzzl hwbrzzl marked this pull request as ready for review March 20, 2026 15:17
@hwbrzzl
Copy link
Copy Markdown
Contributor Author

hwbrzzl commented Mar 20, 2026

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Mar 20, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@codecov
Copy link
Copy Markdown

codecov Bot commented Mar 20, 2026

Codecov Report

❌ Patch coverage is 87.56477% with 24 lines in your changes missing coverage. Please review.
✅ Project coverage is 67.98%. Comparing base (7cd523f) to head (d61a6cc).
⚠️ Report is 1 commits behind head on master.

Files with missing lines Patch % Lines
validation/filters.go 0.00% 15 Missing ⚠️
validation/utils.go 94.73% 5 Missing and 4 partials ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master    #1417      +/-   ##
==========================================
+ Coverage   67.82%   67.98%   +0.16%     
==========================================
  Files         354      354              
  Lines       27345    27521     +176     
==========================================
+ Hits        18546    18710     +164     
- Misses       7952     7963      +11     
- Partials      847      848       +1     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Mar 20, 2026

📝 Walkthrough

Walkthrough

The changes enhance validated data handling to preserve original data types when expanding wildcard rules. ValidatedData() now populates results using shape-aware utilities and normalizes output through normalizeValidatedShape(). Reflection-based indexing in dotGet and collectKeys supports flexible slice/array handling. New utilities enable setting nested values while maintaining container shapes derived from source data.

Changes

Cohort / File(s) Summary
Core validation engine
validation/engine.go, validation/utils.go
Modified ValidatedData() to use setValidated() and normalizeValidatedShape() for shape-preserving output. Expanded dotGet and collectKeys with reflection-based slice/array indexing. Added setValidated, normalizeValidatedShape, and helper utilities to preserve source data types in validated results (e.g., typed []int instead of []any).
Engine tests
validation/engine_test.go
Added TestEngine_ExpandWildcardRules_TypedSlice verifying wildcard rule expansion on typed slices, and tests asserting ValidatedData() reconstructs typed array fields and handles sparse wildcard indices with nil gaps.
Validation tests
validation/rules_test.go, validation/utils_test.go, validation/validation_test.go
Updated TestValidatedDataWithWildcard with scenarios covering []any, typed []int, and nested objects. Added comprehensive tests for setValidated, slice-to-type conversion, string parsing helpers (strToInts, strToArray), map-key rules, and wildcard validation across different slice types.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Poem

🐰 Wildcards bloom like clover in the validation field,
Typed slices dance where shapes are sealed,
No more casting chaos, just pristine arrays preserved,
The rabbits rejoice—each type gets what it deserved! 🎉

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 14.71% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title concisely captures the main behavioral change: preserving slice shape for wildcard-validated data, handling nil traversal, and managing message priority in validation output.
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
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch bowen/fix-validation

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@validation/utils.go`:
- Around line 429-438: collectKeys currently calls reflect.ValueOf(v) without
guarding for nil, which panics for nullable leaves (e.g., map[string]any{"name":
nil} or []any{nil}); fix by checking for v == nil (or
reflect.ValueOf(v).IsValid() / rv.IsNil() when applicable) before using
rv.Kind() in collectKeys so nil leaves are treated as terminal values and not
recursed into, and add regression tests exercising DataBag.Keys()/collectKeys
with map[string]any{"name": nil} and []any{nil} to ensure no panic.
- Around line 117-123: dotGet currently calls reflect.ValueOf(data).Kind()
without guarding for nil/invalid values which can panic when an intermediate is
nil; update dotGet to first check for nil/invalid reflect.Value (e.g. rv :=
reflect.ValueOf(data); if !rv.IsValid() { return nil, false }) and also handle
nil interfaces/pointers by checking rv.IsNil() for kinds Interface/Ptr and
returning (nil, false) before calling Kind(), optionally unwrapping rv =
rv.Elem() for Interface/Ptr so subsequent logic (the slice/array branch) is
safe; refer to dotGet and the rv variable in utils.go when applying this change.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 89f86b12-ee26-4e8c-95a4-4067153e52f6

📥 Commits

Reviewing files that changed from the base of the PR and between 7cd523f and c716996.

📒 Files selected for processing (6)
  • validation/engine.go
  • validation/engine_test.go
  • validation/rules_test.go
  • validation/utils.go
  • validation/utils_test.go
  • validation/validation_test.go

Comment thread validation/utils.go
Comment thread validation/utils.go
@hwbrzzl hwbrzzl marked this pull request as draft March 20, 2026 15:38
@hwbrzzl hwbrzzl changed the title fix: preserve validated wildcard output shape fix(validation): normalize wildcard validated output and message precedence Mar 22, 2026
@hwbrzzl hwbrzzl changed the title fix(validation): normalize wildcard validated output and message precedence fix(validation): preserve wildcard output shape and message override priority Mar 22, 2026
@hwbrzzl hwbrzzl changed the title fix(validation): preserve wildcard output shape and message override priority fix(validation): harden wildcard traversal and preserve validated slice shape Mar 22, 2026
@hwbrzzl hwbrzzl changed the title fix(validation): harden wildcard traversal and preserve validated slice shape fix(validation): preserve wildcard shape and prioritize explicit rule messages Mar 22, 2026
@hwbrzzl hwbrzzl changed the title fix(validation): preserve wildcard shape and prioritize explicit rule messages fix(validation): preserve wildcard output shape and nil-safe traversal Mar 22, 2026
@hwbrzzl hwbrzzl changed the title fix(validation): preserve wildcard output shape and nil-safe traversal fix(validation): preserve wildcard slice shape, nil traversal, and message priority Mar 22, 2026
@hwbrzzl hwbrzzl marked this pull request as ready for review March 22, 2026 07:22
Copy link
Copy Markdown
Contributor

@h2zi h2zi left a comment

Choose a reason for hiding this comment

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

lgtm

@hwbrzzl hwbrzzl merged commit c877cd3 into master Mar 22, 2026
18 checks passed
@hwbrzzl hwbrzzl deleted the bowen/fix-validation branch March 22, 2026 13:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants