Skip to content

Commit f296505

Browse files
committed
fix: recursive relative reference resolution
1 parent b7bc6be commit f296505

File tree

3 files changed

+125
-3
lines changed

3 files changed

+125
-3
lines changed

src/Microsoft.OpenApi/Models/BaseOpenApiReference.cs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -337,10 +337,14 @@ private static string ResolveRelativePointer(string nodeLocation, string relativ
337337
}
338338

339339
// Fallback on building a full path
340+
if (nodeLocation.StartsWith("#/components/schemas/", StringComparison.OrdinalIgnoreCase))
341+
{ // If the nodeLocation is a schema, we only want to keep the first three segments which are components/schemas/{schemaName}
342+
return $"#/{string.Join("/", nodeLocationSegments.Take(3).Concat(relativeSegments))}";
343+
}
340344
#if NETSTANDARD2_1 || NETCOREAPP2_1_OR_GREATER || NET5_0_OR_GREATER
341-
return $"#/{string.Join("/", nodeLocationSegments.SkipLast(relativeSegments.Length).Union(relativeSegments))}";
345+
return $"#/{string.Join("/", nodeLocationSegments.SkipLast(relativeSegments.Length).Concat(relativeSegments))}";
342346
#else
343-
return $"#/{string.Join("/", nodeLocationSegments.Take(nodeLocationSegments.Count - relativeSegments.Length).Union(relativeSegments))}";
347+
return $"#/{string.Join("/", nodeLocationSegments.Take(nodeLocationSegments.Count - relativeSegments.Length).Concat(relativeSegments))}";
344348
#endif
345349
}
346350
}
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
{
2+
"openapi": "3.1.0",
3+
"info": {
4+
"title": "Recursive relative reference in a subschema of an component schema",
5+
"version": "1.0.0"
6+
},
7+
"paths": {
8+
"/items": {
9+
"get": {
10+
"responses": {
11+
"200": {
12+
"description": "ok",
13+
"content": {
14+
"application/json": {
15+
"schema": {
16+
"$ref": "#/components/schemas/Foo"
17+
}
18+
}
19+
}
20+
}
21+
}
22+
}
23+
}
24+
},
25+
"components": {
26+
"schemas": {
27+
"Foo": {
28+
"type": "object",
29+
"properties": {
30+
"name": {
31+
"type": [
32+
"string",
33+
"null"
34+
],
35+
"format": null,
36+
"x-schema-id": null
37+
},
38+
"parent": {
39+
"type": [
40+
"object",
41+
"null"
42+
],
43+
"properties": {
44+
"name": {
45+
"type": [
46+
"string",
47+
"null"
48+
],
49+
"format": null,
50+
"x-schema-id": null
51+
},
52+
"parent": {
53+
"$ref": "#/properties/parent",
54+
"x-schema-id": "Category"
55+
},
56+
"tags": {
57+
"type": [
58+
"array",
59+
"null"
60+
],
61+
"items": {
62+
"type": "object",
63+
"properties": {
64+
"name": {
65+
"type": [
66+
"string",
67+
"null"
68+
],
69+
"format": null,
70+
"x-schema-id": null
71+
}
72+
},
73+
"required": [
74+
"name"
75+
],
76+
"x-schema-id": "Tag"
77+
}
78+
}
79+
},
80+
"required": [
81+
"name"
82+
],
83+
"x-schema-id": "Category"
84+
},
85+
"tags": {
86+
"$ref": "#/properties/parent/properties/tags"
87+
}
88+
},
89+
"required": [
90+
"name"
91+
],
92+
"x-schema-id": "Category"
93+
}
94+
}
95+
}
96+
}

test/Microsoft.OpenApi.Readers.Tests/V31Tests/RelativeReferenceTests.cs

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -280,7 +280,7 @@ public void ResolveSubSchema_ShouldRecurseIntoAllOfComposition()
280280
Assert.Equal(JsonSchemaType.Integer, result!.Type);
281281
}
282282
[Fact]
283-
public async Task SHouldResolveRelativeSubReference()
283+
public async Task ShouldResolveRelativeSubReference()
284284
{
285285
// Arrange
286286
var filePath = Path.Combine(SampleFolderPath, "relativeSubschemaReference.json");
@@ -296,5 +296,27 @@ public async Task SHouldResolveRelativeSubReference()
296296
Assert.Equal(JsonSchemaType.Array, seq2Property.Items.Type);
297297
Assert.Equal(JsonSchemaType.String, seq2Property.Items.Items.Type);
298298
}
299+
[Fact]
300+
public async Task ShouldResolveRecursiveRelativeSubReference()
301+
{
302+
// Arrange
303+
var filePath = Path.Combine(SampleFolderPath, "recursiveRelativeSubschemaReference.json");
304+
305+
// Act
306+
var (actual, _) = await OpenApiDocument.LoadAsync(filePath, SettingsFixture.ReaderSettings);
307+
308+
var fooComponentSchema = actual.Components.Schemas["Foo"];
309+
var fooSchemaParentProperty = fooComponentSchema.Properties["parent"];
310+
Assert.NotNull(fooSchemaParentProperty);
311+
var fooSchemaParentPropertyTagsProperty = fooSchemaParentProperty.Properties["tags"];
312+
Assert.NotNull(fooSchemaParentPropertyTagsProperty);
313+
Assert.Equal(JsonSchemaType.Array | JsonSchemaType.Null, fooSchemaParentPropertyTagsProperty.Type);
314+
Assert.Equal(JsonSchemaType.Object, fooSchemaParentPropertyTagsProperty.Items.Type);
315+
316+
var fooSchemaTagsProperty = fooComponentSchema.Properties["tags"];
317+
Assert.NotNull(fooSchemaTagsProperty);
318+
Assert.Equal(JsonSchemaType.Array | JsonSchemaType.Null, fooSchemaTagsProperty.Type);
319+
Assert.Equal(JsonSchemaType.Object, fooSchemaTagsProperty.Items.Type);
320+
}
299321
}
300322
}

0 commit comments

Comments
 (0)