From e7404d1efe70be546ce9a7d88aedffe7679fe97f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A1chym=20Metli=C4=8Dka?= Date: Mon, 15 Jun 2026 16:11:54 +0200 Subject: [PATCH 1/6] add (for now failing) tests --- .../oas/OpenApiSchemaValidationsTest.java | 71 +++++++++++++++++++ .../3_1/nullable-deprecated-in-oas31.yaml | 14 ++++ 2 files changed, 85 insertions(+) create mode 100644 modules/openapi-generator/src/test/resources/3_1/nullable-deprecated-in-oas31.yaml diff --git a/modules/openapi-generator/src/test/java/org/openapitools/codegen/validations/oas/OpenApiSchemaValidationsTest.java b/modules/openapi-generator/src/test/java/org/openapitools/codegen/validations/oas/OpenApiSchemaValidationsTest.java index ebff8ea7e4b8..a963e0987c6c 100644 --- a/modules/openapi-generator/src/test/java/org/openapitools/codegen/validations/oas/OpenApiSchemaValidationsTest.java +++ b/modules/openapi-generator/src/test/java/org/openapitools/codegen/validations/oas/OpenApiSchemaValidationsTest.java @@ -1,6 +1,8 @@ package org.openapitools.codegen.validations.oas; +import io.swagger.v3.oas.models.OpenAPI; import io.swagger.v3.oas.models.media.*; +import org.openapitools.codegen.TestUtils; import org.openapitools.codegen.validation.Invalid; import org.openapitools.codegen.validation.ValidationResult; import org.testng.Assert; @@ -67,6 +69,75 @@ public void testOneOfWithSiblingPropertiesDisabledRule(Schema schema, boolean ma Assert.assertEquals(warnings.size(), 0, "Expected rule to be disabled."); } + /** + * Probe: verify where swagger-parser stores `nullable: true` when parsing an OAS 3.1 spec. + * In OAS 3.1, `nullable` is not a recognized keyword — it may be stored in schema.getExtensions() + * under the raw key "nullable", or it may be silently dropped. + * This test documents the actual parser behavior so the fix in checkNullableAttribute + * knows which field to check. + */ + @Test(description = "Probe: where does swagger-parser store 'nullable: true' in an OAS 3.1 spec?") + public void probe_nullableInOas31_parserStorageLocation() { + OpenAPI openAPI = TestUtils.parseSpec("src/test/resources/3_1/nullable-deprecated-in-oas31.yaml"); + Schema proxyUrl = (Schema) openAPI.getComponents().getSchemas().get("TestModel").getProperties().get("proxyUrl"); + + // Document actual parser behavior — at least one of these must be non-null for the fix to work. + // If both are null, the parser silently drops 'nullable: true' and a different approach is needed. + Boolean getNullable = proxyUrl.getNullable(); + Object extensionNullable = proxyUrl.getExtensions() != null ? proxyUrl.getExtensions().get("nullable") : null; + + // swagger-parser stores it in extensions["nullable"] (not in getNullable() which stays null for 3.1) + Assert.assertNull(getNullable, + "In OAS 3.1, getNullable() should be null because 'nullable' is not a valid 3.1 keyword"); + Assert.assertEquals(extensionNullable, Boolean.TRUE, + "In OAS 3.1, swagger-parser stores 'nullable: true' in schema.getExtensions()[\"nullable\"]"); + } + + /** + * The validation warning for 'nullable: true' in an OAS 3.1 spec must fire. + * The existing checkNullableAttribute only checked ModelUtils.isNullable(schema) which relies on + * schema.getNullable() — but swagger-parser does not populate that field for 3.1 specs (it stores + * the value in extensions["nullable"] instead). The fix must also check extensions["nullable"]. + */ + @Test(description = "nullable: true in OAS 3.1 spec must trigger the nullable-deprecated warning") + public void testNullableAttributeInOas31_triggerWarning() { + OpenAPI openAPI = TestUtils.parseSpec("src/test/resources/3_1/nullable-deprecated-in-oas31.yaml"); + Schema proxyUrl = (Schema) openAPI.getComponents().getSchemas().get("TestModel").getProperties().get("proxyUrl"); + + RuleConfiguration config = new RuleConfiguration(); + config.setEnableRecommendations(true); + OpenApiSchemaValidations validator = new OpenApiSchemaValidations(config); + + ValidationResult result = validator.validate(new SchemaWrapper(openAPI, proxyUrl)); + List nullableWarnings = result.getWarnings().stream() + .filter(invalid -> "Schema uses the 'nullable' attribute.".equals(invalid.getRule().getDescription())) + .collect(Collectors.toList()); + + Assert.assertEquals(nullableWarnings.size(), 1, + "Expected exactly one 'nullable attribute deprecated' warning for a 3.1 spec using nullable: true"); + } + + /** + * The nullable-deprecated warning must NOT fire for an OAS 3.1 spec using the correct 3.1 null type syntax. + */ + @Test(description = "correct OAS 3.1 null type syntax (type: [string, null]) must NOT trigger the nullable-deprecated warning") + public void testNullTypeInOas31_noWarning() { + OpenAPI openAPI = TestUtils.parseSpec("src/test/resources/3_1/null-types-simple.yaml"); + Schema stringDataOrNull = (Schema) openAPI.getComponents().getSchemas().get("WithNullableType").getProperties().get("stringDataOrNull"); + + RuleConfiguration config = new RuleConfiguration(); + config.setEnableRecommendations(true); + OpenApiSchemaValidations validator = new OpenApiSchemaValidations(config); + + ValidationResult result = validator.validate(new SchemaWrapper(openAPI, stringDataOrNull)); + List nullableWarnings = result.getWarnings().stream() + .filter(invalid -> "Schema uses the 'nullable' attribute.".equals(invalid.getRule().getDescription())) + .collect(Collectors.toList()); + + Assert.assertEquals(nullableWarnings.size(), 0, + "OAS 3.1 with type:[string,null] must not trigger the nullable-deprecated warning"); + } + @DataProvider(name = "apacheNginxRecommendationExpectations") public Object[][] apacheNginxRecommendationExpectations() { return new Object[][]{ diff --git a/modules/openapi-generator/src/test/resources/3_1/nullable-deprecated-in-oas31.yaml b/modules/openapi-generator/src/test/resources/3_1/nullable-deprecated-in-oas31.yaml new file mode 100644 index 000000000000..5e3c2ddbec92 --- /dev/null +++ b/modules/openapi-generator/src/test/resources/3_1/nullable-deprecated-in-oas31.yaml @@ -0,0 +1,14 @@ +openapi: 3.1.0 +info: + title: Nullable deprecated in OAS 3.1 test + version: 1.0.0 +paths: {} +components: + schemas: + TestModel: + type: object + properties: + # OAS 3.0 'nullable: true' used in a 3.1 spec — deprecated and should trigger a warning + proxyUrl: + type: string + nullable: true From 5e8ab244dbe1be33c7ad6c251d963d1522b06d95 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A1chym=20Metli=C4=8Dka?= Date: Mon, 15 Jun 2026 16:13:42 +0200 Subject: [PATCH 2/6] fix check nullable implementation to trigger the warning --- .../codegen/validations/oas/OpenApiSchemaValidations.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/validations/oas/OpenApiSchemaValidations.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/validations/oas/OpenApiSchemaValidations.java index c80f0c52494d..7251555adeef 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/validations/oas/OpenApiSchemaValidations.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/validations/oas/OpenApiSchemaValidations.java @@ -120,7 +120,13 @@ private static ValidationRule.Result checkNullableAttribute(SchemaWrapper schema if (schemaWrapper.getOpenAPI() != null) { SemVer version = new SemVer(schemaWrapper.getOpenAPI().getOpenapi()); if (version.atLeast("3.1")) { - if (ModelUtils.isNullable(schema)) { + // ModelUtils.isNullable checks schema.getNullable(), but swagger-parser does not populate + // that field when parsing OAS 3.1 documents — 'nullable' is not a valid 3.1 keyword, so + // the parser stores it as a raw extension under the key "nullable" instead. + // We must check both paths to catch the deprecated usage in either case. + boolean hasNullableExtension = schema.getExtensions() != null + && Boolean.TRUE.equals(schema.getExtensions().get("nullable")); + if (ModelUtils.isNullable(schema) || hasNullableExtension) { result = new ValidationRule.Fail(); result.setDetails(String.format(Locale.ROOT, "OAS document is version '%s'. Schema '%s' uses 'nullable' attribute, which has been deprecated in OAS 3.1.", From 5507fecc70dc48a3f2dffc7b406fdd9b1055e5d5 Mon Sep 17 00:00:00 2001 From: Jachym Metlicka Date: Mon, 15 Jun 2026 18:50:22 +0200 Subject: [PATCH 3/6] Revert "fix check nullable implementation to trigger the warning" This reverts commit 5e8ab244dbe1be33c7ad6c251d963d1522b06d95. --- .../codegen/validations/oas/OpenApiSchemaValidations.java | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/validations/oas/OpenApiSchemaValidations.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/validations/oas/OpenApiSchemaValidations.java index 7251555adeef..c80f0c52494d 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/validations/oas/OpenApiSchemaValidations.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/validations/oas/OpenApiSchemaValidations.java @@ -120,13 +120,7 @@ private static ValidationRule.Result checkNullableAttribute(SchemaWrapper schema if (schemaWrapper.getOpenAPI() != null) { SemVer version = new SemVer(schemaWrapper.getOpenAPI().getOpenapi()); if (version.atLeast("3.1")) { - // ModelUtils.isNullable checks schema.getNullable(), but swagger-parser does not populate - // that field when parsing OAS 3.1 documents — 'nullable' is not a valid 3.1 keyword, so - // the parser stores it as a raw extension under the key "nullable" instead. - // We must check both paths to catch the deprecated usage in either case. - boolean hasNullableExtension = schema.getExtensions() != null - && Boolean.TRUE.equals(schema.getExtensions().get("nullable")); - if (ModelUtils.isNullable(schema) || hasNullableExtension) { + if (ModelUtils.isNullable(schema)) { result = new ValidationRule.Fail(); result.setDetails(String.format(Locale.ROOT, "OAS document is version '%s'. Schema '%s' uses 'nullable' attribute, which has been deprecated in OAS 3.1.", From 3ac64bac37313d36a45726663836dba60780d93a Mon Sep 17 00:00:00 2001 From: Jachym Metlicka Date: Mon, 15 Jun 2026 18:50:31 +0200 Subject: [PATCH 4/6] Reapply "fix check nullable implementation to trigger the warning" This reverts commit 5507fecc70dc48a3f2dffc7b406fdd9b1055e5d5. --- .../codegen/validations/oas/OpenApiSchemaValidations.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/validations/oas/OpenApiSchemaValidations.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/validations/oas/OpenApiSchemaValidations.java index c80f0c52494d..7251555adeef 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/validations/oas/OpenApiSchemaValidations.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/validations/oas/OpenApiSchemaValidations.java @@ -120,7 +120,13 @@ private static ValidationRule.Result checkNullableAttribute(SchemaWrapper schema if (schemaWrapper.getOpenAPI() != null) { SemVer version = new SemVer(schemaWrapper.getOpenAPI().getOpenapi()); if (version.atLeast("3.1")) { - if (ModelUtils.isNullable(schema)) { + // ModelUtils.isNullable checks schema.getNullable(), but swagger-parser does not populate + // that field when parsing OAS 3.1 documents — 'nullable' is not a valid 3.1 keyword, so + // the parser stores it as a raw extension under the key "nullable" instead. + // We must check both paths to catch the deprecated usage in either case. + boolean hasNullableExtension = schema.getExtensions() != null + && Boolean.TRUE.equals(schema.getExtensions().get("nullable")); + if (ModelUtils.isNullable(schema) || hasNullableExtension) { result = new ValidationRule.Fail(); result.setDetails(String.format(Locale.ROOT, "OAS document is version '%s'. Schema '%s' uses 'nullable' attribute, which has been deprecated in OAS 3.1.", From e6f9c64a90acbd61d185ab9a64b96b3ae023126c Mon Sep 17 00:00:00 2001 From: Jachym Metlicka Date: Mon, 15 Jun 2026 18:53:06 +0200 Subject: [PATCH 5/6] remove one test --- .../oas/OpenApiSchemaValidationsTest.java | 24 ------------------- 1 file changed, 24 deletions(-) diff --git a/modules/openapi-generator/src/test/java/org/openapitools/codegen/validations/oas/OpenApiSchemaValidationsTest.java b/modules/openapi-generator/src/test/java/org/openapitools/codegen/validations/oas/OpenApiSchemaValidationsTest.java index a963e0987c6c..a48d714d88a5 100644 --- a/modules/openapi-generator/src/test/java/org/openapitools/codegen/validations/oas/OpenApiSchemaValidationsTest.java +++ b/modules/openapi-generator/src/test/java/org/openapitools/codegen/validations/oas/OpenApiSchemaValidationsTest.java @@ -69,30 +69,6 @@ public void testOneOfWithSiblingPropertiesDisabledRule(Schema schema, boolean ma Assert.assertEquals(warnings.size(), 0, "Expected rule to be disabled."); } - /** - * Probe: verify where swagger-parser stores `nullable: true` when parsing an OAS 3.1 spec. - * In OAS 3.1, `nullable` is not a recognized keyword — it may be stored in schema.getExtensions() - * under the raw key "nullable", or it may be silently dropped. - * This test documents the actual parser behavior so the fix in checkNullableAttribute - * knows which field to check. - */ - @Test(description = "Probe: where does swagger-parser store 'nullable: true' in an OAS 3.1 spec?") - public void probe_nullableInOas31_parserStorageLocation() { - OpenAPI openAPI = TestUtils.parseSpec("src/test/resources/3_1/nullable-deprecated-in-oas31.yaml"); - Schema proxyUrl = (Schema) openAPI.getComponents().getSchemas().get("TestModel").getProperties().get("proxyUrl"); - - // Document actual parser behavior — at least one of these must be non-null for the fix to work. - // If both are null, the parser silently drops 'nullable: true' and a different approach is needed. - Boolean getNullable = proxyUrl.getNullable(); - Object extensionNullable = proxyUrl.getExtensions() != null ? proxyUrl.getExtensions().get("nullable") : null; - - // swagger-parser stores it in extensions["nullable"] (not in getNullable() which stays null for 3.1) - Assert.assertNull(getNullable, - "In OAS 3.1, getNullable() should be null because 'nullable' is not a valid 3.1 keyword"); - Assert.assertEquals(extensionNullable, Boolean.TRUE, - "In OAS 3.1, swagger-parser stores 'nullable: true' in schema.getExtensions()[\"nullable\"]"); - } - /** * The validation warning for 'nullable: true' in an OAS 3.1 spec must fire. * The existing checkNullableAttribute only checked ModelUtils.isNullable(schema) which relies on From 02cfaa7fcfd2b8d7e8464329bcaea4277cdfe9db Mon Sep 17 00:00:00 2001 From: Jachym Metlicka Date: Mon, 15 Jun 2026 18:58:04 +0200 Subject: [PATCH 6/6] trigger warning on any value of nullable: in open api 3.1.0 version --- .../codegen/validations/oas/OpenApiSchemaValidations.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/validations/oas/OpenApiSchemaValidations.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/validations/oas/OpenApiSchemaValidations.java index 7251555adeef..52069d9bbc97 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/validations/oas/OpenApiSchemaValidations.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/validations/oas/OpenApiSchemaValidations.java @@ -123,10 +123,11 @@ private static ValidationRule.Result checkNullableAttribute(SchemaWrapper schema // ModelUtils.isNullable checks schema.getNullable(), but swagger-parser does not populate // that field when parsing OAS 3.1 documents — 'nullable' is not a valid 3.1 keyword, so // the parser stores it as a raw extension under the key "nullable" instead. - // We must check both paths to catch the deprecated usage in either case. + // We must check both paths to catch the deprecated usage in either case, + // regardless of whether the value is true or false. boolean hasNullableExtension = schema.getExtensions() != null - && Boolean.TRUE.equals(schema.getExtensions().get("nullable")); - if (ModelUtils.isNullable(schema) || hasNullableExtension) { + && schema.getExtensions().containsKey("nullable"); + if (ModelUtils.isNullable(schema) || schema.getNullable() != null || hasNullableExtension) { result = new ValidationRule.Fail(); result.setDetails(String.format(Locale.ROOT, "OAS document is version '%s'. Schema '%s' uses 'nullable' attribute, which has been deprecated in OAS 3.1.",