Skip to content

Commit 75b7f72

Browse files
committed
updated deps and examples error reporting
1 parent 5887d97 commit 75b7f72

File tree

5 files changed

+3435
-2576
lines changed

5 files changed

+3435
-2576
lines changed

functions/openapi/oas_schema.go

Lines changed: 88 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -145,20 +145,29 @@ func (os OASSchema) RunRule(nodes []*yaml.Node, context model.RuleFunctionContex
145145
}
146146

147147
schemaErr := validationErrors[i].SchemaValidationErrors[y]
148-
var reason = schemaErr.Reason
149-
// if reason is empty or generic, extract leaf error messages (issue #766)
150-
if reason == "validation failed" || reason == "" {
151-
if schemaErr.OriginalError != nil {
152-
leafErrors := extractLeafValidationErrors(schemaErr.OriginalError)
153-
if len(leafErrors) > 0 {
154-
reason = strings.Join(leafErrors, "; ")
148+
var reason string
149+
150+
// Always prefer leaf error extraction from OriginalError when available (issue #766)
151+
if schemaErr.OriginalError != nil {
152+
leafErrors := extractLeafValidationErrors(schemaErr.OriginalError)
153+
if len(leafErrors) > 0 {
154+
// Limit to last 3 leaf errors for readability
155+
if len(leafErrors) > 3 {
156+
leafErrors = leafErrors[len(leafErrors)-3:]
155157
}
156-
}
157-
// fallback to generic message
158-
if reason == "" {
159-
reason = "multiple components failed validation"
158+
reason = strings.Join(leafErrors, "; ")
160159
}
161160
}
161+
162+
// Fallback to Reason if no leaf errors extracted
163+
if reason == "" {
164+
reason = schemaErr.Reason
165+
}
166+
167+
// Final fallback
168+
if reason == "" {
169+
reason = "schema validation failed"
170+
}
162171
res := model.RuleFunctionResult{
163172
Message: fmt.Sprintf("schema invalid: %v", reason),
164173
StartNode: n,
@@ -214,10 +223,13 @@ func checkForNullableKeyword(context model.RuleFunctionContext) []model.RuleFunc
214223
return results
215224
}
216225

217-
// extractLeafValidationErrors extracts only leaf error messages from a jsonschema.ValidationError tree (issue #766)
226+
// extractLeafValidationErrors extracts only meaningful leaf error messages from a jsonschema.ValidationError tree (issue #766)
227+
// It filters out noise from oneOf/anyOf schema structures (like "missing properties: [$ref]")
218228
func extractLeafValidationErrors(err *jsonschema.ValidationError) []string {
219-
var results []string
229+
var highPriority []string // Type errors, pattern errors, format errors - the real issues
230+
var lowPriority []string // Enum errors (often noise from oneOf branches)
220231
seen := make(map[string]bool)
232+
seenPaths := make(map[string]bool) // Track paths we've already reported errors for
221233
const maxDepth = 50
222234

223235
var extract func(e *jsonschema.ValidationError, depth int)
@@ -229,12 +241,26 @@ func extractLeafValidationErrors(err *jsonschema.ValidationError) []string {
229241
// if this is a leaf node (no causes), extract the message
230242
if len(e.Causes) == 0 {
231243
path := "/" + strings.Join(e.InstanceLocation, "/")
244+
245+
// Skip noise errors from oneOf/anyOf schema structures
246+
if isNoiseError(e.ErrorKind) {
247+
return
248+
}
249+
232250
msg := errorKindToString(e.ErrorKind)
233251
if msg != "" {
234252
fullMsg := fmt.Sprintf("`%s` %s", path, msg)
235253
if !seen[fullMsg] {
236254
seen[fullMsg] = true
237-
results = append(results, fullMsg)
255+
256+
// Categorize by priority
257+
if isHighPriorityError(e.ErrorKind) {
258+
highPriority = append(highPriority, fullMsg)
259+
seenPaths[path] = true
260+
} else if !seenPaths[path] {
261+
// Only add low priority if we haven't seen this path in high priority
262+
lowPriority = append(lowPriority, fullMsg)
263+
}
238264
}
239265
}
240266
}
@@ -246,7 +272,54 @@ func extractLeafValidationErrors(err *jsonschema.ValidationError) []string {
246272
}
247273

248274
extract(err, 0)
249-
return results
275+
276+
// Return high priority errors first, then low priority
277+
if len(highPriority) > 0 {
278+
return highPriority
279+
}
280+
return lowPriority
281+
}
282+
283+
// isNoiseError returns true for errors that are noise from oneOf/anyOf schema structures
284+
func isNoiseError(ek jsonschema.ErrorKind) bool {
285+
if ek == nil {
286+
return true
287+
}
288+
switch k := ek.(type) {
289+
case *kind.Required:
290+
// "missing properties: [$ref]" is noise from oneOf branches in OpenAPI schemas
291+
for _, missing := range k.Missing {
292+
if missing == "$ref" {
293+
return true
294+
}
295+
}
296+
case *kind.FalseSchema:
297+
// "property not allowed" from false schema branches
298+
return true
299+
}
300+
return false
301+
}
302+
303+
// isHighPriorityError returns true for errors that represent the actual root cause
304+
func isHighPriorityError(ek jsonschema.ErrorKind) bool {
305+
if ek == nil {
306+
return false
307+
}
308+
switch ek.(type) {
309+
case *kind.Type:
310+
return true // Wrong type is usually the real issue
311+
case *kind.Pattern:
312+
return true // Pattern mismatch is specific
313+
case *kind.Format:
314+
return true // Format error is specific
315+
case *kind.Const:
316+
return true // Const mismatch is specific
317+
case *kind.AdditionalProperties:
318+
return true // Extra properties is specific
319+
case *kind.Required:
320+
return true // Missing required field (that isn't $ref)
321+
}
322+
return false
250323
}
251324

252325
// errorKindToString converts a jsonschema ErrorKind to a human-readable string

html-report/ui/package.json

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,22 +20,22 @@
2020
"@types/chart.js": "^2.9.37",
2121
"@typescript-eslint/eslint-plugin": "^5.38.1",
2222
"@typescript-eslint/parser": "^5.38.1",
23-
"@web/dev-server-esbuild": "^0.3.3",
24-
"@web/test-runner": "^0.15.1",
25-
"@web/test-runner-commands": "^0.6.5",
26-
"@web/test-runner-playwright": "^0.9.0",
27-
"copy-webpack-plugin": "^11.0.0",
23+
"@web/dev-server-esbuild": "^1.0.4",
24+
"@web/test-runner": "^0.20.2",
25+
"@web/test-runner-commands": "^0.9.0",
26+
"@web/test-runner-playwright": "^0.11.1",
27+
"copy-webpack-plugin": "^13.0.1",
2828
"eslint": "^8.23.0",
2929
"eslint-config-prettier": "^8.5.0",
3030
"eslint-plugin-import": "^2.26.0",
3131
"eslint-plugin-lit-a11y": "^2.3.0",
3232
"eslint-plugin-wc": "^1.3.2",
33-
"lint-staged": "^13.0.3",
33+
"lint-staged": "^16.2.7",
3434
"mini-css-extract-plugin": "^2.6.1",
3535
"prettier": "^2.7.1",
3636
"webpack": "^5.74.0",
3737
"webpack-cli": "^5.0.1",
38-
"webpack-dev-server": "^4.11.1"
38+
"webpack-dev-server": "^5.2.2"
3939
},
4040
"dependencies": {
4141
"@open-wc/testing": "^3.1.7",

html-report/ui/tsconfig.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,5 +12,5 @@
1212
},
1313
"include": ["src/**/*.ts"],
1414
"exclude": ["node_modules", "./build",
15-
"web-test-runner.config.mjs", "webpack.config.js"]
15+
"web-test-runner.config.mjs", "webpack.config.js", "**/*.test.ts"]
1616
}

html-report/ui/webpack.config.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ module.exports = {
1717
{
1818
test: /\.tsx?$/,
1919
use: 'ts-loader',
20-
exclude: /node_modules/,
20+
exclude: [/node_modules/, /\.test\.ts$/],
2121
},
2222
{
2323
test: /\.css$/,

0 commit comments

Comments
 (0)