@@ -48,7 +48,7 @@ func (es ExamplesSchema) RunRule(_ []*yaml.Node, ruleContext model.RuleFunctionC
4848 return results
4949 }
5050
51- // Get configuration values from context, use defaults if not set
51+ // get configuration values from context, use defaults if not set
5252 maxConcurrentValidations := ruleContext .MaxConcurrentValidations
5353 if maxConcurrentValidations <= 0 {
5454 maxConcurrentValidations = 10 // Default: 10 parallel validations
@@ -59,19 +59,19 @@ func (es ExamplesSchema) RunRule(_ []*yaml.Node, ruleContext model.RuleFunctionC
5959 validationTimeout = 10 * time .Second // Default: 10 seconds
6060 }
6161
62- // Create a timeout context for the entire validation process
62+ // create a timeout context for the entire validation process
6363 ctx , cancel := context .WithTimeout (context .Background (), validationTimeout )
6464 defer cancel ()
6565
66- // Create semaphore for concurrency limiting
66+ // create semaphore for concurrency limiting
6767 sem := make (chan struct {}, maxConcurrentValidations )
6868
69- // Track active workers
69+ // track active workers
7070 var activeWorkers int32
7171 var completedWorkers int32
7272
7373 buildResult := func (message , path string , key , node * yaml.Node , component v3.AcceptsRuleResults ) model.RuleFunctionResult {
74- // Try to find all paths for this node if it's a schema
74+ // try to find all paths for this node if it's a schema
7575 var allPaths []string
7676 if schema , ok := component .(* v3.Schema ); ok {
7777 _ , allPaths = vacuumUtils .LocateSchemaPropertyPaths (ruleContext , schema , key , node )
@@ -85,7 +85,7 @@ func (es ExamplesSchema) RunRule(_ []*yaml.Node, ruleContext model.RuleFunctionC
8585 Rule : ruleContext .Rule ,
8686 }
8787
88- // Set the Paths array if we found multiple locations
88+ // set the Paths array if we found multiple locations
8989 if len (allPaths ) > 1 {
9090 result .Paths = allPaths
9191 }
@@ -97,9 +97,9 @@ func (es ExamplesSchema) RunRule(_ []*yaml.Node, ruleContext model.RuleFunctionC
9797 var expLock sync.Mutex
9898 var wg sync.WaitGroup
9999
100- // Helper function to spawn workers with context and concurrency control
100+ // helper function to spawn workers with context and concurrency control
101101 spawnWorker := func (work func ()) {
102- // Check if context is already cancelled before spawning
102+ // check if context is already cancelled before spawning
103103 select {
104104 case <- ctx .Done ():
105105 return
@@ -114,26 +114,26 @@ func (es ExamplesSchema) RunRule(_ []*yaml.Node, ruleContext model.RuleFunctionC
114114 defer atomic .AddInt32 (& completedWorkers , 1 )
115115 defer atomic .AddInt32 (& activeWorkers , - 1 )
116116
117- // Recover from panics to prevent crashes
117+ // recover from panics to prevent crashes
118118 defer func () {
119119 if r := recover (); r != nil {
120- // Log panic if logger available
120+ // log panic if logger available
121121 if ruleContext .Logger != nil {
122122 ruleContext .Logger .Error ("ExamplesSchema validation panic" , "error" , r )
123123 }
124124 }
125125 }()
126126
127- // Try to acquire semaphore with context
127+ // try to acquire semaphore with context
128128 select {
129129 case sem <- struct {}{}:
130130 defer func () { <- sem }()
131131 case <- ctx .Done ():
132- // Context cancelled while waiting for semaphore
132+ // context cancelled while waiting for semaphore
133133 return
134134 }
135135
136- // Check context again before starting work
136+ // check context again before starting work
137137 select {
138138 case <- ctx .Done ():
139139 return
@@ -199,7 +199,7 @@ func (es ExamplesSchema) RunRule(_ []*yaml.Node, ruleContext model.RuleFunctionC
199199 for i := range ruleContext .DrDocument .Schemas {
200200 s := ruleContext .DrDocument .Schemas [i ]
201201 spawnWorker (func () {
202- // Check context at start of work
202+ // check context at start of work
203203 select {
204204 case <- ctx .Done ():
205205 return
@@ -208,7 +208,7 @@ func (es ExamplesSchema) RunRule(_ []*yaml.Node, ruleContext model.RuleFunctionC
208208
209209 if s .Value .Examples != nil {
210210 for x , ex := range s .Value .Examples {
211- // Check context in loop
211+ // check context in loop
212212 select {
213213 case <- ctx .Done ():
214214 return
@@ -269,38 +269,100 @@ func (es ExamplesSchema) RunRule(_ []*yaml.Node, ruleContext model.RuleFunctionC
269269 }
270270 }
271271
272+ // exampleValidatorFunc defines the function signature for validating examples
273+ type exampleValidatorFunc func (example any ) (bool , []* errors.ValidationError )
274+
275+ // processValidationErrors converts validation errors to rule function results
276+ processValidationErrors := func (
277+ validationErrors []* errors.ValidationError ,
278+ path string ,
279+ keyNode , valueNode * yaml.Node ,
280+ schema * v3.Schema ,
281+ ) []model.RuleFunctionResult {
282+ var rx []model.RuleFunctionResult
283+ for _ , r := range validationErrors {
284+ for _ , err := range r .SchemaValidationErrors {
285+ result := buildResult (
286+ vacuumUtils .SuppliedOrDefault (ruleContext .Rule .Message , err .Reason ),
287+ path , keyNode , valueNode , schema )
288+
289+ // check if this is a banned error
290+ banned := false
291+ for g := range bannedErrors {
292+ if strings .Contains (err .Reason , bannedErrors [g ]) {
293+ banned = true
294+ break
295+ }
296+ }
297+ if ! banned {
298+ rx = append (rx , result )
299+ }
300+ }
301+ }
302+ return rx
303+ }
304+
305+ // createJSONValidator creates a validator for JSON examples
306+ createJSONValidator := func (
307+ iKey * int ,
308+ sKey , label string ,
309+ s * v3.Schema ,
310+ obj v3.AcceptsRuleResults ,
311+ node * yaml.Node ,
312+ keyNode * yaml.Node ,
313+ ) exampleValidatorFunc {
314+ return func (example any ) (bool , []* errors.ValidationError ) {
315+ return validateSchema (iKey , sKey , label , s , obj , node , keyNode , example )
316+ }
317+ }
318+
319+ // createXMLValidator creates a validator for XML examples
320+ createXMLValidator := func (s * v3.Schema , ver float32 ) exampleValidatorFunc {
321+ return func (example any ) (bool , []* errors.ValidationError ) {
322+ if xmlStr , ok := example .(string ); ok {
323+ if ver > 0 {
324+ return validator .ValidateXMLStringWithVersion (s .Value , xmlStr , ver )
325+ }
326+ return validator .ValidateXMLString (s .Value , xmlStr )
327+ }
328+ return true , nil
329+ }
330+ }
331+
272332 parseExamples := func (s * v3.Schema ,
273333 obj v3.AcceptsRuleResults ,
274- examples * orderedmap.Map [string ,
275- * v3Base. Example ] ) []model.RuleFunctionResult {
334+ examples * orderedmap.Map [string , * v3Base. Example ],
335+ validatorFunc exampleValidatorFunc ) []model.RuleFunctionResult {
276336
277337 var rx []model.RuleFunctionResult
278338 for examplesPairs := examples .First (); examplesPairs != nil ; examplesPairs = examplesPairs .Next () {
279-
280339 example := examplesPairs .Value ()
281340 exampleKey := examplesPairs .Key ()
282341
283342 var ex any
284343 if example .Value != nil {
285344 _ = example .Value .Decode (& ex )
286- result := validateSchema (nil , exampleKey , "examples" , s , obj , example .Value , example .GoLow ().KeyNode , ex )
287- if result != nil {
288- rx = append (rx , result ... )
345+ valid , validationErrors := validatorFunc (ex )
346+
347+ if ! valid {
348+ path := fmt .Sprintf ("%s.examples['%s']" , obj .(v3.Foundational ).GenerateJSONPath (), exampleKey )
349+ rx = append (rx , processValidationErrors (validationErrors , path ,
350+ example .GoLow ().KeyNode , example .Value , s )... )
289351 }
290352 }
291353 }
292354 return rx
293355 }
294356
295- parseExample := func (s * v3.Schema , node , key * yaml.Node ) []model.RuleFunctionResult {
296-
357+ parseExample := func (s * v3.Schema , node , key * yaml.Node , validatorFunc exampleValidatorFunc ) []model.RuleFunctionResult {
297358 var rx []model.RuleFunctionResult
298359 var ex any
299360 _ = node .Decode (& ex )
300361
301- result := validateSchema (nil , "" , "example" , s , s , node , key , ex )
302- if result != nil {
303- rx = append (rx , result ... )
362+ valid , validationErrors := validatorFunc (ex )
363+ if ! valid {
364+ path := ""
365+ rx = append (rx , processValidationErrors (validationErrors , path , key , node , s )... )
304366 }
305367 return rx
306368 }
@@ -309,25 +371,27 @@ func (es ExamplesSchema) RunRule(_ []*yaml.Node, ruleContext model.RuleFunctionC
309371 for i := range ruleContext .DrDocument .Parameters {
310372 p := ruleContext .DrDocument .Parameters [i ]
311373 spawnWorker (func () {
312- // Check context at start of work
374+ // check context at start of work
313375 select {
314376 case <- ctx .Done ():
315377 return
316378 default :
317379 }
318380
319381 if p .Value .Examples .Len () >= 1 && p .SchemaProxy != nil {
382+ jsonValidator := createJSONValidator (nil , "" , "examples" , p .SchemaProxy .Schema , p , nil , nil )
320383 expLock .Lock ()
321384 if p .Value .Examples != nil && p .Value .Examples .Len () > 0 {
322- results = append (results , parseExamples (p .SchemaProxy .Schema , p , p .Value .Examples )... )
385+ results = append (results , parseExamples (p .SchemaProxy .Schema , p , p .Value .Examples , jsonValidator )... )
323386 }
324387 expLock .Unlock ()
325388 } else {
326389 if p .Value .Example != nil && p .SchemaProxy != nil {
390+ jsonValidator := createJSONValidator (nil , "" , "example" , p .SchemaProxy .Schema , p , p .Value .Example , p .Value .GoLow ().Example .GetKeyNode ())
327391 expLock .Lock ()
328392 if p .Value .Examples != nil && p .Value .Examples .Len () > 0 {
329393 results = append (results , parseExample (p .SchemaProxy .Schema , p .Value .Example ,
330- p .Value .GoLow ().Example .GetKeyNode ())... )
394+ p .Value .GoLow ().Example .GetKeyNode (), jsonValidator )... )
331395 }
332396 expLock .Unlock ()
333397 }
@@ -340,22 +404,24 @@ func (es ExamplesSchema) RunRule(_ []*yaml.Node, ruleContext model.RuleFunctionC
340404 for i := range ruleContext .DrDocument .Headers {
341405 h := ruleContext .DrDocument .Headers [i ]
342406 spawnWorker (func () {
343- // Check context at start of work
407+ // check context at start of work
344408 select {
345409 case <- ctx .Done ():
346410 return
347411 default :
348412 }
349413
350414 if h .Value .Examples .Len () >= 1 && h .Schema != nil {
415+ jsonValidator := createJSONValidator (nil , "" , "examples" , h .Schema .Schema , h , nil , nil )
351416 expLock .Lock ()
352- results = append (results , parseExamples (h .Schema .Schema , h , h .Value .Examples )... )
417+ results = append (results , parseExamples (h .Schema .Schema , h , h .Value .Examples , jsonValidator )... )
353418 expLock .Unlock ()
354419 } else {
355420 if h .Value .Example != nil && h .Schema != nil {
421+ jsonValidator := createJSONValidator (nil , "" , "example" , h .Schema .Schema , h , h .Value .Example , h .Value .GoLow ().Example .GetKeyNode ())
356422 expLock .Lock ()
357423 results = append (results , parseExample (h .Schema .Schema , h .Value .Example ,
358- h .Value .GoLow ().Example .GetKeyNode ())... )
424+ h .Value .GoLow ().Example .GetKeyNode (), jsonValidator )... )
359425 expLock .Unlock ()
360426 }
361427 }
@@ -368,22 +434,38 @@ func (es ExamplesSchema) RunRule(_ []*yaml.Node, ruleContext model.RuleFunctionC
368434 for i := range ruleContext .DrDocument .MediaTypes {
369435 mt := ruleContext .DrDocument .MediaTypes [i ]
370436 spawnWorker (func () {
371- // Check context at start of work
437+ // check context at start of work
372438 select {
373439 case <- ctx .Done ():
374440 return
375441 default :
376442 }
377443
444+ // check if this is xml content type
445+ mediaTypeStr := mt .GetKeyValue ()
446+ isXML := schema_validation .IsXMLContentType (mediaTypeStr )
447+
378448 if mt .Value .Examples .Len () >= 1 && mt .SchemaProxy != nil {
449+ var exampleValidator exampleValidatorFunc
450+ if isXML {
451+ exampleValidator = createXMLValidator (mt .SchemaProxy .Schema , version )
452+ } else {
453+ exampleValidator = createJSONValidator (nil , "" , "examples" , mt .SchemaProxy .Schema , mt , nil , nil )
454+ }
379455 expLock .Lock ()
380- results = append (results , parseExamples (mt .SchemaProxy .Schema , mt , mt .Value .Examples )... )
456+ results = append (results , parseExamples (mt .SchemaProxy .Schema , mt , mt .Value .Examples , exampleValidator )... )
381457 expLock .Unlock ()
382458 } else {
383459 if mt .Value .Example != nil && mt .SchemaProxy != nil {
460+ var exampleValidator exampleValidatorFunc
461+ if isXML {
462+ exampleValidator = createXMLValidator (mt .SchemaProxy .Schema , version )
463+ } else {
464+ exampleValidator = createJSONValidator (nil , "" , "example" , mt .SchemaProxy .Schema , mt , mt .Value .Example , mt .Value .GoLow ().Example .GetKeyNode ())
465+ }
384466 expLock .Lock ()
385467 results = append (results , parseExample (mt .SchemaProxy .Schema , mt .Value .Example ,
386- mt .Value .GoLow ().Example .GetKeyNode ())... )
468+ mt .Value .GoLow ().Example .GetKeyNode (), exampleValidator )... )
387469 expLock .Unlock ()
388470 }
389471 }
@@ -392,7 +474,7 @@ func (es ExamplesSchema) RunRule(_ []*yaml.Node, ruleContext model.RuleFunctionC
392474
393475 }
394476
395- // Wait for all workers to complete or context to timeout
477+ // wait for all workers to complete or context to timeout
396478 done := make (chan struct {})
397479 go func () {
398480 wg .Wait ()
@@ -401,13 +483,13 @@ func (es ExamplesSchema) RunRule(_ []*yaml.Node, ruleContext model.RuleFunctionC
401483
402484 select {
403485 case <- done :
404- // All workers completed normally
486+ // all workers completed normally
405487 if ruleContext .Logger != nil && atomic .LoadInt32 (& completedWorkers ) > 0 {
406488 ruleContext .Logger .Debug ("ExamplesSchema completed validations" ,
407489 "completed" , atomic .LoadInt32 (& completedWorkers ))
408490 }
409491 case <- ctx .Done ():
410- // Timeout occurred - return whatever results we have
492+ // timeout occurred - return whatever results we have
411493 if ruleContext .Logger != nil {
412494 ruleContext .Logger .Warn ("ExamplesSchema validation timeout" ,
413495 "timeout" , validationTimeout ,
0 commit comments