diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/CodegenModel.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/CodegenModel.java index 05bd2946bac5..4bcb5dd4bf94 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/CodegenModel.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/CodegenModel.java @@ -95,6 +95,7 @@ public class CodegenModel implements IJsonSchemaValidationProperties { public boolean isArray; public boolean hasChildren; public boolean isMap; + public boolean isNull; /** * Indicates the OAS schema specifies "deprecated: true". */ @@ -702,6 +703,16 @@ public void setXmlPrefix(String xmlPrefix) { this.xmlPrefix = xmlPrefix; } + @Override + public boolean getIsNull() { + return isNull; + } + + @Override + public void setIsNull(boolean isNull) { + this.isNull = isNull; + } + @Override public boolean equals(Object o) { if (this == o) return true; @@ -730,6 +741,7 @@ public boolean equals(Object o) { isMap == that.isMap && isDeprecated == that.isDeprecated && hasOnlyReadOnly == that.hasOnlyReadOnly && + isNull == that.isNull && getUniqueItems() == that.getUniqueItems() && getExclusiveMinimum() == that.getExclusiveMinimum() && getExclusiveMaximum() == that.getExclusiveMaximum() && @@ -794,7 +806,7 @@ public int hashCode() { getDescription(), getClassVarName(), getModelJson(), getDataType(), getXmlPrefix(), getXmlNamespace(), getXmlName(), getClassFilename(), getUnescapedDescription(), getDiscriminator(), getDefaultValue(), getArrayModelType(), isAlias, isString, isInteger, isLong, isNumber, isNumeric, isFloat, isDouble, - isDate, isDateTime, + isDate, isDateTime, isNull, getVars(), getAllVars(), getRequiredVars(), getOptionalVars(), getReadOnlyVars(), getReadWriteVars(), getParentVars(), getAllowableValues(), getMandatory(), getAllMandatory(), getImports(), hasVars, isEmptyVars(), hasMoreModels, hasEnums, isEnum, isNullable, hasRequired, hasOptional, isArray, @@ -885,6 +897,7 @@ public String toString() { sb.append(", items='").append(items).append('\''); sb.append(", additionalProperties='").append(additionalProperties).append('\''); sb.append(", isModel='").append(isModel).append('\''); + sb.append(", isNull='").append(isNull); sb.append('}'); return sb.toString(); } diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/CodegenParameter.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/CodegenParameter.java index 39cca3eeb7a5..7c6f80ae8430 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/CodegenParameter.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/CodegenParameter.java @@ -101,6 +101,7 @@ public class CodegenParameter implements IJsonSchemaValidationProperties { public Number multipleOf; private Integer maxProperties; private Integer minProperties; + public boolean isNull; public CodegenParameter copy() { CodegenParameter output = new CodegenParameter(); @@ -147,6 +148,7 @@ public CodegenParameter copy() { output.minimum = this.minimum; output.pattern = this.pattern; output.additionalProperties = this.additionalProperties; + output.isNull = this.isNull; if (this._enum != null) { output._enum = new ArrayList(this._enum); @@ -200,7 +202,7 @@ public CodegenParameter copy() { @Override public int hashCode() { - return Objects.hash(isFormParam, isQueryParam, isPathParam, isHeaderParam, isCookieParam, isBodyParam, isContainer, isCollectionFormatMulti, isPrimitiveType, isModel, isExplode, baseName, paramName, dataType, datatypeWithEnum, dataFormat, collectionFormat, description, unescapedDescription, baseType, defaultValue, enumName, style, example, jsonSchema, isString, isNumeric, isInteger, isLong, isNumber, isFloat, isDouble, isDecimal, isByteArray, isBinary, isBoolean, isDate, isDateTime, isUuid, isUri, isEmail, isFreeFormObject, isAnyType, isArray, isMap, isFile, isEnum, _enum, allowableValues, items, mostInnerItems, additionalProperties, vars, requiredVars, vendorExtensions, hasValidation, getMaxProperties(), getMinProperties(), isNullable, required, getMaximum(), getExclusiveMaximum(), getMinimum(), getExclusiveMinimum(), getMaxLength(), getMinLength(), getPattern(), getMaxItems(), getMinItems(), getUniqueItems(), contentType, multipleOf); + return Objects.hash(isFormParam, isQueryParam, isPathParam, isHeaderParam, isCookieParam, isBodyParam, isContainer, isCollectionFormatMulti, isPrimitiveType, isModel, isExplode, baseName, paramName, dataType, datatypeWithEnum, dataFormat, collectionFormat, description, unescapedDescription, baseType, defaultValue, enumName, style, example, jsonSchema, isString, isNumeric, isInteger, isLong, isNumber, isFloat, isDouble, isDecimal, isByteArray, isBinary, isBoolean, isDate, isDateTime, isUuid, isUri, isEmail, isFreeFormObject, isAnyType, isArray, isMap, isFile, isEnum, _enum, allowableValues, items, mostInnerItems, additionalProperties, vars, requiredVars, vendorExtensions, hasValidation, getMaxProperties(), getMinProperties(), isNullable, required, getMaximum(), getExclusiveMaximum(), getMinimum(), getExclusiveMinimum(), getMaxLength(), getMinLength(), getPattern(), getMaxItems(), getMinItems(), getUniqueItems(), contentType, multipleOf, isNull); } @Override @@ -244,6 +246,7 @@ public boolean equals(Object o) { hasValidation == that.hasValidation && isNullable == that.isNullable && required == that.required && + isNull == that.isNull && getExclusiveMaximum() == that.getExclusiveMaximum() && getExclusiveMinimum() == that.getExclusiveMinimum() && getUniqueItems() == that.getUniqueItems() && @@ -357,6 +360,7 @@ public String toString() { sb.append(", uniqueItems=").append(uniqueItems); sb.append(", contentType=").append(contentType); sb.append(", multipleOf=").append(multipleOf); + sb.append(", isNull=").append(isNull); sb.append('}'); return sb.toString(); } @@ -568,5 +572,15 @@ public List getRequiredVars() { public void setRequiredVars(List requiredVars) { this.requiredVars = requiredVars; } + + @Override + public boolean getIsNull() { + return isNull; + } + + @Override + public void setIsNull(boolean isNull) { + this.isNull = isNull; + } } 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 c3cbfe569250..b956a3ea7e19 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 @@ -132,6 +132,7 @@ public class CodegenProperty implements Cloneable, IJsonSchemaValidationProperti public boolean isUuid; public boolean isUri; public boolean isEmail; + public boolean isNull; /** * The type is a free-form object, i.e. it is a map of string to values with no declared properties. * A OAS free-form schema may include the 'additionalProperties' attribute, which puts a constraint @@ -676,6 +677,16 @@ public void setRequiredVars(List requiredVars) { this.requiredVars = requiredVars; } + @Override + public boolean getIsNull() { + return isNull; + } + + @Override + public void setIsNull(boolean isNull) { + this.isNull = isNull; + } + @Override public String toString() { final StringBuilder sb = new StringBuilder("CodegenProperty{"); @@ -764,6 +775,7 @@ public String toString() { sb.append(", xmlName='").append(xmlName).append('\''); sb.append(", xmlNamespace='").append(xmlNamespace).append('\''); sb.append(", isXmlWrapped=").append(isXmlWrapped); + sb.append(", isNull=").append(isNull); sb.append('}'); return sb.toString(); } @@ -812,6 +824,7 @@ public boolean equals(Object o) { isInherited == that.isInherited && isXmlAttribute == that.isXmlAttribute && isXmlWrapped == that.isXmlWrapped && + isNull == that.isNull && Objects.equals(openApiType, that.openApiType) && Objects.equals(baseName, that.baseName) && Objects.equals(complexType, that.complexType) && @@ -873,6 +886,6 @@ public int hashCode() { items, mostInnerItems, additionalProperties, vars, requiredVars, vendorExtensions, hasValidation, isInherited, discriminatorValue, nameInCamelCase, nameInSnakeCase, enumName, maxItems, minItems, isXmlAttribute, xmlPrefix, xmlName, - xmlNamespace, isXmlWrapped); + xmlNamespace, isXmlWrapped, isNull); } } diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/CodegenResponse.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/CodegenResponse.java index b3ceedb149aa..7762676b7ca8 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/CodegenResponse.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/CodegenResponse.java @@ -57,6 +57,7 @@ public class CodegenResponse implements IJsonSchemaValidationProperties { public boolean isArray; public boolean isBinary = false; public boolean isFile = false; + public boolean isNull; public Object schema; public String jsonSchema; public Map vendorExtensions = new HashMap(); @@ -84,7 +85,7 @@ public int hashCode() { isString, isNumeric, isInteger, isLong, isNumber, isFloat, isDouble, isDecimal, isByteArray, isBoolean, isDate, isDateTime, isUuid, isEmail, isModel, isFreeFormObject, isAnyType, isDefault, simpleType, primitiveType, isMap, isArray, isBinary, isFile, schema, jsonSchema, vendorExtensions, items, additionalProperties, - vars, requiredVars, + vars, requiredVars, isNull, getMaxProperties(), getMinProperties(), uniqueItems, getMaxItems(), getMinItems(), getMaxLength(), getMinLength(), exclusiveMinimum, exclusiveMaximum, getMinimum(), getMaximum(), getPattern(), is1xx, is2xx, is3xx, is4xx, is5xx); @@ -122,6 +123,7 @@ public boolean equals(Object o) { isFile == that.isFile && items == that.items && additionalProperties == that.additionalProperties && + isNull == that.isNull && is1xx == that.is1xx && is2xx == that.is2xx && is3xx == that.is3xx && @@ -423,6 +425,7 @@ public String toString() { sb.append(", additionalProperties='").append(additionalProperties).append('\''); sb.append(", vars='").append(vars).append('\''); sb.append(", requiredVars='").append(requiredVars).append('\''); + sb.append(", isNull='").append(isNull); sb.append('}'); return sb.toString(); } @@ -443,4 +446,14 @@ public boolean isRange() { return true; return false; } + + @Override + public boolean getIsNull() { + return isNull; + } + + @Override + public void setIsNull(boolean isNull) { + this.isNull = isNull; + } } diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java index 9738f9a94529..9f063bef496b 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java @@ -2306,6 +2306,8 @@ public CodegenModel fromModel(String name, Schema schema) { m.arrayModelType = arrayProperty.complexType; addParentContainer(m, name, schema); ModelUtils.syncValidationProperties(schema, m); + } else if (ModelUtils.isNullType(schema)) { + m.isNull = true; } else if (schema instanceof ComposedSchema) { final ComposedSchema composed = (ComposedSchema) schema; Map properties = new LinkedHashMap(); @@ -3279,6 +3281,8 @@ public CodegenProperty fromProperty(String name, Schema p) { innerSchema = new StringSchema().description("//TODO automatically added by openapi-generator due to undefined type"); p.setAdditionalProperties(innerSchema); } + } else if (ModelUtils.isNullType(p)) { + property.isNull = true; } //Inline enum case: @@ -4044,6 +4048,7 @@ public CodegenResponse fromResponse(String responseCode, ApiResponse response) { if (r.schema != null) { Map allSchemas = null; CodegenProperty cp = fromProperty("response", responseSchema); + r.isNull = cp.isNull; if (ModelUtils.isArraySchema(responseSchema)) { ArraySchema as = (ArraySchema) responseSchema; @@ -4317,6 +4322,8 @@ public CodegenParameter fromParameter(Parameter parameter, Set imports) imports.add(codegenProperty.baseType); codegenProperty = codegenProperty.items; } + } else if (ModelUtils.isNullType(parameterSchema)) { + codegenParameter.isNull = true; } /* TODO revise the logic below } else { @@ -6146,6 +6153,7 @@ public CodegenParameter fromRequestBody(RequestBody body, Set imports, S } else { codegenParameter.baseName = bodyParameterName; } + codegenParameter.isNull = codegenProperty.isNull; codegenParameter.isPrimitiveType = true; codegenParameter.baseType = codegenProperty.baseType; codegenParameter.dataType = codegenProperty.dataType; diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/IJsonSchemaValidationProperties.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/IJsonSchemaValidationProperties.java index 7a1a0323a083..9b0ad0f89f95 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/IJsonSchemaValidationProperties.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/IJsonSchemaValidationProperties.java @@ -90,4 +90,8 @@ public interface IJsonSchemaValidationProperties { List getRequiredVars(); void setRequiredVars(List requiredVars); + + boolean getIsNull(); + + void setIsNull(boolean isNull); } diff --git a/modules/openapi-generator/src/test/java/org/openapitools/codegen/DefaultCodegenTest.java b/modules/openapi-generator/src/test/java/org/openapitools/codegen/DefaultCodegenTest.java index 9233174859a1..87959fbb2ed5 100644 --- a/modules/openapi-generator/src/test/java/org/openapitools/codegen/DefaultCodegenTest.java +++ b/modules/openapi-generator/src/test/java/org/openapitools/codegen/DefaultCodegenTest.java @@ -2557,6 +2557,23 @@ public void testIsXPresence() { assertEquals(cm.isString, false); assertEquals(cm.isDate, true); + modelName = "NullModel"; + sc = openAPI.getComponents().getSchemas().get(modelName); + cm = codegen.fromModel(modelName, sc); + assertEquals(cm.isNull, true); + + modelName = "ObjectWithTypeNullProperties"; + sc = openAPI.getComponents().getSchemas().get(modelName); + cm = codegen.fromModel(modelName, sc); + assertEquals(cm.getVars().get(0).isNull, true); + assertEquals(cm.getVars().get(1).getItems().isNull, true); + assertEquals(cm.getAdditionalProperties().isNull, true); + + modelName = "ArrayOfNulls"; + sc = openAPI.getComponents().getSchemas().get(modelName); + cm = codegen.fromModel(modelName, sc); + assertEquals(cm.getItems().isNull, true); + modelName = "ObjectWithDateWithValidation"; sc = openAPI.getComponents().getSchemas().get(modelName); cm = codegen.fromModel(modelName, sc); @@ -2618,6 +2635,20 @@ public void testIsXPresence() { assertEquals(co.bodyParams.get(0).isDateTime, true); assertEquals(co.responses.get(0).isString, false); assertEquals(co.responses.get(0).isDateTime, true); + + path = "/null/{param}"; + operation = openAPI.getPaths().get(path).getPost(); + co = codegen.fromOperation(path, "POST", operation, null); + assertEquals(co.pathParams.get(0).isNull, true); + assertEquals(co.bodyParams.get(0).isNull, true); + assertEquals(co.responses.get(0).isNull, true); + + path = "/ref_null/{param}"; + operation = openAPI.getPaths().get(path).getPost(); + co = codegen.fromOperation(path, "POST", operation, null); + assertEquals(co.pathParams.get(0).isNull, true); + assertEquals(co.bodyParams.get(0).isNull, true); + assertEquals(co.responses.get(0).isNull, true); } @Test diff --git a/modules/openapi-generator/src/test/resources/3_0/issue_7651.yaml b/modules/openapi-generator/src/test/resources/3_0/issue_7651.yaml index ce2bfff7fac7..f35d64b4b358 100644 --- a/modules/openapi-generator/src/test/resources/3_0/issue_7651.yaml +++ b/modules/openapi-generator/src/test/resources/3_0/issue_7651.yaml @@ -120,8 +120,73 @@ paths: type: string format: date-time pattern: '^2020.*' + /null/{param}: + post: + tags: + - isX + operationId: null + parameters: + - name: param + in: path + required: true + schema: + type: 'null' + requestBody: + content: + application/json: + schema: + type: 'null' + required: true + responses: + 200: + description: success + content: + application/json: + schema: + type: 'null' + /ref_null/{param}: + post: + tags: + - isX + operationId: null + parameters: + - name: param + in: path + required: true + schema: + $ref: '#/components/schemas/NullModel' + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/NullModel' + required: true + responses: + 200: + description: success + content: + application/json: + schema: + $ref: '#/components/schemas/NullModel' components: schemas: + NullModel: + type: 'null' + ObjectWithTypeNullProperties: + type: object + properties: + nullProp: + type: 'null' + listOfNulls: + type: array + items: + type: 'null' + additionalProperties: + type: 'null' + ArrayOfNulls: + type: array + items: + type: 'null' DateWithValidation: type: string format: date