From 6f37409d2be846d542e37ea496ac0c807a7c7cf5 Mon Sep 17 00:00:00 2001
From: Noah Fontes
Date: Thu, 10 Mar 2022 00:31:44 -0800
Subject: [PATCH] Fix nullable models embedded in allOf (#11804)
It is possible, though contrived, for a property to have both isAnyType
and isModel set. In this case, when the outer type is nullable, the Go
template expects a Nullable* type to be provided by the generator, but
it is skipped over instead. This change aligns the generator with the
template's expectations.
---
.../openapitools/codegen/CodegenProperty.java | 8 +++--
.../codegen/languages/AbstractGoCodegen.java | 3 +-
.../codegen/languages/GoClientCodegen.java | 4 +--
.../codegen/go/GoClientCodegenTest.java | 17 ++++++++++
.../test/resources/3_0/allOf_nullable.yaml | 33 +++++++++++++++++++
.../handler/PathHandlerInterface.java | 4 +--
6 files changed, 61 insertions(+), 8 deletions(-)
create mode 100644 modules/openapi-generator/src/test/resources/3_0/allOf_nullable.yaml
diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/CodegenProperty.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/CodegenProperty.java
index d184d39b4d2..ff9384cb17d 100644
--- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/CodegenProperty.java
+++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/CodegenProperty.java
@@ -917,6 +917,7 @@ public class CodegenProperty implements Cloneable, IJsonSchemaValidationProperti
sb.append(", isArray=").append(isArray);
sb.append(", isMap=").append(isMap);
sb.append(", isEnum=").append(isEnum);
+ sb.append(", isAnyType=").append(isAnyType);
sb.append(", isReadOnly=").append(isReadOnly);
sb.append(", isWriteOnly=").append(isWriteOnly);
sb.append(", isNullable=").append(isNullable);
@@ -995,6 +996,7 @@ public class CodegenProperty implements Cloneable, IJsonSchemaValidationProperti
isArray == that.isArray &&
isMap == that.isMap &&
isEnum == that.isEnum &&
+ isAnyType == that.isAnyType &&
isReadOnly == that.isReadOnly &&
isWriteOnly == that.isWriteOnly &&
isNullable == that.isNullable &&
@@ -1068,9 +1070,9 @@ public class CodegenProperty implements Cloneable, IJsonSchemaValidationProperti
hasMoreNonReadOnly, isPrimitiveType, isModel, isContainer, isString, isNumeric,
isInteger, isLong, isNumber, isFloat, isDouble, isDecimal, isByteArray, isBinary, isFile,
isBoolean, isDate, isDateTime, isUuid, isUri, isEmail, isFreeFormObject,
- isArray, isMap, isEnum, isReadOnly, isWriteOnly, isNullable, isShort, isUnboundedInteger,
- isSelfReference, isCircularReference, isDiscriminator, _enum, allowableValues,
- items, mostInnerItems, additionalProperties, vars, requiredVars,
+ isArray, isMap, isEnum, isAnyType, isReadOnly, isWriteOnly, isNullable, isShort,
+ isUnboundedInteger, isSelfReference, isCircularReference, isDiscriminator, _enum,
+ allowableValues, items, mostInnerItems, additionalProperties, vars, requiredVars,
vendorExtensions, hasValidation, isInherited, discriminatorValue, nameInCamelCase,
nameInSnakeCase, enumName, maxItems, minItems, isXmlAttribute, xmlPrefix, xmlName,
xmlNamespace, isXmlWrapped, isNull, additionalPropertiesIsAnyType, hasVars, hasRequired,
diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractGoCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractGoCodegen.java
index a071f2f4a80..353c5f3a2db 100644
--- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractGoCodegen.java
+++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractGoCodegen.java
@@ -601,7 +601,8 @@ public abstract class AbstractGoCodegen extends DefaultCodegen implements Codege
// For primitive types and custom types (e.g. interface{}, map[string]interface{}...),
// the generated code has a wrapper type and a Get() function to access the underlying type.
// For containers (e.g. Array, Map), the generated code returns the type directly.
- if (property.isContainer || property.isFreeFormObject || property.isAnyType) {
+ if (property.isContainer || property.isFreeFormObject
+ || (property.isAnyType && !property.isModel)) {
property.vendorExtensions.put("x-golang-is-container", true);
}
}
diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/GoClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/GoClientCodegen.java
index 439dc4c8f9b..4dd0b8cbba2 100644
--- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/GoClientCodegen.java
+++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/GoClientCodegen.java
@@ -403,8 +403,8 @@ public class GoClientCodegen extends AbstractGoCodegen {
for (CodegenProperty param : Iterables.concat(model.vars, model.allVars, model.requiredVars, model.optionalVars)) {
param.vendorExtensions.put("x-go-base-type", param.dataType);
- if (!param.isNullable || param.isMap || param.isArray ||
- param.isFreeFormObject || param.isAnyType) {
+ if (!param.isNullable || param.isContainer || param.isFreeFormObject
+ || (param.isAnyType && !param.isModel)) {
continue;
}
if (param.isDateTime) {
diff --git a/modules/openapi-generator/src/test/java/org/openapitools/codegen/go/GoClientCodegenTest.java b/modules/openapi-generator/src/test/java/org/openapitools/codegen/go/GoClientCodegenTest.java
index 2412d5fdbb8..3de21addf61 100644
--- a/modules/openapi-generator/src/test/java/org/openapitools/codegen/go/GoClientCodegenTest.java
+++ b/modules/openapi-generator/src/test/java/org/openapitools/codegen/go/GoClientCodegenTest.java
@@ -141,4 +141,21 @@ public class GoClientCodegenTest {
TestUtils.assertFileNotContains(modelFile, "int32 *int32");
TestUtils.assertFileNotContains(modelFile, "dst.int32");
}
+
+ @Test
+ public void testNullableComposition() throws IOException {
+ File output = Files.createTempDirectory("test").toFile();
+ output.deleteOnExit();
+
+ final CodegenConfigurator configurator = new CodegenConfigurator()
+ .setGeneratorName("go")
+ .setInputSpec("src/test/resources/3_0/allOf_nullable.yaml")
+ .setOutputDir(output.getAbsolutePath().replace("\\", "/"));
+
+ DefaultGenerator generator = new DefaultGenerator();
+ List files = generator.opts(configurator.toClientOptInput()).generate();
+ files.forEach(File::deleteOnExit);
+
+ TestUtils.assertFileContains(Paths.get(output + "/model_example.go"), "Child NullableChild");
+ }
}
diff --git a/modules/openapi-generator/src/test/resources/3_0/allOf_nullable.yaml b/modules/openapi-generator/src/test/resources/3_0/allOf_nullable.yaml
new file mode 100644
index 00000000000..5b201d7833f
--- /dev/null
+++ b/modules/openapi-generator/src/test/resources/3_0/allOf_nullable.yaml
@@ -0,0 +1,33 @@
+openapi: 3.0.1
+info:
+ version: 1.0.0
+ title: Example
+ license:
+ name: MIT
+servers:
+ - url: http://api.example.xyz/v1
+paths:
+ /example:
+ get:
+ operationId: list
+ responses:
+ '200':
+ description: OK
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Example"
+components:
+ schemas:
+ Child:
+ type: object
+ properties:
+ name:
+ type: string
+ Example:
+ type: object
+ properties:
+ child:
+ nullable: true
+ allOf:
+ - $ref: '#/components/schemas/Child'
diff --git a/samples/server/petstore/java-undertow/src/main/java/org/openapitools/handler/PathHandlerInterface.java b/samples/server/petstore/java-undertow/src/main/java/org/openapitools/handler/PathHandlerInterface.java
index 3f538dd91f3..00ff133c1c4 100644
--- a/samples/server/petstore/java-undertow/src/main/java/org/openapitools/handler/PathHandlerInterface.java
+++ b/samples/server/petstore/java-undertow/src/main/java/org/openapitools/handler/PathHandlerInterface.java
@@ -539,10 +539,10 @@ public interface PathHandlerInterface {
* Response headers: [CodegenProperty{openApiType='integer', baseName='X-Rate-Limit', complexType='null', getter='getxRateLimit', setter='setxRateLimit', description='calls per hour allowed by the user', dataType='Integer', datatypeWithEnum='Integer', dataFormat='int32', name='xRateLimit', min='null', max='null', defaultValue='null', defaultValueWithParam=' = data.X-Rate-Limit;', baseType='Integer', containerType='null', title='null', unescapedDescription='calls per hour allowed by the user', maxLength=null, minLength=null, pattern='null', example='null', jsonSchema='{
"type" : "integer",
"format" : "int32"
-}', minimum='null', maximum='null', exclusiveMinimum=false, exclusiveMaximum=false, required=false, deprecated=false, hasMoreNonReadOnly=false, isPrimitiveType=true, isModel=false, isContainer=false, isString=false, isNumeric=true, isInteger=true, isShort=true, 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, isFreeFormObject=false, isArray=false, isMap=false, isEnum=false, isReadOnly=false, isWriteOnly=false, isNullable=false, isSelfReference=false, isCircularReference=false, isDiscriminator=false, _enum=null, allowableValues=null, items=null, additionalProperties=null, vars=[], requiredVars=[], mostInnerItems=null, vendorExtensions={}, hasValidation=false, isInherited=false, discriminatorValue='null', nameInCamelCase='XRateLimit', nameInSnakeCase='X_RATE_LIMIT', enumName='null', maxItems=null, minItems=null, maxProperties=null, minProperties=null, uniqueItems=false, multipleOf=null, isXmlAttribute=false, xmlPrefix='null', xmlName='null', xmlNamespace='null', isXmlWrapped=false, isNull=false, getAdditionalPropertiesIsAnyType=false, getHasVars=false, getHasRequired=false, getHasDiscriminatorWithNonEmptyMapping=false, composedSchemas=null, hasMultipleTypes=false}, CodegenProperty{openApiType='string', baseName='X-Expires-After', complexType='Date', getter='getxExpiresAfter', setter='setxExpiresAfter', description='date in UTC when token expires', dataType='Date', datatypeWithEnum='Date', dataFormat='date-time', name='xExpiresAfter', min='null', max='null', defaultValue='null', defaultValueWithParam=' = data.X-Expires-After;', baseType='Date', containerType='null', title='null', unescapedDescription='date in UTC when token expires', maxLength=null, minLength=null, pattern='null', example='null', jsonSchema='{
+}', minimum='null', maximum='null', exclusiveMinimum=false, exclusiveMaximum=false, required=false, deprecated=false, hasMoreNonReadOnly=false, isPrimitiveType=true, isModel=false, isContainer=false, isString=false, isNumeric=true, isInteger=true, isShort=true, 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, isFreeFormObject=false, isArray=false, isMap=false, isEnum=false, isAnyType=false, isReadOnly=false, isWriteOnly=false, isNullable=false, isSelfReference=false, isCircularReference=false, isDiscriminator=false, _enum=null, allowableValues=null, items=null, additionalProperties=null, vars=[], requiredVars=[], mostInnerItems=null, vendorExtensions={}, hasValidation=false, isInherited=false, discriminatorValue='null', nameInCamelCase='XRateLimit', nameInSnakeCase='X_RATE_LIMIT', enumName='null', maxItems=null, minItems=null, maxProperties=null, minProperties=null, uniqueItems=false, multipleOf=null, isXmlAttribute=false, xmlPrefix='null', xmlName='null', xmlNamespace='null', isXmlWrapped=false, isNull=false, getAdditionalPropertiesIsAnyType=false, getHasVars=false, getHasRequired=false, getHasDiscriminatorWithNonEmptyMapping=false, composedSchemas=null, hasMultipleTypes=false}, CodegenProperty{openApiType='string', baseName='X-Expires-After', complexType='Date', getter='getxExpiresAfter', setter='setxExpiresAfter', description='date in UTC when token expires', dataType='Date', datatypeWithEnum='Date', dataFormat='date-time', name='xExpiresAfter', min='null', max='null', defaultValue='null', defaultValueWithParam=' = data.X-Expires-After;', baseType='Date', containerType='null', title='null', unescapedDescription='date in UTC when token expires', maxLength=null, minLength=null, pattern='null', example='null', jsonSchema='{
"type" : "string",
"format" : "date-time"
-}', minimum='null', maximum='null', exclusiveMinimum=false, exclusiveMaximum=false, required=false, deprecated=false, hasMoreNonReadOnly=false, isPrimitiveType=false, isModel=false, 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=true, isUuid=false, isUri=false, isEmail=false, isFreeFormObject=false, isArray=false, isMap=false, isEnum=false, isReadOnly=false, isWriteOnly=false, isNullable=false, isSelfReference=false, isCircularReference=false, isDiscriminator=false, _enum=null, allowableValues=null, items=null, additionalProperties=null, vars=[], requiredVars=[], mostInnerItems=null, vendorExtensions={}, hasValidation=false, isInherited=false, discriminatorValue='null', nameInCamelCase='XExpiresAfter', nameInSnakeCase='X_EXPIRES_AFTER', enumName='null', maxItems=null, minItems=null, maxProperties=null, minProperties=null, uniqueItems=false, multipleOf=null, isXmlAttribute=false, xmlPrefix='null', xmlName='null', xmlNamespace='null', isXmlWrapped=false, isNull=false, getAdditionalPropertiesIsAnyType=false, getHasVars=false, getHasRequired=false, getHasDiscriminatorWithNonEmptyMapping=false, composedSchemas=null, hasMultipleTypes=false}]
+}', minimum='null', maximum='null', exclusiveMinimum=false, exclusiveMaximum=false, required=false, deprecated=false, hasMoreNonReadOnly=false, isPrimitiveType=false, isModel=false, 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=true, isUuid=false, isUri=false, isEmail=false, isFreeFormObject=false, isArray=false, isMap=false, isEnum=false, isAnyType=false, isReadOnly=false, isWriteOnly=false, isNullable=false, isSelfReference=false, isCircularReference=false, isDiscriminator=false, _enum=null, allowableValues=null, items=null, additionalProperties=null, vars=[], requiredVars=[], mostInnerItems=null, vendorExtensions={}, hasValidation=false, isInherited=false, discriminatorValue='null', nameInCamelCase='XExpiresAfter', nameInSnakeCase='X_EXPIRES_AFTER', enumName='null', maxItems=null, minItems=null, maxProperties=null, minProperties=null, uniqueItems=false, multipleOf=null, isXmlAttribute=false, xmlPrefix='null', xmlName='null', xmlNamespace='null', isXmlWrapped=false, isNull=false, getAdditionalPropertiesIsAnyType=false, getHasVars=false, getHasRequired=false, getHasDiscriminatorWithNonEmptyMapping=false, composedSchemas=null, hasMultipleTypes=false}]
*
* Produces: [{mediaType=application/xml}, {mediaType=application/json}]
* Returns: {@link String}