Skip to content

Commit 2327640

Browse files
committed
There is a new mode in libopenapi for setting the rendering context to be either bundling or validation.
1 parent f040207 commit 2327640

2 files changed

Lines changed: 218 additions & 3 deletions

File tree

schema_validation/validate_schema.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -122,15 +122,15 @@ func (s *schemaValidator) validateSchemaWithVersion(schema *base.Schema, payload
122122
return false, validationErrors
123123
}
124124

125-
// extract index of schema, and check the version
126-
// schemaIndex := schema.GoLow().Index
127125
var renderedSchema []byte
128126

129127
// render the schema, to be used for validation, stop this from running concurrently, mutations are made to state
130128
// and, it will cause async issues.
131129
// Create isolated render context for this validation to prevent false positive cycle detection
132130
// when multiple validations run concurrently.
133-
renderCtx := base.NewInlineRenderContext()
131+
// Use validation mode to force full inlining of discriminator refs - the JSON schema compiler
132+
// needs a self-contained schema without unresolved $refs.
133+
renderCtx := base.NewInlineRenderContextForValidation()
134134
s.lock.Lock()
135135
var e error
136136
renderedSchema, e = schema.RenderInlineWithContext(renderCtx)

schema_validation/validate_schema_test.go

Lines changed: 215 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1053,3 +1053,218 @@ components:
10531053
})
10541054
}
10551055
}
1056+
1057+
// TestValidateSchema_Discriminator_OneOf_WithRefs_Issue788 tests that validation works correctly
1058+
// when a schema has discriminator + oneOf with $ref to component schemas.
1059+
// This was a regression in vacuum v0.21.2+ where the validator would fail with
1060+
// "JSON schema compile failed: json-pointer not found" because discriminator refs
1061+
// were being preserved instead of inlined.
1062+
// https://github.com/daveshanley/vacuum/issues/788
1063+
func TestValidateSchema_Discriminator_OneOf_WithRefs_Issue788(t *testing.T) {
1064+
spec := `openapi: 3.1.0
1065+
info:
1066+
title: Test Discriminator OneOf With Refs
1067+
version: 1.0.0
1068+
components:
1069+
schemas:
1070+
ProductWidget:
1071+
type: object
1072+
required:
1073+
- productName
1074+
- quantity
1075+
- color
1076+
properties:
1077+
productName:
1078+
type: string
1079+
enum:
1080+
- Widget
1081+
quantity:
1082+
type: integer
1083+
minimum: 1
1084+
color:
1085+
type: string
1086+
enum:
1087+
- Red
1088+
- Blue
1089+
- Green
1090+
ProductGadget:
1091+
type: object
1092+
required:
1093+
- productName
1094+
- quantity
1095+
- size
1096+
properties:
1097+
productName:
1098+
type: string
1099+
enum:
1100+
- Gadget
1101+
quantity:
1102+
type: integer
1103+
minimum: 1
1104+
size:
1105+
type: string
1106+
enum:
1107+
- Small
1108+
- Medium
1109+
- Large
1110+
Product:
1111+
oneOf:
1112+
- $ref: '#/components/schemas/ProductWidget'
1113+
- $ref: '#/components/schemas/ProductGadget'
1114+
discriminator:
1115+
propertyName: productName`
1116+
1117+
doc, err := libopenapi.NewDocument([]byte(spec))
1118+
assert.NoError(t, err)
1119+
1120+
model, errs := doc.BuildV3Model()
1121+
assert.Empty(t, errs)
1122+
1123+
productSchema := model.Model.Components.Schemas.GetOrZero("Product").Schema()
1124+
1125+
// Valid Widget product
1126+
validWidget := map[string]interface{}{
1127+
"productName": "Widget",
1128+
"quantity": 1,
1129+
"color": "Green",
1130+
}
1131+
1132+
validator := NewSchemaValidator()
1133+
valid, validationErrors := validator.ValidateSchemaObject(productSchema, validWidget)
1134+
1135+
// This should pass without "json-pointer not found" error
1136+
assert.True(t, valid, "validation should pass for valid product with discriminator oneOf refs")
1137+
assert.Empty(t, validationErrors, "no validation errors should be present")
1138+
}
1139+
1140+
// TestValidateSchema_Discriminator_AnyOf_WithRefs tests anyOf with discriminator and $refs
1141+
func TestValidateSchema_Discriminator_AnyOf_WithRefs(t *testing.T) {
1142+
spec := `openapi: 3.1.0
1143+
info:
1144+
title: Test Discriminator AnyOf With Refs
1145+
version: 1.0.0
1146+
components:
1147+
schemas:
1148+
Cat:
1149+
type: object
1150+
required:
1151+
- petType
1152+
- meow
1153+
properties:
1154+
petType:
1155+
type: string
1156+
const: cat
1157+
meow:
1158+
type: boolean
1159+
Dog:
1160+
type: object
1161+
required:
1162+
- petType
1163+
- bark
1164+
properties:
1165+
petType:
1166+
type: string
1167+
const: dog
1168+
bark:
1169+
type: boolean
1170+
Pet:
1171+
anyOf:
1172+
- $ref: '#/components/schemas/Cat'
1173+
- $ref: '#/components/schemas/Dog'
1174+
discriminator:
1175+
propertyName: petType`
1176+
1177+
doc, err := libopenapi.NewDocument([]byte(spec))
1178+
assert.NoError(t, err)
1179+
1180+
model, errs := doc.BuildV3Model()
1181+
assert.Empty(t, errs)
1182+
1183+
petSchema := model.Model.Components.Schemas.GetOrZero("Pet").Schema()
1184+
1185+
// Valid cat
1186+
validCat := map[string]interface{}{
1187+
"petType": "cat",
1188+
"meow": true,
1189+
}
1190+
1191+
validator := NewSchemaValidator()
1192+
valid, validationErrors := validator.ValidateSchemaObject(petSchema, validCat)
1193+
1194+
assert.True(t, valid, "validation should pass for valid cat with discriminator anyOf refs")
1195+
assert.Empty(t, validationErrors, "no validation errors should be present")
1196+
}
1197+
1198+
// TestValidateSchema_Discriminator_OneOf_WithRefs_InvalidData tests that invalid data
1199+
// still fails validation correctly (not a false negative)
1200+
func TestValidateSchema_Discriminator_OneOf_WithRefs_InvalidData(t *testing.T) {
1201+
spec := `openapi: 3.1.0
1202+
info:
1203+
title: Test Discriminator OneOf Invalid
1204+
version: 1.0.0
1205+
components:
1206+
schemas:
1207+
ProductWidget:
1208+
type: object
1209+
required:
1210+
- productName
1211+
- quantity
1212+
- color
1213+
properties:
1214+
productName:
1215+
type: string
1216+
enum:
1217+
- Widget
1218+
quantity:
1219+
type: integer
1220+
minimum: 1
1221+
color:
1222+
type: string
1223+
enum:
1224+
- Red
1225+
- Blue
1226+
ProductGadget:
1227+
type: object
1228+
required:
1229+
- productName
1230+
- quantity
1231+
- size
1232+
properties:
1233+
productName:
1234+
type: string
1235+
enum:
1236+
- Gadget
1237+
quantity:
1238+
type: integer
1239+
minimum: 1
1240+
size:
1241+
type: string
1242+
Product:
1243+
oneOf:
1244+
- $ref: '#/components/schemas/ProductWidget'
1245+
- $ref: '#/components/schemas/ProductGadget'
1246+
discriminator:
1247+
propertyName: productName`
1248+
1249+
doc, err := libopenapi.NewDocument([]byte(spec))
1250+
assert.NoError(t, err)
1251+
1252+
model, errs := doc.BuildV3Model()
1253+
assert.Empty(t, errs)
1254+
1255+
productSchema := model.Model.Components.Schemas.GetOrZero("Product").Schema()
1256+
1257+
// Invalid product - missing required field 'color' for Widget
1258+
invalidProduct := map[string]interface{}{
1259+
"productName": "Widget",
1260+
"quantity": 1,
1261+
// missing required 'color' field
1262+
}
1263+
1264+
validator := NewSchemaValidator()
1265+
valid, validationErrors := validator.ValidateSchemaObject(productSchema, invalidProduct)
1266+
1267+
// This should fail because 'color' is required for Widget
1268+
assert.False(t, valid, "validation should fail for invalid product")
1269+
assert.NotEmpty(t, validationErrors, "validation errors should be present")
1270+
}

0 commit comments

Comments
 (0)