Skip to content
This repository was archived by the owner on Nov 11, 2025. It is now read-only.

Commit ac6d8b6

Browse files
committed
add defaultMapping in discriminator
1 parent 2e2c3cf commit ac6d8b6

File tree

10 files changed

+164
-5
lines changed

10 files changed

+164
-5
lines changed

src/Microsoft.OpenApi/Models/OpenApiDiscriminator.cs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,11 @@ public class OpenApiDiscriminator : IOpenApiSerializable, IOpenApiExtensible
2525
/// </summary>
2626
public IDictionary<string, IOpenApiExtension>? Extensions { get; set; }
2727

28+
/// <summary>
29+
/// OAI 3.2.0: The schema name or URI reference to a schema that is expected to validate the structure of the model when the discriminating property is not present in the payload or contains a value for which there is no explicit or implicit mapping.
30+
/// </summary>
31+
public OpenApiSchemaReference? DefaultMapping { get; set; }
32+
2833
/// <summary>
2934
/// Parameter-less constructor
3035
/// </summary>
@@ -38,6 +43,7 @@ public OpenApiDiscriminator(OpenApiDiscriminator discriminator)
3843
PropertyName = discriminator?.PropertyName ?? PropertyName;
3944
Mapping = discriminator?.Mapping != null ? new Dictionary<string, OpenApiSchemaReference>(discriminator.Mapping) : null;
4045
Extensions = discriminator?.Extensions != null ? new Dictionary<string, IOpenApiExtension>(discriminator.Extensions) : null;
46+
DefaultMapping = discriminator?.DefaultMapping;
4147
}
4248

4349
/// <summary>
@@ -48,6 +54,13 @@ public void SerializeAsV32(IOpenApiWriter writer)
4854
{
4955
SerializeInternal(writer, OpenApiSpecVersion.OpenApi3_2);
5056

57+
// Write defaultMapping property in 3.2.0
58+
if (DefaultMapping != null)
59+
{
60+
writer.WritePropertyName("defaultMapping");
61+
DefaultMapping.SerializeAsV32(writer);
62+
}
63+
5164
// extensions
5265
writer.WriteExtensions(Extensions, OpenApiSpecVersion.OpenApi3_2);
5366

@@ -62,6 +75,13 @@ public void SerializeAsV31(IOpenApiWriter writer)
6275
{
6376
SerializeInternal(writer, OpenApiSpecVersion.OpenApi3_1);
6477

78+
// Write as x-oas-default-mapping extension in 3.1.0
79+
if (DefaultMapping != null)
80+
{
81+
writer.WritePropertyName("x-oas-default-mapping");
82+
DefaultMapping.SerializeAsV31(writer);
83+
}
84+
6585
// extensions
6686
writer.WriteExtensions(Extensions, OpenApiSpecVersion.OpenApi3_1);
6787

@@ -75,6 +95,13 @@ public void SerializeAsV3(IOpenApiWriter writer)
7595
{
7696
SerializeInternal(writer, OpenApiSpecVersion.OpenApi3_0);
7797

98+
// Write as x-oas-default-mapping extension in 3.0.0
99+
if (DefaultMapping != null)
100+
{
101+
writer.WritePropertyName("x-oas-default-mapping");
102+
DefaultMapping.SerializeAsV3(writer);
103+
}
104+
78105
writer.WriteEndObject();
79106
}
80107

src/Microsoft.OpenApi/Reader/V3/OpenApiDiscriminatorDeserializer.cs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,16 @@ internal static partial class OpenApiV3Deserializer
2424
}
2525
};
2626

27-
private static readonly PatternFieldMap<OpenApiDiscriminator> _discriminatorPatternFields = new();
27+
private static readonly PatternFieldMap<OpenApiDiscriminator> _discriminatorPatternFields = new() {
28+
{s => s.StartsWith(OpenApiConstants.ExtensionFieldNamePrefix, StringComparison.OrdinalIgnoreCase), (o, p, n, doc) => {
29+
// Handle x-oas-default-mapping as DefaultMapping property
30+
if (p.Equals("x-oas-default-mapping", StringComparison.OrdinalIgnoreCase))
31+
{
32+
o.DefaultMapping = LoadMapping(n, doc);
33+
}
34+
}
35+
}
36+
};
2837

2938
public static OpenApiDiscriminator LoadDiscriminator(ParseNode node, OpenApiDocument hostDocument)
3039
{

src/Microsoft.OpenApi/Reader/V31/OpenApiDiscriminatorDeserializer.cs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,18 @@ internal static partial class OpenApiV31Deserializer
2828
private static readonly PatternFieldMap<OpenApiDiscriminator> _discriminatorPatternFields =
2929
new()
3030
{
31-
{s => s.StartsWith(OpenApiConstants.ExtensionFieldNamePrefix, StringComparison.OrdinalIgnoreCase), (o, p, n, _) => o.AddExtension(p, LoadExtension(p,n))}
31+
{s => s.StartsWith(OpenApiConstants.ExtensionFieldNamePrefix, StringComparison.OrdinalIgnoreCase), (o, p, n, doc) => {
32+
// Handle x-oas-default-mapping as DefaultMapping property
33+
if (p.Equals("x-oas-default-mapping", StringComparison.OrdinalIgnoreCase))
34+
{
35+
o.DefaultMapping = LoadMapping(n, doc);
36+
}
37+
else
38+
{
39+
o.AddExtension(p, LoadExtension(p, n));
40+
}
41+
}
42+
}
3243
};
3344

3445
public static OpenApiDiscriminator LoadDiscriminator(ParseNode node, OpenApiDocument hostDocument)

src/Microsoft.OpenApi/Reader/V32/OpenApiDiscriminatorDeserializer.cs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
using System;
1+
using System;
22

33
namespace Microsoft.OpenApi.Reader.V32
44
{
@@ -22,6 +22,12 @@ internal static partial class OpenApiV32Deserializer
2222
{
2323
o.Mapping = n.CreateSimpleMap((node) => LoadMapping(node, doc));
2424
}
25+
},
26+
{
27+
"defaultMapping", (o, n, doc) =>
28+
{
29+
o.DefaultMapping = LoadMapping(n, doc);
30+
}
2531
}
2632
};
2733

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT license.
3+
4+
using System.Collections.Generic;
5+
using System.IO;
6+
using System.Threading.Tasks;
7+
using Microsoft.OpenApi.Reader;
8+
using Xunit;
9+
10+
namespace Microsoft.OpenApi.Readers.Tests.V31Tests
11+
{
12+
[Collection("DefaultSettings")]
13+
public class OpenApiDiscriminatorTests
14+
{
15+
private const string SampleFolderPath = "V31Tests/Samples/OpenApiDiscriminator/";
16+
17+
[Fact]
18+
public async Task ParseBasicDiscriminatorShouldSucceed()
19+
{
20+
// Arrange
21+
using var stream = Resources.GetStream(Path.Combine(SampleFolderPath, "basicDiscriminator.yaml"));
22+
// Copy stream to MemoryStream
23+
using var memoryStream = new MemoryStream();
24+
await stream.CopyToAsync(memoryStream);
25+
memoryStream.Position = 0;
26+
27+
// Act
28+
var openApiDocument = new OpenApiDocument();
29+
var discriminator = OpenApiModelFactory.Load<OpenApiDiscriminator>(memoryStream, OpenApiSpecVersion.OpenApi3_1, OpenApiConstants.Yaml, openApiDocument, out var diagnostic, SettingsFixture.ReaderSettings);
30+
31+
// Assert
32+
Assert.Equivalent(
33+
new OpenApiDiscriminator
34+
{
35+
PropertyName = "pet_type",
36+
Mapping = new Dictionary<string, OpenApiSchemaReference>
37+
{
38+
["puppy"] = new OpenApiSchemaReference("Dog", openApiDocument),
39+
["kitten"] = new OpenApiSchemaReference("Cat" , openApiDocument, "https://gigantic-server.com/schemas/animals.json"),
40+
["monster"] = new OpenApiSchemaReference("monster" , openApiDocument, "https://gigantic-server.com/schemas/Monster/schema.json")
41+
},
42+
DefaultMapping = new OpenApiSchemaReference("Animal", openApiDocument)
43+
}, discriminator);
44+
}
45+
}
46+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
propertyName: pet_type
2+
mapping:
3+
puppy: '#/components/schemas/Dog'
4+
kitten: https://gigantic-server.com/schemas/animals.json#/components/schemas/Cat
5+
monster: https://gigantic-server.com/schemas/Monster/schema.json#/components/schemas/monster
6+
x-oas-default-mapping: '#/components/schemas/Animal'
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT license.
3+
4+
using System.Collections.Generic;
5+
using System.IO;
6+
using System.Threading.Tasks;
7+
using Microsoft.OpenApi.Reader;
8+
using Xunit;
9+
10+
namespace Microsoft.OpenApi.Readers.Tests.V32Tests
11+
{
12+
[Collection("DefaultSettings")]
13+
public class OpenApiDiscriminatorTests
14+
{
15+
private const string SampleFolderPath = "V32Tests/Samples/OpenApiDiscriminator/";
16+
17+
[Fact]
18+
public async Task ParseBasicDiscriminatorShouldSucceed()
19+
{
20+
// Arrange
21+
using var stream = Resources.GetStream(Path.Combine(SampleFolderPath, "basicDiscriminator.yaml"));
22+
// Copy stream to MemoryStream
23+
using var memoryStream = new MemoryStream();
24+
await stream.CopyToAsync(memoryStream);
25+
memoryStream.Position = 0;
26+
27+
// Act
28+
var openApiDocument = new OpenApiDocument();
29+
var discriminator = OpenApiModelFactory.Load<OpenApiDiscriminator>(memoryStream, OpenApiSpecVersion.OpenApi3_2, OpenApiConstants.Yaml, openApiDocument, out var diagnostic, SettingsFixture.ReaderSettings);
30+
31+
// Assert
32+
Assert.Equivalent(
33+
new OpenApiDiscriminator
34+
{
35+
PropertyName = "pet_type",
36+
Mapping = new Dictionary<string, OpenApiSchemaReference>
37+
{
38+
["puppy"] = new OpenApiSchemaReference("Dog", openApiDocument),
39+
["kitten"] = new OpenApiSchemaReference("Cat" , openApiDocument, "https://gigantic-server.com/schemas/animals.json"),
40+
["monster"] = new OpenApiSchemaReference("monster" , openApiDocument, "https://gigantic-server.com/schemas/Monster/schema.json")
41+
},
42+
DefaultMapping = new OpenApiSchemaReference("Animal", openApiDocument)
43+
}, discriminator);
44+
}
45+
}
46+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
propertyName: pet_type
2+
mapping:
3+
puppy: '#/components/schemas/Dog'
4+
kitten: https://gigantic-server.com/schemas/animals.json#/components/schemas/Cat
5+
monster: https://gigantic-server.com/schemas/Monster/schema.json#/components/schemas/monster
6+
defaultMapping: '#/components/schemas/Animal'

test/Microsoft.OpenApi.Readers.Tests/V3Tests/OpenApiDiscriminatorTests.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,8 @@ public async Task ParseBasicDiscriminatorShouldSucceed()
3838
["puppy"] = new OpenApiSchemaReference("Dog", openApiDocument),
3939
["kitten"] = new OpenApiSchemaReference("Cat" , openApiDocument, "https://gigantic-server.com/schemas/animals.json"),
4040
["monster"] = new OpenApiSchemaReference("schema.json" , openApiDocument, "https://gigantic-server.com/schemas/Monster/schema.json")
41-
}
41+
},
42+
DefaultMapping = new OpenApiSchemaReference("Animal", openApiDocument)
4243
}, discriminator);
4344
}
4445
}

test/Microsoft.OpenApi.Readers.Tests/V3Tests/Samples/OpenApiDiscriminator/basicDiscriminator.yaml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,5 @@
22
mapping:
33
puppy: '#/components/schemas/Dog'
44
kitten: https://gigantic-server.com/schemas/animals.json#/components/schemas/Cat
5-
monster: https://gigantic-server.com/schemas/Monster/schema.json
5+
monster: https://gigantic-server.com/schemas/Monster/schema.json
6+
x-oas-default-mapping: '#/components/schemas/Animal'

0 commit comments

Comments
 (0)