[CSHARP] Missing base class parameters in derived class contractor for multi level inheritance scenarios
Description
As inheritance is transitive in C#, it allows you to define an inheritance hierarchy for a set of types. In other words, type D can inherit from type C, which inherits from type B, which inherits from the base class type A. Because inheritance is transitive, the members of type A are available to type D.
Swagger codegen currently only support single level inheritance e.g types from A is available to B but types from A is currently not available to C or D. This cause runtime exceptions when trying to deserialize C if A has mandatory properties even though the values are avialable in the JSON payload.
And unfortunately this is not picked up during a build, unless we add integration test to deserialise D and C for scenarios where A and B has mandatory properties.
Swagger-codegen version
3.0.35
Swagger declaration file content or url
I prefer to use examples that are close to the real world, so rather then using type A, B, C & D I have created a sample schema to replicate this issue using hotel property models. See below table for a relationship link between the scenairo described above and the schema. Basically the PropertyDetail model in the inheritance example above is of type C etc.
| Type | Model Name |
|---|---|
| A | PropertyID |
| B | Property |
| C | PropertyDetail |
| D | PropertyFullDetail |
openapi: 3.0.0
info:
version: '1.0'
title: Test
paths:
/search:
get:
responses:
200:
description: OK - Successful
content:
application/json:
schema:
$ref: '#/components/schemas/PropertyDetail'
components:
schemas:
PropertyID:
required:
- '@type'
- PropertyKey
properties:
'@type':
type: string
id:
type: string
Property:
allOf:
- $ref: '#/components/schemas/PropertyID'
- required:
- name
properties:
name:
maxLength: 128
type: string
description: Property name
description:
maxItems: 5
type: array
items:
type: string
PropertyDetail:
allOf:
- $ref: '#/components/schemas/Property'
- required:
- address
properties:
address:
maxItems: 5
minItems: 1
type: array
items:
maxLength: 128
type: string
telephone:
maxItems: 5
minItems: 1
type: array
items:
maxLength: 128
type: string
PropertyFullDetail:
allOf:
- $ref: '#/components/schemas/PropertyDetail'
- required:
- address
properties:
rating:
type: number
images:
type: array
items:
maxLength: 128
type: string
Command line used for generation
java -jar swagger-codegen-cli-3.0.35.jar generate -l csharp -i "path-to-spec"
Steps to reproduce
- Generate the C# files using the command line above
- Build the project
- Add below code to the PropertyFullDetailInstanceTest method in PropertyFullDetailTest.cs
/// <summary>
/// Test an instance of PropertyFullDetail
/// </summary>
[Test]
public void PropertyFullDetailInstanceTest()
{
var json = "{\"name\": \"Hilton\",\"address\": [\"50 Hilton Avenue\"],\"description\": [\"Testing\"],\"@type\": \"PropertyFullDetail\",\"id\": \"HI001\"}";
var instance = JsonConvert.DeserializeObject<PropertyFullDetail>(json);
}
- The test will fail with "System.IO.InvalidDataException : type is a required property for PropertyID and cannot be null".
Please note that type is set in the JSON payload, it is just ignored by the PropertyFullDetail constructor.
Actual result
Example of the generated PropertyDetail and PropertyFullDetail constructor.
PropertyDetail (type C) is missing type and id from the base class (type A) and PropertyFullDetail (type D) is missing name and description (from type B) and also type and id (from type A).
public PropertyDetail(List<string> address = default(List<string>), List<string> telephone = default(List<string>), string name = default(string), List<string> description = default(List<string>)) : base(name, description)
public PropertyFullDetail(decimal? rating = default(decimal?), List<string> images = default(List<string>), List<string> address = default(List<string>), List<string> telephone = default(List<string>)) : base(address, telephone)
Expected result
Example of expected PropertyDetail and PropertyFullDetail constructor with parameters inherited from the base class.
public PropertyDetail(List<string> address = default(List<string>), List<string> telephone = default(List<string>), string name = default(string), List<string> description = default(List<string>), string type = default(string), string id = default(string)) : base(name, description, type, id)
public PropertyFullDetail(decimal? rating = default(decimal?), List<string> images = default(List<string>), List<string> address = default(List<string>), List<string> telephone = default(List<string>), string name = default(string), List<string> description = default(List<string>), string type = default(string), string id = default(string)) : base(address, telephone, name, description, type, id)
Related issues/PRs
Unable to find one, sorry! :upside_down_face:
Suggest a fix/enhancement
If a base class has mandatory properties, any derived class in a multi level inheritance hierarchy will cause runtime deserialization exceptions.
This is critical issue for the project that I am currently working on that has a schema that generates over 400 models. So far I have identified 8 models that experience this issue. But I have not received payload samples from the vendor to get code coverage for all models.
I currently manually edit the constructors after generating my classes, but I am worried about the classes that we are not discovering during the development or UAT phase of the project that might cause runtime exceptions in production later on.