@@ -31,6 +31,7 @@ import (
3131 "strings"
3232
3333 "github.com/santhosh-tekuri/jsonschema/v6"
34+ "github.com/santhosh-tekuri/jsonschema/v6/kind"
3435 "sigs.k8s.io/yaml"
3536)
3637
@@ -208,7 +209,7 @@ func validateConfigWithSchema(configBytes []byte, schema map[string]any, install
208209 return fmt .Errorf ("value must be a string" )
209210 }
210211 if str != installNamespace {
211- return fmt .Errorf ("invalid value %q: watchNamespace must be %q (the namespace where the operator is installed) because this operator only supports OwnNamespace install mode" , str , installNamespace )
212+ return fmt .Errorf ("invalid value %q: must be %q (the namespace where the operator is installed) because this operator only supports OwnNamespace install mode" , str , installNamespace )
212213 }
213214 return nil
214215 },
@@ -228,7 +229,7 @@ func validateConfigWithSchema(configBytes []byte, schema map[string]any, install
228229 return fmt .Errorf ("value must be a string" )
229230 }
230231 if str == installNamespace {
231- return fmt .Errorf ("invalid value %q: watchNamespace must be different from %q (the install namespace) because this operator uses SingleNamespace install mode to watch a different namespace" , str , installNamespace )
232+ return fmt .Errorf ("invalid value %q: must be different from %q (the install namespace) because this operator uses SingleNamespace install mode to watch a different namespace" , str , installNamespace )
232233 }
233234 return nil
234235 },
@@ -294,25 +295,29 @@ func formatSchemaError(err error) error {
294295
295296// formatSingleError formats a single validation error from the schema library.
296297func formatSingleError (errUnit jsonschema.OutputUnit ) string {
298+ if errUnit .Error == nil {
299+ return ""
300+ }
301+
297302 // Check the keyword location to identify the error type
298- switch {
299- case strings . Contains ( errUnit . KeywordLocation , "/required" ) :
303+ switch errKind := errUnit . Error . Kind .( type ) {
304+ case * kind. Required :
300305 // Missing required field
301306 fieldName := extractFieldNameFromMessage (errUnit .Error )
302307 if fieldName != "" {
303308 return fmt .Sprintf ("required field %q is missing" , fieldName )
304309 }
305310 return "required field is missing"
306311
307- case strings . Contains ( errUnit . KeywordLocation , "/additionalProperties" ) :
312+ case * kind. AdditionalProperties :
308313 // Unknown/additional field
309314 fieldName := extractFieldNameFromMessage (errUnit .Error )
310315 if fieldName != "" {
311316 return fmt .Sprintf ("unknown field %q" , fieldName )
312317 }
313318 return "unknown field"
314319
315- case strings . Contains ( errUnit . KeywordLocation , "/type" ) :
320+ case * kind. Type :
316321 // Type mismatch (e.g., got null, want string)
317322 fieldPath := buildFieldPath (errUnit .InstanceLocation )
318323 if fieldPath != "" {
@@ -324,16 +329,14 @@ func formatSingleError(errUnit jsonschema.OutputUnit) string {
324329 }
325330 return fmt .Sprintf ("invalid type: %s" , errUnit .Error .String ())
326331
327- case strings .Contains (errUnit .KeywordLocation , "/format" ):
328- // Custom format validation (e.g., OwnNamespace, SingleNamespace constraints)
329- // These already have good error messages from our custom validators
330- if errUnit .Error != nil {
331- return errUnit .Error .String ()
332- }
332+ case * kind.Format :
333333 fieldPath := buildFieldPath (errUnit .InstanceLocation )
334- return fmt .Sprintf ("invalid format for field %q" , fieldPath )
334+ if fieldPath != "" {
335+ return fmt .Sprintf ("invalid format for field %q: %s" , fieldPath , errUnit .Error .String ())
336+ }
337+ return fmt .Sprintf ("invalid format: %s" , errUnit .Error .String ())
335338
336- case strings . Contains ( errUnit . KeywordLocation , "/anyOf" ) :
339+ case * kind. AnyOf :
337340 // anyOf validation failed - could be null or wrong type
338341 // This happens when a field accepts [null, string] but got something else
339342 fieldPath := buildFieldPath (errUnit .InstanceLocation )
@@ -342,13 +345,31 @@ func formatSingleError(errUnit jsonschema.OutputUnit) string {
342345 }
343346 return "invalid value"
344347
348+ case * kind.MaxLength :
349+ fieldPath := buildFieldPath (errUnit .InstanceLocation )
350+ if fieldPath != "" {
351+ return fmt .Sprintf ("field %q must have maximum length of %d (len=%d)" , fieldPath , errKind .Want , errKind .Got )
352+ }
353+ return errUnit .Error .String ()
354+
355+ case * kind.MinLength :
356+ fieldPath := buildFieldPath (errUnit .InstanceLocation )
357+ if fieldPath != "" {
358+ return fmt .Sprintf ("field %q must have minimum length of %d (len=%d)" , fieldPath , errKind .Want , errKind .Got )
359+ }
360+ return errUnit .Error .String ()
361+
362+ case * kind.Pattern :
363+ fieldPath := buildFieldPath (errUnit .InstanceLocation )
364+ if fieldPath != "" {
365+ return fmt .Sprintf ("field %q must match pattern %q" , fieldPath , errKind .Want )
366+ }
367+ return errUnit .Error .String ()
368+
345369 default :
346- // Unknown error type - return the library's error message
370+ // Unhandled error type - return the library's error message
347371 // This serves as a fallback for future schema features we haven't customized yet
348- if errUnit .Error != nil {
349- return errUnit .Error .String ()
350- }
351- return ""
372+ return errUnit .Error .String ()
352373 }
353374}
354375
0 commit comments