Skip to content

Commit

Permalink
Feat/add allowed model properties 152 (#583)
Browse files Browse the repository at this point in the history
* Fix 178, should not fail when there are no 'example' for a component

* Update lib/src/main/java/org/openapitools/openapistylevalidator/OpenApiSpecStyleValidator.java

little reformat

* Add allowedModelProperties configuration

This new property accept an array of property names. Those names will be valid even of they do not match the propertyNamingConvention.

* Fix formatting errors

---------

Co-authored-by: Jean-François Côté <jcote@stingray.com>
Co-authored-by: Blaise GERVAIS <bgervais@masana.care>
  • Loading branch information
3 people authored Jul 21, 2023
1 parent cd388a7 commit 1d1890f
Show file tree
Hide file tree
Showing 4 changed files with 78 additions and 27 deletions.
45 changes: 23 additions & 22 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -91,28 +91,29 @@ Example using the default output path for the jar (replace `<version>` with the
#### Options File
The options file is described in json (example in `specs/options.json`), and has the following possible values:

|Option|Type|Possible Values|Default Value|Description|
|---|---|---|---|---|
|validateInfoLicense|boolean|`true`, `false`|`true`|Ensures that there is a license section in the info section|
|validateInfoDescription|boolean|`true`, `false`|`true`|Ensures that there is a description attribute in the info section|
|validateInfoContact|boolean|`true`, `false`|`true`|Ensures that there is a contact section in the info section|
|validateOperationOperationId|boolean|`true`, `false`|`true`|Ensures that there is an operation id for each operation|
|validateOperationDescription|boolean|`true`, `false`|`true`|Ensures that there is a description for each operation|
|validateOperationTag|boolean|`true`, `false`|`true`|Ensures that there is a tag for each operation|
|validateOperationSummary|boolean|`true`, `false`|`true`|Ensures that there is a summary for each operation|
|validateModelPropertiesExample|boolean|`true`, `false`|`true`|Ensures that the properties of the Schemas have an example value defined|
|validateModelPropertiesDescription|boolean|`true`, `false`|`true`|Ensures that the properties of the Schemas have a description value defined|
|validateModelRequiredProperties|boolean|`true`, `false`|`true`|Ensures that all required properties of the Schemas are listed among their properties|
|validateModelNoLocalDef|boolean|`true`, `false`|`true`|Not implemented yet|
|validateNaming|boolean|`true`, `false`|`true`|Ensures the names follow a given naming convention|
|ignoreHeaderXNaming|boolean|`true`, `false`|`true`|Exclude from validation header parameters starting with `x-`|
|pathNamingConvention|string|`CamelCase`, `PascalCase`, `HyphenUpperCase`, `HyphenCase`, `UnderscoreCase`, `UnderscoreUpperCase`, `AnyCase` |`HyphenCase`|Naming convention for paths|
|parameterNamingConvention|string|`CamelCase`, `PascalCase`, `HyphenUpperCase`, `HyphenCase`, `UnderscoreCase`, `UnderscoreUpperCase`, `AnyCase`|`CamelCase`|Global naming convention for all parameter types (path, query and cookie) [(note)](#parameter-naming-convention-hierarchy)|
|pathParamNamingConvention|string|`CamelCase`, `PascalCase`, `HyphenUpperCase`, `HyphenCase`, `UnderscoreCase`, `UnderscoreUpperCase`, `AnyCase`|(same as `parameterNamingConvention`)|Specific naming convention for path parameters [(note)](#parameter-naming-convention-hierarchy)|
|queryParamNamingConvention|string|`CamelCase`, `PascalCase`, `HyphenUpperCase`, `HyphenCase`, `UnderscoreCase`, `UnderscoreUpperCase`, `AnyCase`|(same as `parameterNamingConvention`)|Specific naming convention for query parameters [(note)](#parameter-naming-convention-hierarchy)|
|cookieParamNamingConvention|string|`CamelCase`, `PascalCase`, `HyphenUpperCase`, `HyphenCase`, `UnderscoreCase`, `UnderscoreUpperCase`, `AnyCase`|(same as `parameterNamingConvention`)|Specific naming convention for cookie parameters [(note)](#parameter-naming-convention-hierarchy)|
|headerNamingConvention|string|`CamelCase`, `PascalCase`, `HyphenUpperCase`, `HyphenCase`, `UnderscoreCase`, `UnderscoreUpperCase`, `AnyCase`|`UnderscoreUpperCase`|Naming convention for headers|
|propertyNamingConvention|string|`CamelCase`, `PascalCase`, `HyphenUpperCase`, `HyphenCase`, `UnderscoreCase`, `UnderscoreUpperCase`, `AnyCase`|`CamelCase`|Naming convention for properties|
|Option|Type| Possible Values |Default Value|Description|
|---|---|----------------------------------------------------------------------------------------------------------------|---|---|
|validateInfoLicense|boolean| `true`, `false` |`true`|Ensures that there is a license section in the info section|
|validateInfoDescription|boolean| `true`, `false` |`true`|Ensures that there is a description attribute in the info section|
|validateInfoContact|boolean| `true`, `false` |`true`|Ensures that there is a contact section in the info section|
|validateOperationOperationId|boolean| `true`, `false` |`true`|Ensures that there is an operation id for each operation|
|validateOperationDescription|boolean| `true`, `false` |`true`|Ensures that there is a description for each operation|
|validateOperationTag|boolean| `true`, `false` |`true`|Ensures that there is a tag for each operation|
|validateOperationSummary|boolean| `true`, `false` |`true`|Ensures that there is a summary for each operation|
|validateModelPropertiesExample|boolean| `true`, `false` |`true`|Ensures that the properties of the Schemas have an example value defined|
|validateModelPropertiesDescription|boolean| `true`, `false` |`true`|Ensures that the properties of the Schemas have a description value defined|
|validateModelRequiredProperties|boolean| `true`, `false` |`true`|Ensures that all required properties of the Schemas are listed among their properties|
|validateModelNoLocalDef|boolean| `true`, `false` |`true`|Not implemented yet|
|validateNaming|boolean| `true`, `false` |`true`|Ensures the names follow a given naming convention|
|ignoreHeaderXNaming|boolean| `true`, `false` |`true`|Exclude from validation header parameters starting with `x-`|
|pathNamingConvention|string| `CamelCase`, `PascalCase`, `HyphenUpperCase`, `HyphenCase`, `UnderscoreCase`, `UnderscoreUpperCase`, `AnyCase` |`HyphenCase`|Naming convention for paths|
|parameterNamingConvention|string| `CamelCase`, `PascalCase`, `HyphenUpperCase`, `HyphenCase`, `UnderscoreCase`, `UnderscoreUpperCase`, `AnyCase` |`CamelCase`|Global naming convention for all parameter types (path, query and cookie) [(note)](#parameter-naming-convention-hierarchy)|
|pathParamNamingConvention|string| `CamelCase`, `PascalCase`, `HyphenUpperCase`, `HyphenCase`, `UnderscoreCase`, `UnderscoreUpperCase`, `AnyCase` |(same as `parameterNamingConvention`)|Specific naming convention for path parameters [(note)](#parameter-naming-convention-hierarchy)|
|queryParamNamingConvention|string| `CamelCase`, `PascalCase`, `HyphenUpperCase`, `HyphenCase`, `UnderscoreCase`, `UnderscoreUpperCase`, `AnyCase` |(same as `parameterNamingConvention`)|Specific naming convention for query parameters [(note)](#parameter-naming-convention-hierarchy)|
|cookieParamNamingConvention|string| `CamelCase`, `PascalCase`, `HyphenUpperCase`, `HyphenCase`, `UnderscoreCase`, `UnderscoreUpperCase`, `AnyCase` |(same as `parameterNamingConvention`)|Specific naming convention for cookie parameters [(note)](#parameter-naming-convention-hierarchy)|
|headerNamingConvention|string| `CamelCase`, `PascalCase`, `HyphenUpperCase`, `HyphenCase`, `UnderscoreCase`, `UnderscoreUpperCase`, `AnyCase` |`UnderscoreUpperCase`|Naming convention for headers|
|propertyNamingConvention|string| `CamelCase`, `PascalCase`, `HyphenUpperCase`, `HyphenCase`, `UnderscoreCase`, `UnderscoreUpperCase`, `AnyCase` |`CamelCase`|Naming convention for properties|
|allowedModelProperties|array| `["_links", "_embedded"]`|`[]`|An array of property names that are authorised even if they do not match to the "propertyNamingConvention"|

#### Parameter Naming Convention hierarchy

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -193,8 +193,10 @@ private void validateNaming() {
if (model.getProperties() != null) {
for (Map.Entry<String, Schema> entry :
model.getProperties().entrySet()) {
String name = entry.getKey();
boolean isValid = namingValidator.isNamingValid(
entry.getKey(), parameters.getPropertyNamingConvention());
name, parameters.getPropertyNamingConvention())
|| parameters.getAllowedModelProperties().contains(name);
if (!isValid) {
errorAggregator.logModelBadNaming(
entry.getKey(),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
package org.openapitools.openapistylevalidator;

import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

public class ValidatorParameters {
public static final String VALIDATE_INFO_LICENSE = "validateInfoLicense";
public static final String VALIDATE_INFO_DESCRIPTION = "validateInfoDescription";
Expand Down Expand Up @@ -76,6 +81,8 @@ public String getDesignation() {
private boolean pathParamNamingConventionWasExplicitlySet = false;
private boolean cookieParamNamingConventionWasExplicitlySet = false;

private Set<String> allowedModelProperties = new HashSet<>();

public ValidatorParameters() {
// For Gson
}
Expand Down Expand Up @@ -277,6 +284,15 @@ public ValidatorParameters setIgnoreHeaderXNaming(boolean ignoreHeaderXNaming) {
return this;
}

public ValidatorParameters setAllowedModelProperties(List<String> allowedModelProperties) {
this.allowedModelProperties = new HashSet<>(allowedModelProperties);
return this;
}

public Set<String> getAllowedModelProperties() {
return Collections.unmodifiableSet(this.allowedModelProperties);
}

@Override
public String toString() {
return String.format(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,8 @@

import static org.junit.jupiter.api.Assertions.assertEquals;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.*;
import java.util.stream.Collectors;
import org.eclipse.microprofile.openapi.OASFactory;
import org.eclipse.microprofile.openapi.models.OpenAPI;
import org.eclipse.microprofile.openapi.models.media.Schema;
Expand Down Expand Up @@ -408,6 +406,40 @@ void shouldReportCookieParamNamingConventionError() {
}
/* end - tests for issue #367 */

// https://github.com/OpenAPITools/openapi-style-validator/issues/152
@Test
void shouldAcceptSpecificModelProperties() {
OpenAPI openAPI = createValidOpenAPI();
openAPI.components(openAPI.getComponents()
.addSchema(
"Items",
OASFactory.createSchema().type(SchemaType.OBJECT).properties(new HashMap<String, Schema>() {
{
put(
"_links",
OASFactory.createSchema()
// _links should be an object. However, this is enough
// to represent the case
.type(SchemaType.STRING));
}
})));

OpenApiSpecStyleValidator validator = new OpenApiSpecStyleValidator(openAPI);
ValidatorParameters parameters = new ValidatorParameters()
// Keep the details away
.setValidateModelPropertiesDescription(false)
.setValidateModelPropertiesExample(false)
// Reproduce the original error
.setPropertyNamingConvention(NamingConvention.CamelCase)
.setValidateNaming(true)
// Add the fix
.setAllowedModelProperties(Collections.singletonList("_links"));
List<StyleError> errors = validator.validate(parameters);
Assertions.assertTrue(
errors.isEmpty(),
() -> errors.stream().map(StyleError::toString).collect(Collectors.joining()));
}

private static OpenAPI createValidOpenAPI() {
return OASFactory.createOpenAPI()
.openapi("3.0.1")
Expand Down

0 comments on commit 1d1890f

Please # to comment.