diff --git a/modules/openapi-generator-cli/src/main/java/org/openapitools/codegen/cmd/Validate.java b/modules/openapi-generator-cli/src/main/java/org/openapitools/codegen/cmd/Validate.java index 8af47269fcb4..ec35f9ae596c 100644 --- a/modules/openapi-generator-cli/src/main/java/org/openapitools/codegen/cmd/Validate.java +++ b/modules/openapi-generator-cli/src/main/java/org/openapitools/codegen/cmd/Validate.java @@ -45,6 +45,10 @@ public class Validate extends OpenApiGeneratorCommand { @Option(name = {"--recommend"}, title = "recommend spec improvements") private Boolean recommend; + @Option(name = {"--skip-unused-models"}, title = "skip unused models warning", + description = "skips the recommendation warning for schemas defined in components/schemas that are not referenced by any operation") + private Boolean skipUnusedModels; + @Option( name = {"-a", "--auth"}, title = "authorization", @@ -71,6 +75,8 @@ public void execute() { if (recommend != null) ruleConfiguration.setEnableRecommendations(recommend); else ruleConfiguration.setEnableRecommendations(false); + if (Boolean.TRUE.equals(skipUnusedModels)) ruleConfiguration.setEnableUnusedSchemasRecommendation(false); + OpenApiEvaluator evaluator = new OpenApiEvaluator(ruleConfiguration); ValidationResult validationResult = evaluator.validate(specification); diff --git a/modules/openapi-generator-gradle-plugin/src/main/kotlin/org/openapitools/generator/gradle/plugin/OpenApiGeneratorPlugin.kt b/modules/openapi-generator-gradle-plugin/src/main/kotlin/org/openapitools/generator/gradle/plugin/OpenApiGeneratorPlugin.kt index 901383ac6378..30ffd7bd75d9 100644 --- a/modules/openapi-generator-gradle-plugin/src/main/kotlin/org/openapitools/generator/gradle/plugin/OpenApiGeneratorPlugin.kt +++ b/modules/openapi-generator-gradle-plugin/src/main/kotlin/org/openapitools/generator/gradle/plugin/OpenApiGeneratorPlugin.kt @@ -87,6 +87,7 @@ class OpenApiGeneratorPlugin : Plugin { remoteInputSpec.set(validate.remoteInputSpec) recommend.set(validate.recommend) treatWarningsAsErrors.set(validate.treatWarningsAsErrors) + skipUnusedModels.set(validate.skipUnusedModels) } register("openApiGenerate", GenerateTask::class.java).configure { diff --git a/modules/openapi-generator-gradle-plugin/src/main/kotlin/org/openapitools/generator/gradle/plugin/extensions/OpenApiGeneratorValidateExtension.kt b/modules/openapi-generator-gradle-plugin/src/main/kotlin/org/openapitools/generator/gradle/plugin/extensions/OpenApiGeneratorValidateExtension.kt index f6a00c5668a4..621e0e23c580 100644 --- a/modules/openapi-generator-gradle-plugin/src/main/kotlin/org/openapitools/generator/gradle/plugin/extensions/OpenApiGeneratorValidateExtension.kt +++ b/modules/openapi-generator-gradle-plugin/src/main/kotlin/org/openapitools/generator/gradle/plugin/extensions/OpenApiGeneratorValidateExtension.kt @@ -49,6 +49,11 @@ open class OpenApiGeneratorValidateExtension(private val project: Project) { */ val treatWarningsAsErrors: Property = project.objects.property().convention(false) + /** + * Whether to suppress warnings for schemas defined in components/schemas that are not referenced by any operation. + */ + val skipUnusedModels: Property = project.objects.property().convention(false) + // ======================================================================== // Backwards-compatibility bridge setter for Groovy DSL // This allows Groovy users to use assignment syntax: inputSpec = "path" diff --git a/modules/openapi-generator-gradle-plugin/src/main/kotlin/org/openapitools/generator/gradle/plugin/tasks/ValidateTask.kt b/modules/openapi-generator-gradle-plugin/src/main/kotlin/org/openapitools/generator/gradle/plugin/tasks/ValidateTask.kt index c7f2085d8ad9..fb4b139636e1 100644 --- a/modules/openapi-generator-gradle-plugin/src/main/kotlin/org/openapitools/generator/gradle/plugin/tasks/ValidateTask.kt +++ b/modules/openapi-generator-gradle-plugin/src/main/kotlin/org/openapitools/generator/gradle/plugin/tasks/ValidateTask.kt @@ -70,9 +70,14 @@ abstract class ValidateTask : DefaultTask() { @get:Input abstract val treatWarningsAsErrors: Property + @get:Optional + @get:Input + abstract val skipUnusedModels: Property + init { recommend.convention(true) treatWarningsAsErrors.convention(false) + skipUnusedModels.convention(false) } @Suppress("unused") @@ -98,6 +103,7 @@ abstract class ValidateTask : DefaultTask() { val recommendations = recommend.get() val failOnWarnings = treatWarningsAsErrors.get() + val shouldSkipUnusedModels = skipUnusedModels.get() logger.lifecycle("Validating spec $specLocation") @@ -110,6 +116,9 @@ abstract class ValidateTask : DefaultTask() { val ruleConfiguration = RuleConfiguration() ruleConfiguration.isEnableRecommendations = recommendations + if (shouldSkipUnusedModels) { + ruleConfiguration.isEnableUnusedSchemasRecommendation = false + } val evaluator = OpenApiEvaluator(ruleConfiguration) val validationResult = evaluator.validate(result.openAPI) diff --git a/modules/openapi-generator-gradle-plugin/src/test/kotlin/ValidateTaskDslTest.kt b/modules/openapi-generator-gradle-plugin/src/test/kotlin/ValidateTaskDslTest.kt index 04d170bea7ec..bcd8413ae049 100644 --- a/modules/openapi-generator-gradle-plugin/src/test/kotlin/ValidateTaskDslTest.kt +++ b/modules/openapi-generator-gradle-plugin/src/test/kotlin/ValidateTaskDslTest.kt @@ -503,6 +503,87 @@ paths: ) } + @Test(dataProvider = "gradle_version_provider") + fun `openApiValidate should warn about unused models by default`(gradleVersion: String?, format: String) { + val propertyFormat = PropertyFormat.valueOf(format) + // Arrange + val projectFiles = mapOf( + "spec.yaml" to javaClass.classLoader.getResourceAsStream("specs/petstore-v3.0-with-unused-schema.yaml") + ) + + withProject( + """ + | plugins { + | id 'org.openapi.generator' + | } + | + | openApiValidate { + | ${inputSpecProperty("spec.yaml", propertyFormat)} + | } + """.trimMargin(), projectFiles + ) + + // Act + val result = getGradleRunner(gradleVersion) + .withProjectDir(temp) + .withArguments("openApiValidate") + .withPluginClasspath() + .build() + + // Assert + assertTrue( + result.output.contains("Unused model: UnusedSchema"), + "Expected unused model warning to be present by default." + ) + assertEquals( + SUCCESS, result.task(":openApiValidate")?.outcome, + "Expected a successful run, but found ${result.task(":openApiValidate")?.outcome}" + ) + } + + @Test(dataProvider = "gradle_version_provider") + fun `openApiValidate should suppress unused model warnings when skipUnusedModels is true`(gradleVersion: String?, format: String) { + val propertyFormat = PropertyFormat.valueOf(format) + // Arrange + val projectFiles = mapOf( + "spec.yaml" to javaClass.classLoader.getResourceAsStream("specs/petstore-v3.0-with-unused-schema.yaml") + ) + + withProject( + """ + | plugins { + | id 'org.openapi.generator' + | } + | + | openApiValidate { + | ${inputSpecProperty("spec.yaml", propertyFormat)} + | skipUnusedModels = true + | } + """.trimMargin(), projectFiles + ) + + // Act + val result = getGradleRunner(gradleVersion) + .withProjectDir(temp) + .withArguments("openApiValidate") + .withPluginClasspath() + .build() + + // Assert + assertTrue( + result.output.contains("Unused model: UnusedSchema").not(), + "Expected unused model warning to be suppressed when skipUnusedModels = true." + ) + assertTrue( + result.output.contains("Spec is valid."), + "Expected spec to be reported as valid." + ) + assertEquals( + SUCCESS, result.task(":openApiValidate")?.outcome, + "Expected a successful run, but found ${result.task(":openApiValidate")?.outcome}" + ) + } + @Test(dataProvider = "gradle_version_only_provider") fun `openApiValidate should support task-level configuration with Kotlin DSL set String syntax`(gradleVersion: String?) { // Create a valid spec file diff --git a/modules/openapi-generator-gradle-plugin/src/test/resources/specs/petstore-v3.0-with-unused-schema.yaml b/modules/openapi-generator-gradle-plugin/src/test/resources/specs/petstore-v3.0-with-unused-schema.yaml new file mode 100644 index 000000000000..7f29ae540ab1 --- /dev/null +++ b/modules/openapi-generator-gradle-plugin/src/test/resources/specs/petstore-v3.0-with-unused-schema.yaml @@ -0,0 +1,45 @@ +openapi: "3.0.0" +info: + version: 1.0.0 + title: Swagger Petstore + license: + name: MIT +servers: + - url: http://petstore.swagger.io/v1 +paths: + /pets: + get: + summary: List all pets + operationId: listPets + tags: + - pets + responses: + '200': + description: A list of pets + content: + application/json: + schema: + $ref: "#/components/schemas/Pets" +components: + schemas: + Pet: + type: object + required: + - id + - name + properties: + id: + type: integer + format: int64 + name: + type: string + Pets: + type: array + items: + $ref: "#/components/schemas/Pet" + UnusedSchema: + description: This schema is defined but never referenced by any operation. + type: object + properties: + id: + type: integer