Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[codegen] Add openAPI attribute to validation and recommendation #5216

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -942,8 +942,28 @@ public static List<Schema> getInterfaces(ComposedSchema composed) {
}

/**
* Get the parent model name from the schemas (allOf, anyOf, oneOf).
* If there are multiple parents, return the first one.
* Get the parent model name from the composed schema (allOf, anyOf, oneOf).
* It traverses the OAS model (possibly resolving $ref) to determine schemas
* that specify a determinator.
* If there are multiple elements in the composed schema and it is not clear
* which one should be the parent, return null.
*
* For example, given the following OAS spec, the parent of 'Dog' is Animal
* because 'Animal' specifies a discriminator.
*
* animal:
* type: object
* discriminator:
* propertyName: type
* properties:
* type: string
*
* dog:
* allOf:
* - $ref: '#/components/schemas/animal'
* - type: object
* properties:
* breed: string
*
* @param composedSchema schema (alias or direct reference)
* @param allSchemas all schemas
Expand All @@ -953,7 +973,6 @@ public static String getParentName(ComposedSchema composedSchema, Map<String, Sc
List<Schema> interfaces = getInterfaces(composedSchema);

List<String> refedParentNames = new ArrayList<>();

if (interfaces != null && !interfaces.isEmpty()) {
for (Schema schema : interfaces) {
// get the actual schema
Expand All @@ -980,7 +999,8 @@ public static String getParentName(ComposedSchema composedSchema, Map<String, Sc
// parent name only makes sense when there is a single obvious parent
if (refedParentNames.size() == 1) {
LOGGER.warn("[deprecated] inheritance without use of 'discriminator.propertyName' is deprecated " +
"and will be removed in a future release. Generating model for {}", composedSchema.getName());
"and will be removed in a future release. Generating model for composed schema name: {}. Title: {}",
composedSchema.getName(), composedSchema.getTitle());
return refedParentNames.get(0);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,10 @@ public ValidationResult validate(OpenAPI specification) {
}

Map<String, Schema> schemas = ModelUtils.getSchemas(specification);
schemas.forEach((key, schema) -> validationResult.consume(schemaValidations.validate(schema)));
schemas.forEach((key, schema) -> {
SchemaWrapper wrapper = new SchemaWrapper(specification, schema);
validationResult.consume(schemaValidations.validate(wrapper));
});

List<Parameter> parameters = new ArrayList<>(50);

Expand All @@ -68,7 +71,7 @@ public ValidationResult validate(OpenAPI specification) {
parameters.addAll(op.getParameters());
}

OperationWrapper wrapper = new OperationWrapper(op, httpMethod);
OperationWrapper wrapper = new OperationWrapper(specification, op, httpMethod);
validationResult.consume(operationValidations.validate(wrapper));
}
});
Expand All @@ -79,7 +82,10 @@ public ValidationResult validate(OpenAPI specification) {
if (components != null) {
Map<String, SecurityScheme> securitySchemes = components.getSecuritySchemes();
if (securitySchemes != null && !securitySchemes.isEmpty()) {
securitySchemes.values().forEach(securityScheme -> validationResult.consume(securitySchemeValidations.validate(securityScheme)));
securitySchemes.values().forEach(securityScheme -> {
SecuritySchemeWrapper wrapper = new SecuritySchemeWrapper(specification, securityScheme);
validationResult.consume(securitySchemeValidations.validate(wrapper));
});
}

if (components.getParameters() != null) {
Expand All @@ -89,7 +95,8 @@ public ValidationResult validate(OpenAPI specification) {

parameters.forEach(parameter -> {
parameter = ModelUtils.getReferencedParameter(specification, parameter);
validationResult.consume(parameterValidations.validate(parameter));
ParameterWrapper wrapper = new ParameterWrapper(specification, parameter);
validationResult.consume(parameterValidations.validate(wrapper));
});

return validationResult;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
/**
* A standalone instance for evaluating rules and recommendations related to OAS {@link Parameter}
*/
class OpenApiParameterValidations extends GenericValidator<Parameter> {
class OpenApiParameterValidations extends GenericValidator<ParameterWrapper> {
OpenApiParameterValidations(RuleConfiguration ruleConfiguration) {
super(new ArrayList<>());
if (ruleConfiguration.isEnableRecommendations()) {
Expand All @@ -32,7 +32,8 @@ class OpenApiParameterValidations extends GenericValidator<Parameter> {
* @param parameter Any spec doc parameter. The method will handle {@link HeaderParameter} evaluation.
* @return {@link ValidationRule.Pass} if the check succeeds, otherwise {@link ValidationRule.Fail} with details "[key] contains an underscore."
*/
private static ValidationRule.Result apacheNginxHeaderCheck(Parameter parameter) {
private static ValidationRule.Result apacheNginxHeaderCheck(ParameterWrapper parameterWrapper) {
Parameter parameter = parameterWrapper.getParameter();
if (parameter == null || !parameter.getIn().equals("header")) return ValidationRule.Pass.empty();
ValidationRule.Result result = ValidationRule.Pass.empty();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

import io.swagger.v3.oas.models.media.ComposedSchema;
import io.swagger.v3.oas.models.media.Schema;

import org.openapitools.codegen.utils.ModelUtils;
import org.openapitools.codegen.validation.GenericValidator;
import org.openapitools.codegen.validation.ValidationRule;

Expand All @@ -10,7 +12,7 @@
/**
* A standalone instance for evaluating rules and recommendations related to OAS {@link Schema}
*/
class OpenApiSchemaValidations extends GenericValidator<Schema> {
class OpenApiSchemaValidations extends GenericValidator<SchemaWrapper> {
OpenApiSchemaValidations(RuleConfiguration ruleConfiguration) {
super(new ArrayList<>());
if (ruleConfiguration.isEnableRecommendations()) {
Expand All @@ -34,10 +36,11 @@ class OpenApiSchemaValidations extends GenericValidator<Schema> {
* Because of this ambiguity in the spec about what is non-standard about oneOf support, we'll warn as a recommendation that
* properties on the schema defining oneOf relationships may not be intentional in the OpenAPI Specification.
*
* @param schema An input schema, regardless of the type of schema
* @param schemaWrapper An input schema, regardless of the type of schema
* @return {@link ValidationRule.Pass} if the check succeeds, otherwise {@link ValidationRule.Fail}
*/
private static ValidationRule.Result checkOneOfWithProperties(Schema schema) {
private static ValidationRule.Result checkOneOfWithProperties(SchemaWrapper schemaWrapper) {
Schema schema = schemaWrapper.getSchema();
ValidationRule.Result result = ValidationRule.Pass.empty();

if (schema instanceof ComposedSchema) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package org.openapitools.codegen.validations.oas;

import io.swagger.v3.oas.models.security.SecurityScheme;
import io.swagger.v3.oas.models.OpenAPI;
import org.apache.commons.lang3.StringUtils;
import org.openapitools.codegen.validation.GenericValidator;
import org.openapitools.codegen.validation.ValidationRule;
Expand All @@ -11,7 +12,7 @@
/**
* A standalone instance for evaluating rules and recommendations related to OAS {@link SecurityScheme}
*/
class OpenApiSecuritySchemeValidations extends GenericValidator<SecurityScheme> {
class OpenApiSecuritySchemeValidations extends GenericValidator<SecuritySchemeWrapper> {
OpenApiSecuritySchemeValidations(RuleConfiguration ruleConfiguration) {
super(new ArrayList<>());
if (ruleConfiguration.isEnableRecommendations()) {
Expand All @@ -31,7 +32,8 @@ class OpenApiSecuritySchemeValidations extends GenericValidator<SecurityScheme>
* @param securityScheme Security schemes are often used as header parameters (e.g. APIKEY).
* @return <code>true</code> if the check succeeds (header does not have an underscore, e.g. 'api_key')
*/
private static ValidationRule.Result apacheNginxHeaderCheck(SecurityScheme securityScheme) {
private static ValidationRule.Result apacheNginxHeaderCheck(SecuritySchemeWrapper securitySchemeWrapper) {
SecurityScheme securityScheme = securitySchemeWrapper.getSecurityScheme();
if (securityScheme == null || securityScheme.getIn() != SecurityScheme.In.HEADER)
return ValidationRule.Pass.empty();
ValidationRule.Result result = ValidationRule.Pass.empty();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@

import io.swagger.v3.oas.models.Operation;
import io.swagger.v3.oas.models.PathItem;
import io.swagger.v3.oas.models.OpenAPI;

/**
* Encapsulates an operation with its HTTP Method. In OAS, the {@link PathItem} structure contains more than what we'd
* want to evaluate for operation-only checks.
*/
public class OperationWrapper {
OpenAPI specification;
private Operation operation;
private PathItem.HttpMethod httpMethod;

Expand All @@ -17,7 +19,8 @@ public class OperationWrapper {
* @param operation The operation instances to wrap
* @param httpMethod The http method to wrap
*/
OperationWrapper(Operation operation, PathItem.HttpMethod httpMethod) {
OperationWrapper(OpenAPI specification, Operation operation, PathItem.HttpMethod httpMethod) {
this.specification = specification;
this.operation = operation;
this.httpMethod = httpMethod;
}
Expand All @@ -39,4 +42,14 @@ public Operation getOperation() {
public PathItem.HttpMethod getHttpMethod() {
return httpMethod;
}


/**
* Returns the OpenAPI specification.
*
* @return The the OpenAPI specification.
*/
public OpenAPI getOpenAPI() {
return specification;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package org.openapitools.codegen.validations.oas;

import io.swagger.v3.oas.models.parameters.Parameter;
import io.swagger.v3.oas.models.OpenAPI;

/**
* Encapsulates an OAS parameter.
*/
public class ParameterWrapper {
OpenAPI specification;
private Parameter parameter;

/**
* Constructs a new instance of {@link ParameterWrapper}
*
* @param specification The OAS specification
* @param parameter The OAS parameter
*/
ParameterWrapper(OpenAPI specification, Parameter parameter) {
this.specification = specification;
this.parameter = parameter;
}

/**
* Return the OAS parameter
*
* @return the OAS parameter
*/
public Parameter getParameter() {
return parameter;
}

/**
* Returns the OpenAPI specification.
*
* @return The the OpenAPI specification.
*/
public OpenAPI getOpenAPI() {
return specification;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package org.openapitools.codegen.validations.oas;

import io.swagger.v3.oas.models.media.Schema;
import io.swagger.v3.oas.models.OpenAPI;

/**
* Encapsulates an OAS schema.
*/
public class SchemaWrapper {
OpenAPI specification;
private Schema schema;

/**
* Constructs a new instance of {@link SchemaWrapper}
*
* @param specification The OAS specification
* @param schema The OAS schema
*/
SchemaWrapper(OpenAPI specification, Schema schema) {
this.specification = specification;
this.schema = schema;
}

/**
* Return the OAS schema
*
* @return the OAS schema
*/
public Schema getSchema() {
return schema;
}

/**
* Returns the OpenAPI specification.
*
* @return The the OpenAPI specification.
*/
public OpenAPI getOpenAPI() {
return specification;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package org.openapitools.codegen.validations.oas;

import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.security.SecurityScheme;

/**
* Encapsulates an OAS parameter.
*/
public class SecuritySchemeWrapper {
OpenAPI specification;
private SecurityScheme securityScheme;

/**
* Constructs a new instance of {@link SecuritySchemeWrapper}
*
* @param specification The OAS specification
* @param securityScheme The OAS securityScheme
*/
SecuritySchemeWrapper(OpenAPI specification, SecurityScheme securityScheme) {
this.specification = specification;
this.securityScheme = securityScheme;
}

/**
* Return the OAS securityScheme
*
* @return the OAS securityScheme
*/
public SecurityScheme getSecurityScheme() {
return securityScheme;
}

/**
* Returns the OpenAPI specification.
*
* @return The the OpenAPI specification.
*/
public OpenAPI getOpenAPI() {
return specification;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ public void testGetOrHeadWithBody(PathItem.HttpMethod method, String operationId
op.setRequestBody(body);
}

ValidationResult result = validator.validate(new OperationWrapper(op, method));
ValidationResult result = validator.validate(new OperationWrapper(null, op, method));
Assert.assertNotNull(result.getWarnings());

List<Invalid> warnings = result.getWarnings().stream()
Expand Down Expand Up @@ -77,7 +77,7 @@ public void testGetOrHeadWithBodyWithDisabledRecommendations(PathItem.HttpMethod
op.setRequestBody(body);
}

ValidationResult result = validator.validate(new OperationWrapper(op, method));
ValidationResult result = validator.validate(new OperationWrapper(null, op, method));
Assert.assertNotNull(result.getWarnings());

List<Invalid> warnings = result.getWarnings().stream()
Expand All @@ -103,7 +103,7 @@ public void testGetOrHeadWithBodyWithDisabledRule(PathItem.HttpMethod method, St
op.setRequestBody(body);
}

ValidationResult result = validator.validate(new OperationWrapper(op, method));
ValidationResult result = validator.validate(new OperationWrapper(null, op, method));
Assert.assertNotNull(result.getWarnings());

List<Invalid> warnings = result.getWarnings().stream()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ public void testApacheNginxWithDisabledRecommendations(String in, String key, bo
parameter.setIn(in);
parameter.setName(key);

ValidationResult result = validator.validate(parameter);
ValidationResult result = validator.validate(new ParameterWrapper(null, parameter));
Assert.assertNotNull(result.getWarnings());

List<Invalid> warnings = result.getWarnings().stream()
Expand All @@ -43,7 +43,7 @@ public void testApacheNginxWithDisabledRule(String in, String key, boolean match
parameter.setIn(in);
parameter.setName(key);

ValidationResult result = validator.validate(parameter);
ValidationResult result = validator.validate(new ParameterWrapper(null, parameter));
Assert.assertNotNull(result.getWarnings());

List<Invalid> warnings = result.getWarnings().stream()
Expand All @@ -64,7 +64,7 @@ public void testDefaultRecommendationApacheNginx(String in, String key, boolean
parameter.setIn(in);
parameter.setName(key);

ValidationResult result = validator.validate(parameter);
ValidationResult result = validator.validate(new ParameterWrapper(null, parameter));
Assert.assertNotNull(result.getWarnings());

List<Invalid> warnings = result.getWarnings().stream()
Expand Down
Loading