swagger-api / swagger-core

Examples and server integrations for generating the Swagger API Specification, which enables easy access to your REST API
http://swagger.io
Apache License 2.0
7.37k stars 2.17k forks source link

Inherited property missing from child Dto but present on identical sibling Dto #3312

Open halvorsone opened 4 years ago

halvorsone commented 4 years ago

We are trying to represent a tree of logic operators with Dtos using simple inheritance.

This is essentially what our Dtos look like:

@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "operator")
@JsonSubTypes({ @JsonSubTypes.Type(value = MyAndDto.class, name = "and"),
        @JsonSubTypes.Type(value = MyOrDto.class, name = "or"),
        @JsonSubTypes.Type(value = MyEqualsDto.class, name = "equals") })
public class MyBaseDto {
    public String operator;
}

public class MyEqualsDto extends MyBaseDto {
    public String field;
    public String value;
}

public class MyPartsDto extends MyBaseDto {
    public MyBaseDto[] parts;
}

public class MyAndDto extends MyPartsDto {
}

public class MyOrDto extends MyPartsDto {
}

As you can see, the MyAndDto and MyOrDto are pretty much identical.

However, the generated Schemas (via springdoc /v3/api-docs endpoint - which uses swagger core) look like this:

"MyAndDto":{
    "type":"object",
    "allOf":[
        {
            "$ref":"#/components/schemas/MyBaseDto"
        },
        {
            "type":"object",
            "properties":{
                "parts":{
                    "type":"array",
                    "items":{
                        "$ref":"#/components/schemas/MyBaseDto"
                    }
                }
            }
        }
    ]
},

"MyBaseDto":{
    "type":"object",
    "properties":{
        "operator":{
            "type":"string"
        }
    },
    "discriminator":{
        "propertyName":"operator"
    }
},

"MyEqualsDto":{
    "type":"object",
    "allOf":[
        {
            "$ref":"#/components/schemas/MyBaseDto"
        },
        {
            "type":"object",
            "properties":{
                "field":{
                    "type":"string"
                },
                "value":{
                    "type":"string"
                }
            }
        }
    ]
},

"MyOrDto":{
    "type":"object",
    "allOf":[
        {
            "$ref":"#/components/schemas/MyBaseDto"
        }
    ]
}

For some reason, the MyAndDto generates correctly with the inherited parts property. But, the identical MyOrDto is missing the inherited parts property entirely.

If you switch the order of the @JsonSubTypes annotations:

@JsonSubTypes({ @JsonSubTypes.Type(value = MyOrDto.class, name = "or"),
        @JsonSubTypes.Type(value = MyAndDto.class, name = "and"),
        @JsonSubTypes.Type(value = MyEqualsDto.class, name = "equals") })

Then the MyOrDto generates correctly, and MyAndDto is incomplete.

I tried debugging and quickly got lost, but, to me it looked like the issue might be somewhere in io.swagger.v3.core.jackson.ModelResolver.resolveSubtypes() where it ends up with some recursion going on because MyAndDto and MyOrDto both contain an array of MyBaseDto. Seems like on one of the recursive passes, an incomplete Schema is getting used.

Thank you.

Micky002 commented 3 years ago

I have the same problem but i can't solve it with reordering in the @JsonSubTypes annotation. I have done some reordering in the controller method so there is the method which use the base type first and methods using subtypes comes after that method. Then the generation is correct.

The problem was that a List<BaseType> property was included in a subtype although i belongs to the base type. Then all other subtypes and also the base type miss this property.

raghuraman1 commented 2 years ago

Hi @frantuma, @webron and @HugoMario,

I think I have a fix for this over here -https://github.com/teq-niq/sample/tree/swagger-core-issue3312-better I found multiple issues.

To solve these two issues I used the OpenApiCustomiser() and also extended the SchemaSerializer. Did this last bit by modifying io/swagger/v3/core/util/ObjectMapperFactory.java

I am pretty sure there can be better and more thorough ways to solve these small problems.

While on the subject I do believe it can be useful allowing io.swagger.v3.core.jackson.SchemaSerializer to be extendible in general. If there is any other way to achieve same extensibility pls advice.

I am also copying @bnasslahsen of springdoc project here.

Raghu

Nikos410 commented 2 years ago

Hello,

we are facing the same issue. Is there any news on this? Is there anyone we can do to help?

(FYI @simonerm @digirik @geroschaarmann)

raghuraman1 commented 2 years ago

Hello,

we are facing the same issue. Is there any news on this? Is there anyone we can do to help?

(FYI @SimonErm @Digirik @GeroSchaarmann)

Hi @Nikos410

Did you try the workaround I showed here- https://github.com/teq-niq/sample/tree/swagger-core-issue3312-better. Does it work for you?

Raghu

Nikos410 commented 2 years ago

Hey @raghuraman1, kind of. All properties are present now, but the allOf is missing now for all schemas. All properties are specified as properties of the child Dto directly.

Nikos410 commented 2 years ago

Oh, I just noticed out issue is similar, but not quite the same. I've opened #4234 for out specific issue.

raghuraman1 commented 2 years ago

Hi @Nikos410 Its better than missing properties. I hope you are able to use it for stopgap. I do think its a important bug needing proper solving. R

Hey @raghuraman1, kind of. All properties are present now, but the allOf is missing now for all schemas. All properties are specified as properties of the child Dto directly.

Nikos410 commented 2 years ago

Its better than missing properties

That is very true. It sadly does not help us however. We are using the openapi-generator project for client generation. If the allof reference is missing, the generator does not properly generate the models (The child classes are not children of the base class in the generated model).

Right now we are patching the generates json spec manually before passing it to the client generator.

raghuraman1 commented 2 years ago

@Nikos410 I agree with you. I have seen this problem especially around polymorphism and inheritence in models when its generated as swagger definitions. I realised that hand editing the yaml or json is the best bet till its resolved. I keep advising people to be very careful about the yaml/json. What I do is have a generated json saved somewhere. Hand edit in a copy and do codegen on it.