[BUG] [dart-dio-next] Models inside anyOf missing type information
Created by: josh-burton
Bug Report Checklist
- [x ] Have you provided a full/minimal spec to reproduce the issue?
-
Have you validated the input using an OpenAPI validator (example)? - [x ] Have you tested with the latest master to confirm the issue still exists?
-
Have you searched for related issues/PRs? -
What's the actual output vs expected output? -
[Optional] Sponsorship to speed up the bug fix or feature request (example)
Description
When a model has a parameter of type allOf with only one child type, specific type information about that type is lost and it is parsed as a generic model.
For example if we have a model User with a field type, which is defined as allOf and then a inner type enum:
"User": {
"type": "object",
"properties": {
"type": {
"allOf": [
{
"$ref": "#/components/schemas/UserType"
}
],
"description": "type of user"
}
}
},
"UserType": {
"type": "string",
"title": "UserType",
"enum": [
"admin",
"user"
]
}
The model definition of UserType below has "isEnum": true
, but the variable/field definition of UserType in User model definition has "isEnum": false
and "isModel": true
.
This model is an enum and `"isEnum" should always be true.
Debug model output
``` [ { "importPath": "lib.src.model.User", "model": { "anyOf": [], "oneOf": [], "allOf": [], "anyOfProps": [], "allOfProps": [], "oneOfProps": [], "name": "User", "classname": "User", "classVarName": "user", "modelJson": "{\n \"type\" : \"object\",\n \"properties\" : {\n \"type\" : {\n \"description\" : \"type of user\",\n \"allOf\" : [ {\n \"$ref\" : \"#/components/schemas/UserType\"\n } ]\n }\n }\n}", "dataType": "JsonObject", "classFilename": "user", "isAlias": false, "isString": false, "isInteger": false, "isLong": false, "isNumber": false, "isNumeric": false, "isFloat": false, "isDouble": false, "isDate": false, "isDateTime": false, "isShort": false, "isUnboundedInteger": false, "isBoolean": false, "additionalPropertiesIsAnyType": false, "vars": [ { "openApiType": "UserType", "baseName": "type", "complexType": "UserType", "getter": "getType", "setter": "setType", "description": "type of user", "dataType": "UserType", "datatypeWithEnum": "UserType", "name": "type", "defaultValueWithParam": " = data.type;", "baseType": "UserType", "unescapedDescription": "type of user", "example": "null", "jsonSchema": "{\n \"description\" : \"type of user\",\n \"allOf\" : [ {\n \"$ref\" : \"#/components/schemas/UserType\"\n } ]\n}", "exclusiveMinimum": false, "exclusiveMaximum": false, "required": false, "deprecated": false, "hasMoreNonReadOnly": false, "isPrimitiveType": false, "isModel": true, "isContainer": false, "isString": false, "isNumeric": false, "isInteger": false, "isShort": false, "isLong": false, "isUnboundedInteger": false, "isNumber": false, "isFloat": false, "isDouble": false, "isDecimal": false, "isByteArray": false, "isBinary": false, "isFile": false, "isBoolean": false, "isDate": false, "isDateTime": false, "isUuid": false, "isUri": false, "isEmail": false, "isNull": false, "isFreeFormObject": false, "isAnyType": true, "isArray": false, "isMap": false, "isEnum": false, "isReadOnly": false, "isWriteOnly": false, "isNullable": true, "isSelfReference": false, "isCircularReference": false, "isDiscriminator": false, "vars": [], "requiredVars": [], "vendorExtensions": { "x-index": 1 }, "hasValidation": false, "isInherited": false, "nameInCamelCase": "Type", "nameInSnakeCase": "TYPE", "uniqueItems": false, "isXmlAttribute": false, "isXmlWrapped": false, "additionalPropertiesIsAnyType": false, "hasVars": false, "hasRequired": false, "hasDiscriminatorWithNonEmptyMapping": false, "datatype": "UserType", "iexclusiveMaximum": false } ], "allVars": [ { "openApiType": "UserType", "baseName": "type", "complexType": "UserType", "getter": "getType", "setter": "setType", "description": "type of user", "dataType": "UserType", "datatypeWithEnum": "UserType", "name": "type", "defaultValueWithParam": " = data.type;", "baseType": "UserType", "unescapedDescription": "type of user", "example": "null", "jsonSchema": "{\n \"description\" : \"type of user\",\n \"allOf\" : [ {\n \"$ref\" : \"#/components/schemas/UserType\"\n } ]\n}", "exclusiveMinimum": false, "exclusiveMaximum": false, "required": false, "deprecated": false, "hasMoreNonReadOnly": false, "isPrimitiveType": false, "isModel": true, "isContainer": false, "isString": false, "isNumeric": false, "isInteger": false, "isShort": false, "isLong": false, "isUnboundedInteger": false, "isNumber": false, "isFloat": false, "isDouble": false, "isDecimal": false, "isByteArray": false, "isBinary": false, "isFile": false, "isBoolean": false, "isDate": false, "isDateTime": false, "isUuid": false, "isUri": false, "isEmail": false, "isNull": false, "isFreeFormObject": false, "isAnyType": true, "isArray": false, "isMap": false, "isEnum": false, "isReadOnly": false, "isWriteOnly": false, "isNullable": true, "isSelfReference": false, "isCircularReference": false, "isDiscriminator": false, "vars": [], "requiredVars": [], "vendorExtensions": { "x-index": 1 }, "hasValidation": false, "isInherited": false, "nameInCamelCase": "Type", "nameInSnakeCase": "TYPE", "uniqueItems": false, "isXmlAttribute": false, "isXmlWrapped": false, "additionalPropertiesIsAnyType": false, "hasVars": false, "hasRequired": false, "hasDiscriminatorWithNonEmptyMapping": false, "datatype": "UserType", "iexclusiveMaximum": false } ], "requiredVars": [], "optionalVars": [ { "openApiType": "UserType", "baseName": "type", "complexType": "UserType", "getter": "getType", "setter": "setType", "description": "type of user", "dataType": "UserType", "datatypeWithEnum": "UserType", "name": "type", "defaultValueWithParam": " = data.type;", "baseType": "UserType", "unescapedDescription": "type of user", "example": "null", "jsonSchema": "{\n \"description\" : \"type of user\",\n \"allOf\" : [ {\n \"$ref\" : \"#/components/schemas/UserType\"\n } ]\n}", "exclusiveMinimum": false, "exclusiveMaximum": false, "required": false, "deprecated": false, "hasMoreNonReadOnly": false, "isPrimitiveType": false, "isModel": true, "isContainer": false, "isString": false, "isNumeric": false, "isInteger": false, "isShort": false, "isLong": false, "isUnboundedInteger": false, "isNumber": false, "isFloat": false, "isDouble": false, "isDecimal": false, "isByteArray": false, "isBinary": false, "isFile": false, "isBoolean": false, "isDate": false, "isDateTime": false, "isUuid": false, "isUri": false, "isEmail": false, "isNull": false, "isFreeFormObject": false, "isAnyType": true, "isArray": false, "isMap": false, "isEnum": false, "isReadOnly": false, "isWriteOnly": false, "isNullable": true, "isSelfReference": false, "isCircularReference": false, "isDiscriminator": false, "vars": [], "requiredVars": [], "vendorExtensions": {}, "hasValidation": false, "isInherited": false, "nameInCamelCase": "Type", "nameInSnakeCase": "TYPE", "uniqueItems": false, "isXmlAttribute": false, "isXmlWrapped": false, "additionalPropertiesIsAnyType": false, "hasVars": false, "hasRequired": false, "hasDiscriminatorWithNonEmptyMapping": false, "datatype": "UserType", "iexclusiveMaximum": false } ], "readOnlyVars": [], "readWriteVars": [ { "openApiType": "UserType", "baseName": "type", "complexType": "UserType", "getter": "getType", "setter": "setType", "description": "type of user", "dataType": "UserType", "datatypeWithEnum": "UserType", "name": "type", "defaultValueWithParam": " = data.type;", "baseType": "UserType", "unescapedDescription": "type of user", "example": "null", "jsonSchema": "{\n \"description\" : \"type of user\",\n \"allOf\" : [ {\n \"$ref\" : \"#/components/schemas/UserType\"\n } ]\n}", "exclusiveMinimum": false, "exclusiveMaximum": false, "required": false, "deprecated": false, "hasMoreNonReadOnly": false, "isPrimitiveType": false, "isModel": true, "isContainer": false, "isString": false, "isNumeric": false, "isInteger": false, "isShort": false, "isLong": false, "isUnboundedInteger": false, "isNumber": false, "isFloat": false, "isDouble": false, "isDecimal": false, "isByteArray": false, "isBinary": false, "isFile": false, "isBoolean": false, "isDate": false, "isDateTime": false, "isUuid": false, "isUri": false, "isEmail": false, "isNull": false, "isFreeFormObject": false, "isAnyType": true, "isArray": false, "isMap": false, "isEnum": false, "isReadOnly": false, "isWriteOnly": false, "isNullable": true, "isSelfReference": false, "isCircularReference": false, "isDiscriminator": false, "vars": [], "requiredVars": [], "vendorExtensions": {}, "hasValidation": false, "isInherited": false, "nameInCamelCase": "Type", "nameInSnakeCase": "TYPE", "uniqueItems": false, "isXmlAttribute": false, "isXmlWrapped": false, "additionalPropertiesIsAnyType": false, "hasVars": false, "hasRequired": false, "hasDiscriminatorWithNonEmptyMapping": false, "datatype": "UserType", "iexclusiveMaximum": false } ], "parentVars": [], "mandatory": [], "allMandatory": [], "imports": [ "package:digitalworkkit_visitor_api/src/model/user_type.dart" ], "hasVars": true, "emptyVars": false, "hasMoreModels": false, "hasEnums": false, "isEnum": false, "hasValidation": false, "isNullable": false, "hasRequired": false, "hasOptional": true, "isArray": false, "hasChildren": false, "isMap": true, "isNull": false, "isDeprecated": false, "hasOnlyReadOnly": false, "vendorExtensions": { "x-has-vars": true }, "isAdditionalPropertiesTrue": false, "uniqueItems": false, "exclusiveMinimum": false, "exclusiveMaximum": false, "isModel": false, "hasDiscriminatorWithNonEmptyMapping": false, "isAnyType": false, "isClassnameSanitized": false } }, { "importPath": "lib.src.model.UserType", "model": { "anyOf": [], "oneOf": [], "allOf": [], "anyOfProps": [], "allOfProps": [], "oneOfProps": [], "name": "UserType", "classname": "UserType", "title": "UserType", "classVarName": "userType", "modelJson": "{\n \"title\" : \"UserType\",\n \"type\" : \"string\",\n \"enum\" : [ \"admin\", \"user\" ]\n}", "dataType": "String", "classFilename": "user_type", "isAlias": false, "isString": true, "isInteger": false, "isLong": false, "isNumber": false, "isNumeric": false, "isFloat": false, "isDouble": false, "isDate": false, "isDateTime": false, "isShort": false, "isUnboundedInteger": false, "isBoolean": false, "additionalPropertiesIsAnyType": false, "vars": [], "allVars": [], "requiredVars": [], "optionalVars": [], "readOnlyVars": [], "readWriteVars": [], "parentVars": [], "allowableValues": { "values": [ "admin", "user" ], "enumVars": [ { "name": "admin", "isString": true, "value": "'admin'" }, { "name": "user", "isString": true, "value": "'user'" } ] }, "mandatory": [], "allMandatory": [], "imports": [], "hasVars": false, "emptyVars": false, "hasMoreModels": false, "hasEnums": false, "isEnum": true, "hasValidation": false, "isNullable": false, "hasRequired": false, "hasOptional": false, "isArray": false, "hasChildren": false, "isMap": false, "isNull": false, "isDeprecated": false, "hasOnlyReadOnly": true, "vendorExtensions": { "x-has-vars": false }, "isAdditionalPropertiesTrue": false, "uniqueItems": false, "exclusiveMinimum": false, "exclusiveMaximum": false, "isModel": false, "hasDiscriminatorWithNonEmptyMapping": false, "isAnyType": false, "isClassnameSanitized": false } } ] ```This causes issues when generating a client with dart-dio-next
, as it will generate deserialization logic that doesn't compile:
switch (key) {
case r'type':
final valueDes = serializers.deserialize(value,
specifiedType: const FullType.nullable(UserType)) as UserType?;
if (valueDes == null) continue;
result.type.replace(valueDes);
break;
}
The line that doesn't compile is result.type.replace(valueDes);
, as the UserType EnumClass doesn't have a replace method.
The replace method is generated there because the generator thinks the UserType field is a model, but it's actually an enum.
openapi-generator version
5.3.0
OpenAPI declaration file content or url
sample json spec: https://gist.github.com/josh-burton/8062bc64212db27e2a2f879ae65c07dd
Generation Details
dart-dio-next with built value
Steps to reproduce
Generate the client, run pub run build_runner build
.
Related issues/PRs
https://github.com/OpenAPITools/openapi-generator/pull/6914
Suggest a fix
I think the true fix for this would be parsing the allOf block correctly. A work around in dart-dio-next would be ok too but I'm not sure how we can work around it when we don't know the actual type of the model.