forked from loafle/openapi-generator-original
[core] Extracting recommendations to validation framework (#4979)
* [core] Extracting recommendations to validation framework
This is work to extract recommendation logic out of the CLI validate command
into a shared evaluation instance which can be used elsewhere (such as Gradle,
or the Online tool).
For now, these validations are in addition to those provided by swagger-parser and
are only the following recommendations:
* Apache/Nginx warning that header values with underscore are dropped by default
* Unused models/schemas
* Use of properties with oneOf, which is ambiguous in OpenAPI Specification
I've allowed for disabling recommendations via System properties, since this is
something that has been requested a few times by users. System properties in this
commit include:
* openapi.generator.rule.recommendations=false
- Allows for disabling recommendations completely. This wouldn't include all warnings
and errors, only those we deem to be suggestions
* openapi.generator.rule.apache-nginx-underscore=false
- Allows for disabling the Apache/Nginx warning when header names have underscore
- This is a legacy CGI configuration, and doesn't affect all web servers
* openapi.generator.rule.oneof-properties-ambiguity=false
- We support this functionality, but the specification may not intend for it
- This is more to reduce noise
* openapi.generator.rule.unused-schemas=false
- We will warn when a schema is not referenced outside of Components, which
users have requested to be able to turn off
* openapi.generator.rule.anti-patterns.uri-unexpected-body=false
* Move recommendation/validations to oas package and add javadoc comments
* Refactor and test recommendation validations
* Refactor validation function signatures to return explicit state rather than boolean
* Add operation recommendation for GET/HEAD w/body
This commit is contained in:
@@ -22,14 +22,14 @@ import io.airlift.airline.Option;
|
||||
|
||||
import io.swagger.parser.OpenAPIParser;
|
||||
import io.swagger.v3.oas.models.OpenAPI;
|
||||
import io.swagger.v3.oas.models.media.ComposedSchema;
|
||||
import io.swagger.v3.oas.models.media.Schema;
|
||||
import io.swagger.v3.parser.core.models.SwaggerParseResult;
|
||||
import org.openapitools.codegen.utils.ModelUtils;
|
||||
import org.apache.commons.lang3.text.WordUtils;
|
||||
import org.openapitools.codegen.validation.ValidationResult;
|
||||
import org.openapitools.codegen.validations.oas.OpenApiEvaluator;
|
||||
import org.openapitools.codegen.validations.oas.RuleConfiguration;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
@Command(name = "validate", description = "Validate specification")
|
||||
@@ -54,42 +54,28 @@ public class Validate implements Runnable {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
OpenAPI specification = result.getOpenAPI();
|
||||
|
||||
if (Boolean.TRUE.equals(recommend)) {
|
||||
if (specification != null) {
|
||||
// Add information about unused models to the warnings set.
|
||||
List<String> unusedModels = ModelUtils.getUnusedSchemas(specification);
|
||||
if (unusedModels != null) {
|
||||
unusedModels.forEach(name -> warnings.add("Unused model: " + name));
|
||||
}
|
||||
RuleConfiguration ruleConfiguration = new RuleConfiguration();
|
||||
ruleConfiguration.setEnableRecommendations(recommend != null ? recommend : false);
|
||||
|
||||
// check for loosely defined oneOf extension requirements.
|
||||
// This is a recommendation because the 3.0.x spec is not clear enough on usage of oneOf.
|
||||
// see https://json-schema.org/draft/2019-09/json-schema-core.html#rfc.section.9.2.1.3 and the OAS section on 'Composition and Inheritance'.
|
||||
Map<String, Schema> schemas = ModelUtils.getSchemas(specification);
|
||||
schemas.forEach((key, schema) -> {
|
||||
if (schema instanceof ComposedSchema) {
|
||||
final ComposedSchema composed = (ComposedSchema) schema;
|
||||
if (composed.getOneOf() != null && composed.getOneOf().size() > 0) {
|
||||
if (composed.getProperties() != null && composed.getProperties().size() >= 1 && composed.getProperties().get("discriminator") == null) {
|
||||
warnings.add("Schema (oneOf) should not contain properties: " + key);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
OpenApiEvaluator evaluator = new OpenApiEvaluator(ruleConfiguration);
|
||||
ValidationResult validationResult = evaluator.validate(specification);
|
||||
|
||||
// TODO: We could also provide description here along with getMessage. getMessage is either a "generic" message or specific (e.g. Model 'Cat' has issues).
|
||||
// This would require that we parse the messageList coming from swagger-parser into a better structure.
|
||||
validationResult.getWarnings().forEach(invalid -> warnings.add(invalid.getMessage()));
|
||||
validationResult.getErrors().forEach(invalid -> errors.add(invalid.getMessage()));
|
||||
|
||||
if (!errors.isEmpty()) {
|
||||
sb.append("Errors:").append(System.lineSeparator());
|
||||
errors.forEach(msg ->
|
||||
sb.append("\t-").append(msg).append(System.lineSeparator())
|
||||
sb.append("\t- ").append(WordUtils.wrap(msg, 90).replace(System.lineSeparator(), System.lineSeparator() + "\t ")).append(System.lineSeparator())
|
||||
);
|
||||
}
|
||||
|
||||
if (!warnings.isEmpty()) {
|
||||
sb.append("Warnings: ").append(System.lineSeparator());
|
||||
warnings.forEach(msg ->
|
||||
sb.append("\t-").append(msg).append(System.lineSeparator())
|
||||
sb.append("\t- ").append(WordUtils.wrap(msg, 90).replace(System.lineSeparator(), System.lineSeparator() + "\t ")).append(System.lineSeparator())
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user