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 710f7838d72f..90e874f6ef41 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 @@ -4107,7 +4107,9 @@ public CodegenProperty fromProperty(String name, Schema p, boolean required, boo if (p.getWriteOnly() != null) { property.isWriteOnly = p.getWriteOnly(); } - if (p.getNullable() != null) { + if (ModelUtils.isNullable(p)) { + property.isNullable = true; + } else if (p.getNullable() != null) { property.isNullable = p.getNullable(); } @@ -4159,7 +4161,9 @@ public CodegenProperty fromProperty(String name, Schema p, boolean required, boo } // set isNullable using nullable or x-nullable in the schema - if (referencedSchema.getNullable() != null) { + if (ModelUtils.isNullable(referencedSchema)) { + property.isNullable = true; + } else if (referencedSchema.getNullable() != null) { property.isNullable = referencedSchema.getNullable(); } else if (referencedSchema.getExtensions() != null && referencedSchema.getExtensions().containsKey(X_NULLABLE)) { @@ -4262,7 +4266,9 @@ public CodegenProperty fromProperty(String name, Schema p, boolean required, boo if (original != null) { p = original; // evaluate common attributes if defined in the top level - if (p.getNullable() != null) { + if (ModelUtils.isNullable(p)) { + property.isNullable = true; + } else if (p.getNullable() != null) { property.isNullable = p.getNullable(); } else if (p.getExtensions() != null && p.getExtensions().containsKey(X_NULLABLE)) { property.isNullable = (Boolean) p.getExtensions().get(X_NULLABLE); diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/utils/ModelUtils.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/utils/ModelUtils.java index 88346d9046f7..3375132c3f9c 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/utils/ModelUtils.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/utils/ModelUtils.java @@ -1794,6 +1794,9 @@ public static boolean isNullable(Schema schema) { if (schema.getExtensions() != null && schema.getExtensions().get(X_NULLABLE) != null) { return Boolean.parseBoolean(schema.getExtensions().get(X_NULLABLE).toString()); } + if (schema.getTypes() != null && schema.getTypes().contains("null")) { + return true; + } // In OAS 3.1, the recommended way to define a nullable property or object is to use oneOf. if (isComposedSchema(schema)) { return isNullableComposedSchema(schema); diff --git a/modules/openapi-generator/src/test/java/org/openapitools/codegen/typescript/axios/TypeScriptAxiosClientCodegenTest.java b/modules/openapi-generator/src/test/java/org/openapitools/codegen/typescript/axios/TypeScriptAxiosClientCodegenTest.java index e9a3e21e2c39..47783b06b195 100644 --- a/modules/openapi-generator/src/test/java/org/openapitools/codegen/typescript/axios/TypeScriptAxiosClientCodegenTest.java +++ b/modules/openapi-generator/src/test/java/org/openapitools/codegen/typescript/axios/TypeScriptAxiosClientCodegenTest.java @@ -179,6 +179,28 @@ public void testDeprecatedArrayAttribute() throws Exception { TestUtils.assertFileContains(file, "'nicknames'?: Array"); } + @Test + public void generatesNullUnionsForOpenApi31NullableContainers() throws Exception { + final File output = Files.createTempDirectory("typescript_axios_nullable_container_types_").toFile(); + output.deleteOnExit(); + + final CodegenConfigurator configurator = new CodegenConfigurator() + .setGeneratorName("typescript-axios") + .setInputSpec("src/test/resources/3_1/typescript-axios/nullable-container-types.yaml") + .setOutputDir(output.getAbsolutePath().replace("\\", "/")); + + final ClientOptInput clientOptInput = configurator.toClientOptInput(); + final DefaultGenerator generator = new DefaultGenerator(); + final List files = generator.opts(clientOptInput).generate(); + files.forEach(File::deleteOnExit); + + Path file = Paths.get(output + "/api.ts"); + + TestUtils.assertFileContains(file, "'validation'?: { [key: string]: string; } | null;"); + TestUtils.assertFileContains(file, "'requirements'?: Array | null;"); + TestUtils.assertFileContains(file, "'settings': MappingItemResourceSettings | null;"); + } + @Test public void generatesTrailingCommasInAsConstEnumObjects() throws Exception { final File output = Files.createTempDirectory("typescript_axios_trailing_commas_").toFile(); diff --git a/modules/openapi-generator/src/test/resources/3_1/typescript-axios/nullable-container-types.yaml b/modules/openapi-generator/src/test/resources/3_1/typescript-axios/nullable-container-types.yaml new file mode 100644 index 000000000000..79c72861de21 --- /dev/null +++ b/modules/openapi-generator/src/test/resources/3_1/typescript-axios/nullable-container-types.yaml @@ -0,0 +1,41 @@ +openapi: 3.1.0 +info: + title: Nullable container types + version: 1.0.0 +paths: + /mappings: + get: + operationId: listMappings + responses: + '200': + description: successful operation + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/MappingItemResource' +components: + schemas: + MappingItemResource: + type: object + properties: + settings: + type: + - object + - 'null' + properties: + validation: + type: + - object + - 'null' + additionalProperties: + type: string + requirements: + type: + - array + - 'null' + items: + type: string + required: + - settings