Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions src/Microsoft.OpenApi/Models/OpenApiDiscriminator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,11 @@ public class OpenApiDiscriminator : IOpenApiSerializable, IOpenApiExtensible
/// </summary>
public IDictionary<string, IOpenApiExtension>? Extensions { get; set; }

/// <summary>
/// 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.
/// </summary>
public OpenApiSchemaReference? DefaultMapping { get; set; }

/// <summary>
/// Parameter-less constructor
/// </summary>
Expand All @@ -38,6 +43,7 @@ public OpenApiDiscriminator(OpenApiDiscriminator discriminator)
PropertyName = discriminator?.PropertyName ?? PropertyName;
Mapping = discriminator?.Mapping != null ? new Dictionary<string, OpenApiSchemaReference>(discriminator.Mapping) : null;
Extensions = discriminator?.Extensions != null ? new Dictionary<string, IOpenApiExtension>(discriminator.Extensions) : null;
DefaultMapping = discriminator?.DefaultMapping;
}

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

// Write defaultMapping property in 3.2.0
if (DefaultMapping != null)
{
writer.WritePropertyName("defaultMapping");
DefaultMapping.SerializeAsV32(writer);
}

// extensions
writer.WriteExtensions(Extensions, OpenApiSpecVersion.OpenApi3_2);

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

// Write as x-oas-default-mapping extension in 3.1.0
if (DefaultMapping != null)
{
writer.WritePropertyName("x-oas-default-mapping");
DefaultMapping.SerializeAsV31(writer);
}

// extensions
writer.WriteExtensions(Extensions, OpenApiSpecVersion.OpenApi3_1);

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

// Write as x-oas-default-mapping extension in 3.0.0
if (DefaultMapping != null)
{
writer.WritePropertyName("x-oas-default-mapping");
DefaultMapping.SerializeAsV3(writer);
}

writer.WriteEndObject();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,16 @@ internal static partial class OpenApiV3Deserializer
}
};

private static readonly PatternFieldMap<OpenApiDiscriminator> _discriminatorPatternFields = new();
private static readonly PatternFieldMap<OpenApiDiscriminator> _discriminatorPatternFields = new() {
{s => s.StartsWith(OpenApiConstants.ExtensionFieldNamePrefix, StringComparison.OrdinalIgnoreCase), (o, p, n, doc) => {
// Handle x-oas-default-mapping as DefaultMapping property
if (p.Equals("x-oas-default-mapping", StringComparison.OrdinalIgnoreCase))
{
o.DefaultMapping = LoadMapping(n, doc);
}
}
}
};

public static OpenApiDiscriminator LoadDiscriminator(ParseNode node, OpenApiDocument hostDocument)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,18 @@ internal static partial class OpenApiV31Deserializer
private static readonly PatternFieldMap<OpenApiDiscriminator> _discriminatorPatternFields =
new()
{
{s => s.StartsWith(OpenApiConstants.ExtensionFieldNamePrefix, StringComparison.OrdinalIgnoreCase), (o, p, n, _) => o.AddExtension(p, LoadExtension(p,n))}
{s => s.StartsWith(OpenApiConstants.ExtensionFieldNamePrefix, StringComparison.OrdinalIgnoreCase), (o, p, n, doc) => {
// Handle x-oas-default-mapping as DefaultMapping property
if (p.Equals("x-oas-default-mapping", StringComparison.OrdinalIgnoreCase))
{
o.DefaultMapping = LoadMapping(n, doc);
}
else
{
o.AddExtension(p, LoadExtension(p, n));
}
}
}
};

public static OpenApiDiscriminator LoadDiscriminator(ParseNode node, OpenApiDocument hostDocument)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using System;
using System;

namespace Microsoft.OpenApi.Reader.V32
{
Expand All @@ -22,6 +22,12 @@ internal static partial class OpenApiV32Deserializer
{
o.Mapping = n.CreateSimpleMap((node) => LoadMapping(node, doc));
}
},
{
"defaultMapping", (o, n, doc) =>
{
o.DefaultMapping = LoadMapping(n, doc);
}
}
};

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.

using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
using Microsoft.OpenApi.Reader;
using Xunit;

namespace Microsoft.OpenApi.Readers.Tests.V31Tests
{
[Collection("DefaultSettings")]
public class OpenApiDiscriminatorTests
{
private const string SampleFolderPath = "V31Tests/Samples/OpenApiDiscriminator/";

[Fact]
public async Task ParseBasicDiscriminatorShouldSucceed()
{
// Arrange
using var stream = Resources.GetStream(Path.Combine(SampleFolderPath, "basicDiscriminator.yaml"));
// Copy stream to MemoryStream
using var memoryStream = new MemoryStream();
await stream.CopyToAsync(memoryStream);
memoryStream.Position = 0;

// Act
var openApiDocument = new OpenApiDocument();
var discriminator = OpenApiModelFactory.Load<OpenApiDiscriminator>(memoryStream, OpenApiSpecVersion.OpenApi3_1, OpenApiConstants.Yaml, openApiDocument, out var diagnostic, SettingsFixture.ReaderSettings);

// Assert
Assert.Equivalent(
new OpenApiDiscriminator
{
PropertyName = "pet_type",
Mapping = new Dictionary<string, OpenApiSchemaReference>
{
["puppy"] = new OpenApiSchemaReference("Dog", openApiDocument),
["kitten"] = new OpenApiSchemaReference("Cat" , openApiDocument, "https://gigantic-server.com/schemas/animals.json"),
["monster"] = new OpenApiSchemaReference("monster" , openApiDocument, "https://gigantic-server.com/schemas/Monster/schema.json")
},
DefaultMapping = new OpenApiSchemaReference("Animal", openApiDocument)
}, discriminator);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
propertyName: pet_type
mapping:
puppy: '#/components/schemas/Dog'
kitten: https://gigantic-server.com/schemas/animals.json#/components/schemas/Cat
monster: https://gigantic-server.com/schemas/Monster/schema.json#/components/schemas/monster
x-oas-default-mapping: '#/components/schemas/Animal'
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.

using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
using Microsoft.OpenApi.Reader;
using Xunit;

namespace Microsoft.OpenApi.Readers.Tests.V32Tests
{
[Collection("DefaultSettings")]
public class OpenApiDiscriminatorTests
{
private const string SampleFolderPath = "V32Tests/Samples/OpenApiDiscriminator/";

[Fact]
public async Task ParseBasicDiscriminatorShouldSucceed()
{
// Arrange
using var stream = Resources.GetStream(Path.Combine(SampleFolderPath, "basicDiscriminator.yaml"));
// Copy stream to MemoryStream
using var memoryStream = new MemoryStream();
await stream.CopyToAsync(memoryStream);
memoryStream.Position = 0;

// Act
var openApiDocument = new OpenApiDocument();
var discriminator = OpenApiModelFactory.Load<OpenApiDiscriminator>(memoryStream, OpenApiSpecVersion.OpenApi3_2, OpenApiConstants.Yaml, openApiDocument, out var diagnostic, SettingsFixture.ReaderSettings);

// Assert
Assert.Equivalent(
new OpenApiDiscriminator
{
PropertyName = "pet_type",
Mapping = new Dictionary<string, OpenApiSchemaReference>
{
["puppy"] = new OpenApiSchemaReference("Dog", openApiDocument),
["kitten"] = new OpenApiSchemaReference("Cat" , openApiDocument, "https://gigantic-server.com/schemas/animals.json"),
["monster"] = new OpenApiSchemaReference("monster" , openApiDocument, "https://gigantic-server.com/schemas/Monster/schema.json")
},
DefaultMapping = new OpenApiSchemaReference("Animal", openApiDocument)
}, discriminator);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
propertyName: pet_type
mapping:
puppy: '#/components/schemas/Dog'
kitten: https://gigantic-server.com/schemas/animals.json#/components/schemas/Cat
monster: https://gigantic-server.com/schemas/Monster/schema.json#/components/schemas/monster
defaultMapping: '#/components/schemas/Animal'
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,8 @@ public async Task ParseBasicDiscriminatorShouldSucceed()
["puppy"] = new OpenApiSchemaReference("Dog", openApiDocument),
["kitten"] = new OpenApiSchemaReference("Cat" , openApiDocument, "https://gigantic-server.com/schemas/animals.json"),
["monster"] = new OpenApiSchemaReference("schema.json" , openApiDocument, "https://gigantic-server.com/schemas/Monster/schema.json")
}
},
DefaultMapping = new OpenApiSchemaReference("Animal", openApiDocument)
}, discriminator);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@
mapping:
puppy: '#/components/schemas/Dog'
kitten: https://gigantic-server.com/schemas/animals.json#/components/schemas/Cat
monster: https://gigantic-server.com/schemas/Monster/schema.json
monster: https://gigantic-server.com/schemas/Monster/schema.json
x-oas-default-mapping: '#/components/schemas/Animal'
Original file line number Diff line number Diff line change
Expand Up @@ -594,6 +594,7 @@ namespace Microsoft.OpenApi
{
public OpenApiDiscriminator() { }
public OpenApiDiscriminator(Microsoft.OpenApi.OpenApiDiscriminator discriminator) { }
public Microsoft.OpenApi.OpenApiSchemaReference? DefaultMapping { get; set; }
public System.Collections.Generic.IDictionary<string, Microsoft.OpenApi.IOpenApiExtension>? Extensions { get; set; }
public System.Collections.Generic.IDictionary<string, Microsoft.OpenApi.OpenApiSchemaReference>? Mapping { get; set; }
public string? PropertyName { get; set; }
Expand Down
Loading