Skip to content

Commit

Permalink
Fix UseOneOfForPolymorphism (domaindrivendev#3155)
Browse files Browse the repository at this point in the history
Fix second+ level inheritance for `UseAllOf`.
  • Loading branch information
k0ka authored Nov 22, 2024
1 parent bb49884 commit b8e1f0f
Show file tree
Hide file tree
Showing 9 changed files with 493 additions and 12 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -105,16 +105,19 @@ private static string AnnotationsDiscriminatorNameSelector(Type baseType)

private static string AnnotationsDiscriminatorValueSelector(Type subType)
{
if (subType.BaseType == null)
return null;
var baseType = subType.BaseType;
while (baseType != null)
{
var subTypeAttribute = baseType.GetCustomAttributes(false)
.OfType<SwaggerSubTypeAttribute>()
.FirstOrDefault(attr => attr.SubType == subType);

var subTypeAttribute = subType.BaseType.GetCustomAttributes(false)
.OfType<SwaggerSubTypeAttribute>()
.FirstOrDefault(attr => attr.SubType == subType);
if (subTypeAttribute != null)
{
return subTypeAttribute.DiscriminatorValue;
}

if (subTypeAttribute != null)
{
return subTypeAttribute.DiscriminatorValue;
baseType = baseType.BaseType;
}

return null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -475,12 +475,18 @@ private bool IsKnownSubType(DataContract dataContract, out DataContract baseType
baseTypeDataContract = null;

var baseType = dataContract.UnderlyingType.BaseType;
while (baseType != null && baseType != typeof(object))
{
if (_generatorOptions.SubTypesSelector(baseType).Contains(dataContract.UnderlyingType))
{
baseTypeDataContract = GetDataContractFor(baseType);
return true;
}

if (baseType == null || baseType == typeof(object) || !_generatorOptions.SubTypesSelector(baseType).Contains(dataContract.UnderlyingType))
return false;
baseType = baseType.BaseType;
}

baseTypeDataContract = GetDataContractFor(baseType);
return true;
return false;
}

private bool TryGetDiscriminatorFor(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,69 @@
}
}
}
},
"/SecondLevel": {
"post": {
"tags": [
"SecondLevel"
],
"requestBody": {
"content": {
"application/json": {
"schema": {
"oneOf": [
{
"$ref": "#/components/schemas/SubSubType"
}
]
}
},
"text/json": {
"schema": {
"oneOf": [
{
"$ref": "#/components/schemas/SubSubType"
}
]
}
},
"application/*+json": {
"schema": {
"oneOf": [
{
"$ref": "#/components/schemas/SubSubType"
}
]
}
}
}
},
"responses": {
"200": {
"description": "OK",
"content": {
"text/plain": {
"schema": {
"type": "integer",
"format": "int32"
}
},
"application/json": {
"schema": {
"type": "integer",
"format": "int32"
}
},
"text/json": {
"schema": {
"type": "integer",
"format": "int32"
}
}
}
}
}
}
}
},
"components": {
Expand Down Expand Up @@ -96,6 +159,28 @@
],
"type": "string"
},
"BaseType": {
"required": [
"discriminator"
],
"type": "object",
"properties": {
"discriminator": {
"type": "string"
},
"property": {
"type": "string",
"nullable": true
}
},
"additionalProperties": false,
"discriminator": {
"propertyName": "discriminator",
"mapping": {
"SubSubType": "#/components/schemas/SubSubType"
}
}
},
"Cat": {
"allOf": [
{
Expand Down Expand Up @@ -129,6 +214,23 @@
"additionalProperties": false
}
]
},
"SubSubType": {
"allOf": [
{
"$ref": "#/components/schemas/BaseType"
},
{
"type": "object",
"properties": {
"property2": {
"type": "string",
"nullable": true
}
},
"additionalProperties": false
}
]
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -584,6 +584,29 @@ public void GenerateSchema_SupportsOption_SubTypesSelector()
var schema = subject.GenerateSchema(typeof(BaseType), schemaRepository);

Assert.Equal(new[] { "SubType1", "BaseType" }, schemaRepository.Schemas.Keys);

var subSchema = schemaRepository.Schemas["SubType1"];
Assert.NotNull(subSchema.AllOf);
Assert.Equal(2, subSchema.AllOf.Count);
}

[Fact]
public void GenerateSchema_SecondLevelInheritance_SubTypesSelector()
{
var subject = Subject(configureGenerator: c =>
{
c.UseAllOfForInheritance = true;
c.SubTypesSelector = (type) => type == typeof(BaseSecondLevelType) ? new[] { typeof(SubSubSecondLevelType) } : Array.Empty<Type>();
});
var schemaRepository = new SchemaRepository();

var schema = subject.GenerateSchema(typeof(BaseSecondLevelType), schemaRepository);

Assert.Equal(new[] { "SubSubSecondLevelType", "BaseSecondLevelType" }, schemaRepository.Schemas.Keys);

var subSchema = schemaRepository.Schemas["SubSubSecondLevelType"];
Assert.NotNull(subSchema.AllOf);
Assert.Equal(2, subSchema.AllOf.Count);
}

[Fact]
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
namespace Swashbuckle.AspNetCore.TestSupport
{
public class BaseSecondLevelType
{
public string BaseProperty { get; set; }
}

public class SubSecondLevelType : BaseSecondLevelType
{
public int Property1 { get; set; }
}

public class SubSubSecondLevelType : SubSecondLevelType
{
public int Property2 { get; set; }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
using System;
using Microsoft.AspNetCore.Mvc;
using Swashbuckle.AspNetCore.Annotations;

namespace NswagClientExample.Controllers
{
[ApiController]
[Route("[controller]")]
public class SecondLevelController : ControllerBase
{
[HttpPost]
public int Create([FromBody]BaseType input)
{
throw new NotImplementedException();
}
}

[SwaggerDiscriminator("discriminator")]
[SwaggerSubType(typeof(SubSubType), DiscriminatorValue = nameof(SubSubType))]
public abstract class BaseType
{
public string Property { get; set; }
}

public abstract class SubType : BaseType
{
}

public class SubSubType : SubType
{
public string Property2 { get; set; }
}
}
99 changes: 99 additions & 0 deletions test/WebSites/NswagClientExample/swagger_net6.0.json
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,69 @@
}
}
}
},
"/SecondLevel": {
"post": {
"tags": [
"SecondLevel"
],
"requestBody": {
"content": {
"application/json": {
"schema": {
"oneOf": [
{
"$ref": "#/components/schemas/SubSubType"
}
]
}
},
"text/json": {
"schema": {
"oneOf": [
{
"$ref": "#/components/schemas/SubSubType"
}
]
}
},
"application/*+json": {
"schema": {
"oneOf": [
{
"$ref": "#/components/schemas/SubSubType"
}
]
}
}
}
},
"responses": {
"200": {
"description": "OK",
"content": {
"text/plain": {
"schema": {
"type": "integer",
"format": "int32"
}
},
"application/json": {
"schema": {
"type": "integer",
"format": "int32"
}
},
"text/json": {
"schema": {
"type": "integer",
"format": "int32"
}
}
}
}
}
}
}
},
"components": {
Expand Down Expand Up @@ -101,6 +164,25 @@
],
"type": "string"
},
"BaseType": {
"required": [
"discriminator"
],
"type": "object",
"properties": {
"discriminator": {
"type": "string"
},
"property": {
"type": "string",
"nullable": true
}
},
"additionalProperties": false,
"discriminator": {
"propertyName": "discriminator"
}
},
"Cat": {
"allOf": [
{
Expand Down Expand Up @@ -134,6 +216,23 @@
"additionalProperties": false
}
]
},
"SubSubType": {
"allOf": [
{
"$ref": "#/components/schemas/BaseType"
},
{
"type": "object",
"properties": {
"property2": {
"type": "string",
"nullable": true
}
},
"additionalProperties": false
}
]
}
}
}
Expand Down
Loading

0 comments on commit b8e1f0f

Please sign in to comment.