Skip to content

Commit 4e1f9e8

Browse files
authored
jsonschema: allow $ref in allOf schemas. (#8698)
Resolve any RefSchema within an AllOf before attempting to merge schemas. Disregards empty anyOfs as top type. Fixes #6523. Signed-off-by: Dionna Glaze <d_glaze@apple.com>
1 parent e07e1ec commit 4e1f9e8

2 files changed

Lines changed: 179 additions & 3 deletions

File tree

v1/ast/compile.go

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1766,7 +1766,22 @@ func (parser *schemaParser) parseSchemaWithPropertyKey(schema any, propertyKey s
17661766
}
17671767

17681768
if subSchema.AllOf != nil {
1769-
subSchemaArray := subSchema.AllOf
1769+
// Build the list of schemas to merge: resolve $refs and skip pure anyOf
1770+
// wrappers that carry no explicit type or structure. Such schemas have an
1771+
// "Undefined" type that would cause a spurious type-mismatch in mergeSchemas.
1772+
subSchemaArray := make([]*gojsonschema.SubSchema, 0, len(subSchema.AllOf))
1773+
for _, s := range subSchema.AllOf {
1774+
for s.RefSchema != nil {
1775+
s = s.RefSchema
1776+
}
1777+
if !s.Types.IsTyped() && s.AnyOf != nil && len(s.PropertiesChildren) == 0 && len(s.ItemsChildren) == 0 {
1778+
continue
1779+
}
1780+
subSchemaArray = append(subSchemaArray, s)
1781+
}
1782+
if len(subSchemaArray) == 0 {
1783+
return types.A, nil
1784+
}
17701785
allOfResult, err := mergeSchemas(subSchemaArray...)
17711786
if err != nil {
17721787
return nil, err

v1/ast/schema_test.go

Lines changed: 163 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -697,6 +697,31 @@ func TestCompilerCheckTypesWithAllOfSchema(t *testing.T) {
697697
schema: allOfSchemaWithUnevenArray,
698698
expectedError: nil,
699699
},
700+
{
701+
note: "allOf with single Object type through ref",
702+
schema: allOfRef,
703+
expectedError: nil,
704+
},
705+
{
706+
note: "allOf with mergeable Object types through ref",
707+
schema: allOfObjectRef,
708+
expectedError: nil,
709+
},
710+
{
711+
note: "allOf with anyOf containing $ref types",
712+
schema: allOfAnyOfRef,
713+
expectedError: nil,
714+
},
715+
{
716+
note: "allOf with $ref issue #6523",
717+
schema: iss6523,
718+
expectedError: nil,
719+
},
720+
{
721+
note: "allOf $ref unmergable",
722+
schema: allOfRefUnmergable,
723+
expectedError: errors.New("unable to merge these schemas: type mismatch: string and integer"),
724+
},
700725
{
701726
note: "allOf schema with unmergeable Array of Arrays",
702727
schema: allOfArrayOfArrays,
@@ -747,8 +772,8 @@ func TestCompilerCheckTypesWithAllOfSchema(t *testing.T) {
747772
c.WithSchemas(schemaSet)
748773
compileStages(c, StageCheckTypes)
749774
if tc.expectedError != nil {
750-
if errors.Is(c.Errors, tc.expectedError) {
751-
t.Fatal("Unexpected error:", err)
775+
if !strings.Contains(c.Errors.Error(), tc.expectedError.Error()) {
776+
t.Fatal("Unexpected error:", c.Errors)
752777
}
753778
} else {
754779
assertNotFailed(t, c)
@@ -1453,6 +1478,142 @@ const allOfArrayMissing = `{
14531478
]
14541479
}`
14551480

1481+
const allOfRef = `{
1482+
"$schema": "https://json-schema.org/draft/2020-12/schema",
1483+
"allOf": [{"$ref": "#/$defs/d"}],
1484+
"$defs": {
1485+
"d": {
1486+
"type": "object",
1487+
"properties": {
1488+
"v": {"const": 1}
1489+
},
1490+
"additionalProperties": false
1491+
}
1492+
}
1493+
}`
1494+
1495+
const allOfObjectRef = `{
1496+
"$schema": "https://json-schema.org/draft/2020-12/schema",
1497+
"allOf": [
1498+
{
1499+
"type": "object",
1500+
"properties": {
1501+
"v": {"integer"}
1502+
}
1503+
},
1504+
{"$ref": "#/$defs/d"}
1505+
],
1506+
"$defs": {
1507+
"d": {
1508+
"type": "object",
1509+
"properties": {
1510+
"v": {"integer"}
1511+
}
1512+
}
1513+
}
1514+
}`
1515+
1516+
const allOfAnyOfRef = `{
1517+
"$schema": "https://json-schema.org/draft/2020-12/schema",
1518+
"allOf": [
1519+
{
1520+
"type": "object",
1521+
"properties": {
1522+
"v": {"enum": [1, 2]}
1523+
}
1524+
},
1525+
{"anyOf": [{"$ref": "#/$defs/d1"}, {"$ref": "#/$defs/d2"}]}
1526+
],
1527+
"$defs": {
1528+
"d1": {
1529+
"type": "object",
1530+
"properties": {
1531+
"v": {"const": 1},
1532+
"k": {"type": "integer"}
1533+
},
1534+
"additionalProperties": false
1535+
},
1536+
"d2": {
1537+
"type": "object",
1538+
"properties": {
1539+
"v": {"const": 2},
1540+
"j": {"type": "string"}
1541+
},
1542+
"additionalProperties": false
1543+
}
1544+
}
1545+
}`
1546+
1547+
const iss6523 = `{
1548+
"$schema": "https://json-schema.org/draft-07/schema",
1549+
"$id": "https://jsonschema.dev/schemas/opa-input",
1550+
"$defs": {
1551+
"mixins/action.schema.json": {
1552+
"$schema": "https://json-schema.org/draft-07/schema",
1553+
"$id": "mixins/action.schema.json",
1554+
"type": "string"
1555+
},
1556+
"mixins/context.schema.json": {
1557+
"$schema": "https://json-schema.org/draft-07/schema",
1558+
"$id": "mixins/context.schema.json",
1559+
"type": "object",
1560+
"properties": {
1561+
"user": {
1562+
"type": "string"
1563+
}
1564+
},
1565+
"required": [
1566+
"user"
1567+
]
1568+
},
1569+
"mixins/special-context.schema.json": {
1570+
"$schema": "https://json-schema.org/draft-07/schema",
1571+
"$id": "mixins/special-context.schema.json",
1572+
"type": "object",
1573+
"properties": {
1574+
"groups": {
1575+
"type": "string"
1576+
}
1577+
},
1578+
"allOf": [
1579+
{
1580+
"$ref": "https://jsonschema.dev/schemas/mixins/context.schema.json"
1581+
}
1582+
],
1583+
"required": [
1584+
"groups"
1585+
]
1586+
}
1587+
},
1588+
"type": "object",
1589+
"properties": {
1590+
"action": {
1591+
"$ref": "https://jsonschema.dev/schemas/mixins/action.schema.json"
1592+
},
1593+
"context": {
1594+
"$ref": "https://jsonschema.dev/schemas/mixins/special-context.schema.json"
1595+
}
1596+
},
1597+
"required": [
1598+
"action",
1599+
"context"
1600+
]
1601+
}`
1602+
1603+
const allOfRefUnmergable = `{
1604+
"$schema": "https://json-schema.org/draft/2020-12/schema",
1605+
"$id": "https://jsonschema.dev/schemas/opa-input",
1606+
"$defs": {
1607+
"d1": {
1608+
"type": "string"
1609+
},
1610+
"d2": {
1611+
"type": "integer",
1612+
}
1613+
},
1614+
"allOf": [{"$ref": "#/$defs/d1"}, {"$ref": "#/$defs/d2"}]
1615+
}`
1616+
14561617
const anyOfSchemaParentVariation = `{
14571618
"$schema": "http://json-schema.org/draft-04/schema#",
14581619
"anyOf": [

0 commit comments

Comments
 (0)