mirror of
https://github.com/opencloud-eu/opencloud.git
synced 2026-09-12 13:49:13 -04:00
fix(search): validate CaseInsensitive against the effective field type
The guard only rejected CaseInsensitive when a non-keyword/path Type was set explicitly. With no Type, isCasedType treated the field as cased, so CaseInsensitive on an inferred numeric/bool/datetime field passed validation but produced no _lowercase sibling, and the query would silently match nothing. Validate now falls back to the inferred Go type.
This commit is contained in:
1 parent
c94059fb85
commit
fc41995a47
2 files changed
+48
-9
No files matched your search
@@ -16,17 +16,20 @@ func Validate(t reflect.Type, overrides map[string]FieldOpts) error {
|
||||
if len(overrides) == 0 {
|
||||
return nil
|
||||
}
|
||||
names := collectNames(t, "")
|
||||
fields := collectFields(t, "")
|
||||
var unknown, miscased []string
|
||||
for k, opts := range overrides {
|
||||
if _, ok := names[k]; !ok {
|
||||
goType, ok := fields[k]
|
||||
if !ok {
|
||||
unknown = append(unknown, k)
|
||||
continue
|
||||
}
|
||||
// CaseInsensitive routes queries to a <field>_lowercase sibling, which is
|
||||
// only generated for keyword/path fields; on any other type the query
|
||||
// would target a non-existent field and silently match nothing.
|
||||
if opts.caseInsensitive() && !isCasedType(opts) {
|
||||
// would target a non-existent field and silently match nothing. Use the
|
||||
// effective type (override, else the inferred Go type), since an override
|
||||
// with no explicit Type still infers keyword/numeric/... from the field.
|
||||
if opts.caseInsensitive() && !effectivelyCased(opts, goType) {
|
||||
miscased = append(miscased, k)
|
||||
}
|
||||
}
|
||||
@@ -41,17 +44,29 @@ func Validate(t reflect.Type, overrides map[string]FieldOpts) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func collectNames(t reflect.Type, prefix string) map[string]struct{} {
|
||||
out := map[string]struct{}{}
|
||||
// effectivelyCased reports whether a field is keyword/path (the only types that
|
||||
// get a _lowercase sibling), from the override type or the inferred Go type.
|
||||
func effectivelyCased(opts FieldOpts, goType reflect.Type) bool {
|
||||
eff := opts.Type
|
||||
if eff == "" && goType != nil {
|
||||
eff = inferType(goType)
|
||||
}
|
||||
return eff == TypeKeyword || eff == TypePath
|
||||
}
|
||||
|
||||
// collectFields maps every known field name (nested as "parent.child") to its Go
|
||||
// type. Embedded structs are flattened, matching encoding/json.
|
||||
func collectFields(t reflect.Type, prefix string) map[string]reflect.Type {
|
||||
out := map[string]reflect.Type{}
|
||||
_ = walkFields(t, func(fi fieldInfo) error {
|
||||
key := fi.Name
|
||||
if prefix != "" {
|
||||
key = prefix + "." + fi.Name
|
||||
}
|
||||
out[key] = struct{}{}
|
||||
out[key] = fi.GoField.Type
|
||||
if sub := structType(fi.GoField.Type); sub != nil {
|
||||
for k := range collectNames(sub, key) {
|
||||
out[k] = struct{}{}
|
||||
for k, v := range collectFields(sub, key) {
|
||||
out[k] = v
|
||||
}
|
||||
}
|
||||
return nil
|
||||
|
||||
@@ -54,4 +54,28 @@ var _ = Describe("Validate", func() {
|
||||
Expect(err.Error()).To(ContainSubstring("Name"))
|
||||
Expect(err.Error()).To(ContainSubstring("CaseInsensitive"))
|
||||
})
|
||||
|
||||
It("rejects CaseInsensitive on an inferred non-keyword field (empty Type)", func() {
|
||||
True := true
|
||||
type doc struct {
|
||||
Size uint64 `json:"Size"`
|
||||
}
|
||||
err := Validate(reflect.TypeFor[doc](), map[string]FieldOpts{
|
||||
"Size": {CaseInsensitive: &True}, // no explicit Type -> inferred numeric
|
||||
})
|
||||
Expect(err).To(HaveOccurred())
|
||||
Expect(err.Error()).To(ContainSubstring("Size"))
|
||||
})
|
||||
|
||||
It("accepts CaseInsensitive on an inferred keyword field (empty Type)", func() {
|
||||
True := true
|
||||
type doc struct {
|
||||
Name string `json:"Name"`
|
||||
Tags []string `json:"Tags"`
|
||||
}
|
||||
Expect(Validate(reflect.TypeFor[doc](), map[string]FieldOpts{
|
||||
"Name": {CaseInsensitive: &True},
|
||||
"Tags": {CaseInsensitive: &True},
|
||||
})).To(Succeed())
|
||||
})
|
||||
})
|
||||
Reference in new issue
Block a user