swagger-parser
swagger-parser copied to clipboard
OpenAPIV3Parser setting null instead of parsing default enum values
Hi,
I've discovered what seems to be an issue when OpenAPIV3Parser parses specifications containing default values for enum types.
When io.swagger.v3.parser.OpenAPIV3Parser#read(java.lang.String) parses default values for enums, the returned OpenAPI model schemas contain null instead of default values. This applies for both defaults set in enums themselves or inside schemas referencing the enums. The same specification that exposes this issue is valid and properly shows defaults inside Swagger Editor. This seems to be in line with Schema Object in the OpenAPI specification.
I'm providing a test class with unit tests that confirm my claims alongside an example OpenAPI specification that is used in tests and can be run inside Swagger Editor. The tests should be easy to add to your existing OpenAPIV3ParserTest. This is tested using swagger-parser versions 2.0.14 and 2.0.22.
Can you please look into this and see if my findings are correct?
Test class
The class contains various examples of setting default values for enums that do or do not set the default values themselves. Assertions are based on behavior in Swagger Editor. Ignored tests are the ones exposing the issue.
package io.swagger.v3.parser.test;
import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.parser.OpenAPIV3Parser;
import org.junit.*;
import java.io.File;
import static org.assertj.core.api.BDDAssertions.then;
public class OpenAPIV3ParserTest {
private static final String GIVEN_SPEC_PATH = "src/test/resources/openapis/openapi-default-enum-bug.json";
private static final String GIVEN_LOCATION = new File(GIVEN_SPEC_PATH).getAbsolutePath();
private OpenAPIV3Parser openAPIV3Parser;
@Before
public void setUp() {
openAPIV3Parser = new OpenAPIV3Parser();
}
@Test
public void shouldParseDefaultForEnumSchema() {
// when
OpenAPI openAPI = openAPIV3Parser.read(GIVEN_LOCATION);
// then
then(openAPI.getComponents()
.getSchemas()
.get("Enum")
.getDefault()
).isNull();
}
@Test
public void shouldParseDefaultForEnumWithDefaultSchema() {
// when
OpenAPI openAPI = openAPIV3Parser.read(GIVEN_LOCATION);
// then
then(openAPI.getComponents()
.getSchemas()
.get("EnumWithDefault")
.getDefault()
).isEqualTo("ENUM_DEFAULT");
}
@Test
public void shouldParseDefaultForSchemaWithEnum() {
// when
OpenAPI openAPI = openAPIV3Parser.read(GIVEN_LOCATION);
// then
then(openAPI.getComponents()
.getSchemas()
.get("SchemaWithEnum")
.getDefault()
).isNull();
}
@Ignore("This test fails due to OpenAPIV3Parser bug.")
@Test
public void shouldParseDefaultForSchemaWithDefaultAndEnum() {
// when
OpenAPI openAPI = openAPIV3Parser.read(GIVEN_LOCATION);
// then
then(openAPI.getComponents()
.getSchemas()
.get("SchemaWithDefaultAndEnum")
.getDefault()
).isEqualTo("SCHEMA_DEFAULT");
}
@Ignore("This test fails due to OpenAPIV3Parser bug.")
@Test
public void shouldParseDefaultForSchemaWithEnumWithDefault() {
// when
OpenAPI openAPI = openAPIV3Parser.read(GIVEN_LOCATION);
// then
then(openAPI.getComponents()
.getSchemas()
.get("SchemaWithEnumWithDefault")
.getDefault()
).isEqualTo("ENUM_DEFAULT");
}
@Ignore("This test fails due to OpenAPIV3Parser bug.")
@Test
public void shouldParseDefaultForSchemaWithDefaultAndEnumWithDefault() {
// when
OpenAPI openAPI = openAPIV3Parser.read(GIVEN_LOCATION);
// then
then(openAPI.getComponents()
.getSchemas()
.get("SchemaWithDefaultAndEnumWithDefault")
.getDefault()
).isEqualTo("SCHEMA_DEFAULT");
}
}
Example specification
This specification is used in the test above and is named openapi-default-enum-bug.json for testing purposes.
{
"openapi": "3.0.1",
"info": {
"title": "Here be enum bugs",
"description": "OpenAPI spec to test the enum parsing bugs.",
"version": "1.0.0"
},
"paths": {
"/enum/parse/bug": {
"post": {
"summary": "Test enum parse bug",
"requestBody": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/TestRequest"
}
}
}
},
"responses": {
"default": {
"description": "Successful response"
}
}
}
}
},
"components": {
"schemas": {
"TestRequest": {
"type": "object",
"description": "This request body schema depicts various enum and default value combinations inside schemas of the following properties.",
"properties": {
"schemaWithEnum": {
"$ref": "#/components/schemas/SchemaWithEnum"
},
"schemaWithDefaultAndEnum": {
"$ref": "#/components/schemas/SchemaWithDefaultAndEnum"
},
"schemaWithEnumWithDefault": {
"$ref": "#/components/schemas/SchemaWithEnumWithDefault"
},
"schemaWithDefaultAndEnumWithDefault": {
"$ref": "#/components/schemas/SchemaWithDefaultAndEnumWithDefault"
}
}
},
"SchemaWithEnum": {
"type": "object",
"properties": {
"enumProperty": {
"description": "This schema with no default values set behaves properly in Swagger Editor and when parsed with OpenAPIV3Parser.",
"allOf": [
{
"$ref": "#/components/schemas/Enum"
}
]
}
}
},
"SchemaWithDefaultAndEnum": {
"type": "object",
"properties": {
"enumProperty": {
"description": "This schema with set default enum value and enum ref shows default \"SCHEMA_DEFAULT\" value in Swagger Editor, but is null when parsed with OpenAPIV3Parser.",
"allOf": [
{
"$ref": "#/components/schemas/Enum"
}
],
"default": "SCHEMA_DEFAULT"
}
}
},
"SchemaWithEnumWithDefault": {
"type": "object",
"properties": {
"enumProperty": {
"description": "This schema with ref to enum with set default value shows default \"ENUM_DEFAULT\" value in Swagger Editor, but is null when parsed with OpenAPIV3Parser.",
"allOf": [
{
"$ref": "#/components/schemas/EnumWithDefault"
}
]
}
}
},
"SchemaWithDefaultAndEnumWithDefault": {
"type": "object",
"properties": {
"enumProperty": {
"description": "This schema with set default enum value and ref to enum with set default value shows default \"SCHEMA_DEFAULT\" value in Swagger Editor, but is null when parsed with OpenAPIV3Parser.",
"allOf": [
{
"$ref": "#/components/schemas/EnumWithDefault"
}
],
"default": "SCHEMA_DEFAULT"
}
}
},
"Enum": {
"type": "string",
"enum": [
"SCHEMA_DEFAULT",
"NOT_DEFAULT"
]
},
"EnumWithDefault": {
"type": "string",
"enum": [
"SCHEMA_DEFAULT",
"ENUM_DEFAULT",
"NOT_DEFAULT"
],
"default": "ENUM_DEFAULT"
}
}
}
}
could you check with latest changes. one part of the issue is fixed. default values are set for enums. EnumWithDefault has its default value set.
@r-sreesaran unfortunately, tests provided by @hkozacinski are still failing with swagger-parser version 2.0.25. I've created a little maven project that you can use to easily re-run the tests: swagger-parser-issue-1454. Hope this helps!
@r-sreesaran I can also confirm that the problem still exists with version 2.0.26. For the following definition, the default value is not available after parsing.
@r-sreesaran the problem still exists.
As far as I can tell, it comes from io.swagger.v3.parser.util.OpenAPIDeserializer.getSchema(ObjectNode node, String location, ParseResult result)
.
After line if (node.get("default") != null)
, there is a tentative to get the actual default value, based on the type of the node. The type is null
because of the allOf
. So the default value is simply ignored.
On top of that, at the time the default value is ignored, the Enum
schema has not been parsed yet, so we don't even know the type of the thing.
https://github.com/swagger-api/swagger-parser/blob/master/modules/swagger-parser-v3/src/main/java/io/swagger/v3/parser/util/OpenAPIDeserializer.java#L2911
A fix would probably require to remember those undecidable cases, then try to decide after the whole spec has been parsed.
@r-sreesaran No sorry: the cause of the problem was in ResolverFully.java I will issue a pull request.
@tmeckel my first comment was the result of the inspection of the source code of 2.0.x. Sorry.
Version 2.1.2 is working much better in this regard, and I think the little fix in ResolverFully
is just the icing on the cake.
@bfreuden great to hear! Hopefully the issue is now completely fixed.
I've just added all the tests and made an adjustement to ResolverFully
so it sets a default value when all combined models have the same default value.
It is almost completely fixed now.
I'm saying "almost" because I'm not sure the SchemaWithEnumWithDefault
case is supposed to be working when resolve=false
and aggregateCombinators=false
. I don't think so (and it isn't working). In my opinion that's the purpose of the resolve=true
and aggregateCombinators=true
, and it's working with those parameters as shown by new unit tests.