diff --git a/modules/swagger-codegen/src/main/java/io/swagger/codegen/DefaultCodegen.java b/modules/swagger-codegen/src/main/java/io/swagger/codegen/DefaultCodegen.java index 488e5ef0dbb..9403596d573 100644 --- a/modules/swagger-codegen/src/main/java/io/swagger/codegen/DefaultCodegen.java +++ b/modules/swagger-codegen/src/main/java/io/swagger/codegen/DefaultCodegen.java @@ -1239,11 +1239,64 @@ public String getSwaggerType(Property p) { } else { if (p != null) { datatype = p.getType(); + // Handle OpenAPI 3.1.0 schemas where type may be null but can be inferred from format + if (datatype == null) { + datatype = inferTypeFromFormat(p.getFormat()); + } } } + // Final fallback: if datatype is still null, default to "object" + // This handles OpenAPI 3.1.0 schemas that may not have explicit type information + if (datatype == null) { + LOGGER.warn("Unable to determine type for property, defaulting to 'object'. Property: " + p); + datatype = "object"; + } return datatype; } + /** + * Infer the type from the format string when the type is null. + * This is particularly useful for OpenAPI 3.1.0 schemas where type information + * may not be explicitly set but can be inferred from the format. + * + * @param format the format string from the property + * @return the inferred type or null if it cannot be inferred + */ + private String inferTypeFromFormat(String format) { + if (format == null) { + return null; + } + switch (format) { + case "int32": + return "integer"; + case "int64": + return "long"; + case "float": + return "float"; + case "double": + return "double"; + case "byte": + return "ByteArray"; + case "binary": + return "binary"; + case "date": + return "date"; + case "date-time": + return "DateTime"; + case "uuid": + return "UUID"; + case "password": + case "email": + case "uri": + case "hostname": + case "ipv4": + case "ipv6": + return "string"; + default: + return null; + } + } + /** * Return the snake-case of the string * diff --git a/modules/swagger-codegen/src/test/java/io/swagger/codegen/DefaultCodegenTest.java b/modules/swagger-codegen/src/test/java/io/swagger/codegen/DefaultCodegenTest.java index f51533b1444..0bc9cdb58d9 100644 --- a/modules/swagger-codegen/src/test/java/io/swagger/codegen/DefaultCodegenTest.java +++ b/modules/swagger-codegen/src/test/java/io/swagger/codegen/DefaultCodegenTest.java @@ -1,10 +1,40 @@ package io.swagger.codegen; +import io.swagger.models.properties.AbstractProperty; +import io.swagger.models.properties.Property; +import io.swagger.models.properties.StringProperty; +import io.swagger.models.properties.BooleanProperty; +import io.swagger.models.properties.IntegerProperty; import org.testng.Assert; import org.testng.annotations.Test; public class DefaultCodegenTest { + /** + * A test property class that simulates the behavior of properties returned by the + * swagger-parser when parsing OpenAPI 3.1.0 schemas where the type might be null. + */ + private static class NullTypeProperty extends AbstractProperty { + private String format; + + public NullTypeProperty() { + super(); + // Set type to null to simulate OpenAPI 3.1.0 parsing issue + super.setType(null); + } + + public NullTypeProperty(String format) { + this(); + this.format = format; + super.setFormat(format); + } + + @Override + public String getType() { + return null; + } + } + @Test public void testInitialConfigValues() throws Exception { final DefaultCodegen codegen = new DefaultCodegen(); @@ -43,4 +73,108 @@ public void testShouldOverwriteWithPathTraversal() { () -> codegen.shouldOverwrite("../../../etc/passwd") ); } + + /** + * Test that getSwaggerType returns proper types for standard property types. + */ + @Test + public void testGetSwaggerTypeForStandardProperties() { + final DefaultCodegen codegen = new DefaultCodegen(); + + // Test StringProperty + Assert.assertEquals(codegen.getSwaggerType(new StringProperty()), "string"); + + // Test BooleanProperty + Assert.assertEquals(codegen.getSwaggerType(new BooleanProperty()), "boolean"); + + // Test IntegerProperty + Assert.assertEquals(codegen.getSwaggerType(new IntegerProperty()), "integer"); + } + + /** + * Test that getSwaggerType handles null property type gracefully by defaulting to "object". + * This is important for OpenAPI 3.1.0 schemas where type information may be null. + * See GitHub issue #12666. + */ + @Test + public void testGetSwaggerTypeForNullTypeProperty() { + final DefaultCodegen codegen = new DefaultCodegen(); + + // Test property with null type and no format + Property nullTypeProp = new NullTypeProperty(); + String result = codegen.getSwaggerType(nullTypeProp); + Assert.assertNotNull(result, "getSwaggerType should never return null"); + Assert.assertEquals(result, "object", "Null type without format should default to 'object'"); + } + + /** + * Test that getSwaggerType can infer type from format when type is null. + * This is particularly useful for OpenAPI 3.1.0 schemas. + * See GitHub issue #12666. + */ + @Test + public void testGetSwaggerTypeInfersTypeFromFormat() { + final DefaultCodegen codegen = new DefaultCodegen(); + + // Test format int32 -> integer + Property int32Prop = new NullTypeProperty("int32"); + Assert.assertEquals(codegen.getSwaggerType(int32Prop), "integer"); + + // Test format int64 -> long + Property int64Prop = new NullTypeProperty("int64"); + Assert.assertEquals(codegen.getSwaggerType(int64Prop), "long"); + + // Test format float -> float + Property floatProp = new NullTypeProperty("float"); + Assert.assertEquals(codegen.getSwaggerType(floatProp), "float"); + + // Test format double -> double + Property doubleProp = new NullTypeProperty("double"); + Assert.assertEquals(codegen.getSwaggerType(doubleProp), "double"); + + // Test format date -> date + Property dateProp = new NullTypeProperty("date"); + Assert.assertEquals(codegen.getSwaggerType(dateProp), "date"); + + // Test format date-time -> DateTime + Property dateTimeProp = new NullTypeProperty("date-time"); + Assert.assertEquals(codegen.getSwaggerType(dateTimeProp), "DateTime"); + + // Test format uuid -> UUID + Property uuidProp = new NullTypeProperty("uuid"); + Assert.assertEquals(codegen.getSwaggerType(uuidProp), "UUID"); + + // Test format binary -> binary + Property binaryProp = new NullTypeProperty("binary"); + Assert.assertEquals(codegen.getSwaggerType(binaryProp), "binary"); + + // Test format byte -> ByteArray + Property byteProp = new NullTypeProperty("byte"); + Assert.assertEquals(codegen.getSwaggerType(byteProp), "ByteArray"); + + // Test string-like formats + Property emailProp = new NullTypeProperty("email"); + Assert.assertEquals(codegen.getSwaggerType(emailProp), "string"); + + Property passwordProp = new NullTypeProperty("password"); + Assert.assertEquals(codegen.getSwaggerType(passwordProp), "string"); + + Property uriProp = new NullTypeProperty("uri"); + Assert.assertEquals(codegen.getSwaggerType(uriProp), "string"); + } + + /** + * Test that getSwaggerType falls back to "object" for unknown format when type is null. + * See GitHub issue #12666. + */ + @Test + public void testGetSwaggerTypeFallsBackToObjectForUnknownFormat() { + final DefaultCodegen codegen = new DefaultCodegen(); + + // Test unknown format - should fall back to object + Property unknownFormatProp = new NullTypeProperty("unknown-format"); + String result = codegen.getSwaggerType(unknownFormatProp); + Assert.assertNotNull(result, "getSwaggerType should never return null"); + Assert.assertEquals(result, "object", "Unknown format should fall back to 'object'"); + } }