From ff37eac3db76c170f67a38e553cb7cf972c470d2 Mon Sep 17 00:00:00 2001 From: gracekarina Date: Thu, 27 Jan 2022 11:21:00 -0500 Subject: [PATCH] includes support for #1603 --- .../v3/parser/core/models/ParseOptions.java | 9 ++ .../io/swagger/v3/parser/OpenAPIV3Parser.java | 4 +- .../v3/parser/util/OpenAPIDeserializer.java | 94 +++++++++++++++---- .../parser/test/OAI31DeserializationTest.java | 32 +++---- .../v3/parser/test/OpenAPIV3ParserTest.java | 93 ++++++++++++++++++ 5 files changed, 194 insertions(+), 38 deletions(-) diff --git a/modules/swagger-parser-core/src/main/java/io/swagger/v3/parser/core/models/ParseOptions.java b/modules/swagger-parser-core/src/main/java/io/swagger/v3/parser/core/models/ParseOptions.java index d460f8fe9c..4b484ebd9c 100644 --- a/modules/swagger-parser-core/src/main/java/io/swagger/v3/parser/core/models/ParseOptions.java +++ b/modules/swagger-parser-core/src/main/java/io/swagger/v3/parser/core/models/ParseOptions.java @@ -9,6 +9,7 @@ public class ParseOptions { private boolean camelCaseFlattenNaming; private boolean skipMatches; private boolean oaiAuthor; + private boolean defaultSchemaTypeObject = true; public boolean isResolve() { return resolve; @@ -69,4 +70,12 @@ public boolean isOaiAuthor() { return oaiAuthor; } + public boolean isDefaultSchemaTypeObject() { + return defaultSchemaTypeObject; + } + + public void setDefaultSchemaTypeObject(boolean defaultSchemaTypeObject) { + this.defaultSchemaTypeObject = defaultSchemaTypeObject; + } + } diff --git a/modules/swagger-parser-v3/src/main/java/io/swagger/v3/parser/OpenAPIV3Parser.java b/modules/swagger-parser-v3/src/main/java/io/swagger/v3/parser/OpenAPIV3Parser.java index 66f8b5fcd9..f8cf35fd98 100644 --- a/modules/swagger-parser-v3/src/main/java/io/swagger/v3/parser/OpenAPIV3Parser.java +++ b/modules/swagger-parser-v3/src/main/java/io/swagger/v3/parser/OpenAPIV3Parser.java @@ -131,10 +131,10 @@ public SwaggerParseResult readWithInfo(String path, JsonNode node) { } public SwaggerParseResult parseJsonNode(String path, JsonNode node) { - return new OpenAPIDeserializer().deserialize(node, path, false); + return new OpenAPIDeserializer().deserialize(node, path, new ParseOptions()); } public SwaggerParseResult parseJsonNode(String path, JsonNode node, ParseOptions options) { - return new OpenAPIDeserializer().deserialize(node, path, options.isOaiAuthor()); + return new OpenAPIDeserializer().deserialize(node, path, options); } public SwaggerParseResult readContents(String yaml) { diff --git a/modules/swagger-parser-v3/src/main/java/io/swagger/v3/parser/util/OpenAPIDeserializer.java b/modules/swagger-parser-v3/src/main/java/io/swagger/v3/parser/util/OpenAPIDeserializer.java index f3d1954a5c..d73a470f97 100644 --- a/modules/swagger-parser-v3/src/main/java/io/swagger/v3/parser/util/OpenAPIDeserializer.java +++ b/modules/swagger-parser-v3/src/main/java/io/swagger/v3/parser/util/OpenAPIDeserializer.java @@ -52,6 +52,7 @@ import io.swagger.v3.oas.models.servers.Server; import io.swagger.v3.oas.models.servers.ServerVariable; import io.swagger.v3.oas.models.servers.ServerVariables; +import io.swagger.v3.parser.core.models.ParseOptions; import io.swagger.v3.parser.core.models.SwaggerParseResult; import io.swagger.v3.core.util.Json; @@ -270,10 +271,10 @@ public SwaggerParseResult deserialize(JsonNode rootNode) { } public SwaggerParseResult deserialize(JsonNode rootNode, String path) { - return deserialize(rootNode, null, false); + return deserialize(rootNode, null, new ParseOptions()); } - public SwaggerParseResult deserialize(JsonNode rootNode, String path, boolean isOaiAuthor) { + public SwaggerParseResult deserialize(JsonNode rootNode, String path, ParseOptions options) { basePath = path; this.rootNode = rootNode; rootMap = new ObjectMapper().convertValue(rootNode, Map.class); @@ -281,7 +282,8 @@ public SwaggerParseResult deserialize(JsonNode rootNode, String path, boolean is try { ParseResult rootParse = new ParseResult(); - rootParse.setOaiAuthor(isOaiAuthor); + rootParse.setOaiAuthor(options.isOaiAuthor()); + rootParse.setDefaultSchemaTypeObject(options.isDefaultSchemaTypeObject()); OpenAPI api = parseRoot(rootNode, rootParse, path); result.openapi31(rootParse.isOpenapi31()); result.setOpenAPI(api); @@ -1356,7 +1358,7 @@ public MediaType getMediaType(ObjectNode contentNode, String location, ParseResu , false)); } - Object example = getAnyExample("example", contentNode, location, result); + Object example = getAnyType("example", contentNode, location, result); if (example != null) { if (examplesObject != null) { result.warning(location, "examples already defined -- ignoring \"example\" field"); @@ -1971,7 +1973,7 @@ public Parameter getParameter(ObjectNode obj, String location, ParseResult resul , false)); } - Object example = getAnyExample("example", obj, location, result); + Object example = getAnyType("example", obj, location, result); if (example != null) { if (examplesObject != null) { result.warning(location, "examples already defined -- ignoring \"example\" field"); @@ -2104,7 +2106,7 @@ public Header getHeader(ObjectNode headerNode, String location, ParseResult resu header.setExamples(getExamples(examplesObject, location, result, false)); } - Object example = getAnyExample("example", headerNode, location, result); + Object example = getAnyType("example", headerNode, location, result); if (example != null) { if (examplesObject != null) { result.warning(location, "examples already defined -- ignoring \"example\" field"); @@ -2133,8 +2135,8 @@ public Header getHeader(ObjectNode headerNode, String location, ParseResult resu return header; } - //TODO rename method as is used by different objects not only to get examples - public Object getAnyExample(String nodeKey, ObjectNode node, String location, ParseResult result) { + + public Object getAnyType(String nodeKey, ObjectNode node, String location, ParseResult result) { JsonNode example = node.get(nodeKey); if (example != null) { if (example.getNodeType().equals(JsonNodeType.STRING)) { @@ -2550,7 +2552,7 @@ at the moment path passed as string (basePath) from upper components can be both } } - if (itemsNode != null) { + if (itemsNode != null && result.isDefaultSchemaTypeObject()) { ArraySchema items = new ArraySchema(); if (itemsNode.getNodeType().equals(JsonNodeType.OBJECT)) { items.setItems(getSchema(itemsNode, location, result)); @@ -2562,6 +2564,18 @@ at the moment path passed as string (basePath) from upper components can be both } } schema = items; + }else if (itemsNode != null){ + Schema items = new Schema(); + if (itemsNode.getNodeType().equals(JsonNodeType.OBJECT)) { + items.setItems(getSchema(itemsNode, location, result)); + } else if (itemsNode.getNodeType().equals(JsonNodeType.ARRAY)) { + for (JsonNode n : itemsNode) { + if (n.isValueNode()) { + items.setItems(getSchema(itemsNode, location, result)); + } + } + } + schema = items; } Boolean additionalPropertiesBoolean = getBoolean("additionalProperties", node, false, location, result); @@ -2576,7 +2590,7 @@ at the moment path passed as string (basePath) from upper components can be both ? getSchema(additionalPropertiesObject, location, result) : additionalPropertiesBoolean; - if (additionalProperties != null) { + if (additionalProperties != null && result.isDefaultSchemaTypeObject()) { if (schema == null) { schema = additionalProperties.equals(Boolean.FALSE) @@ -2801,7 +2815,7 @@ at the moment path passed as string (basePath) from upper components can be both } //sets default value according to the schema type - if (node.get("default") != null) { + if (node.get("default") != null && result.isDefaultSchemaTypeObject()) { if (!StringUtils.isBlank(schema.getType())) { if (schema.getType().equals("array")) { ArrayNode array = getArray("default", node, false, location, result); @@ -2840,6 +2854,13 @@ at the moment path passed as string (basePath) from upper components can be both } } } + }else{ + schema.setDefault(null); + } + + bool = getBoolean("nullable", node, false, location, result); + if (bool != null) { + schema.setNullable(bool); } bool = getBoolean("readOnly", node, false, location, result); @@ -2875,7 +2896,7 @@ at the moment path passed as string (basePath) from upper components can be both } } - Object example = getAnyExample("example", node, location, result); + Object example = getAnyType("example", node, location, result); if (example != null) { schema.setExample(example instanceof NullNode ? null : example); } @@ -3083,7 +3104,7 @@ public Example getExample(ObjectNode node, String location, ParseResult result) example.setDescription(value); } - Object sample = getAnyExample("value", node, location, result); + Object sample = getAnyType("value", node, location, result); if (sample != null) { example.setValue(sample instanceof NullNode ? null : sample); } @@ -3586,8 +3607,19 @@ public Schema getJsonSchema(ObjectNode node, String location, ParseResult result schema = composedSchema; } } - - if (itemsNode != null) { + if (itemsNode != null && result.isDefaultSchemaTypeObject()) { + ArraySchema items = new ArraySchema(); + if (itemsNode.getNodeType().equals(JsonNodeType.OBJECT)) { + items.setItems(getSchema(itemsNode, location, result)); + } else if (itemsNode.getNodeType().equals(JsonNodeType.ARRAY)) { + for (JsonNode n : itemsNode) { + if (n.isValueNode()) { + items.setItems(getSchema(itemsNode, location, result)); + } + } + } + schema = items; + }else if (itemsNode != null) { JsonSchema items = new JsonSchema(); if (itemsNode.getNodeType().equals(JsonNodeType.OBJECT)) { items.setItems(getJsonSchema(itemsNode, location, result)); @@ -3613,7 +3645,16 @@ public Schema getJsonSchema(ObjectNode node, String location, ParseResult result ? getJsonSchema(additionalPropertiesObject, location, result) : additionalPropertiesBoolean; - if (additionalProperties != null) { + + if (additionalProperties != null && result.isDefaultSchemaTypeObject()) { + if (schema == null) { + schema = + additionalProperties.equals(Boolean.FALSE) + ? new ObjectSchema() + : new MapSchema(); + } + schema.setAdditionalProperties(additionalProperties); + }else if (additionalProperties != null) { if (schema == null) { schema = new JsonSchema(); } @@ -3689,7 +3730,9 @@ public Schema getJsonSchema(ObjectNode node, String location, ParseResult result if (node.get("default") != null) { //TODO rename method - schema.setDefault(getAnyExample("default",node,location,result)); + if(result.isDefaultSchemaTypeObject()) { + schema.setDefault(getAnyType("default", node, location, result)); + } } ObjectNode discriminatorNode = getObject("discriminator", node, false, location, result); @@ -4074,7 +4117,7 @@ public Schema getJsonSchema(ObjectNode node, String location, ParseResult result schema.setExamples(exampleList); } - Object example = getAnyExample("example", node, location, result); + Object example = getAnyType("example", node, location, result); if (example != null) { schema.setExample(example instanceof NullNode ? null : example); } @@ -4131,10 +4174,23 @@ public static class ParseResult { private List unique = new ArrayList<>(); private List uniqueTags = new ArrayList<>(); private List reserved = new ArrayList<>(); - + private boolean defaultSchemaTypeObject = true; private boolean openapi31 = false; private boolean oaiAuthor = false; + public boolean isDefaultSchemaTypeObject() { + return defaultSchemaTypeObject; + } + + public void setDefaultSchemaTypeObject(boolean defaultSchemaTypeObject) { + this.defaultSchemaTypeObject = defaultSchemaTypeObject; + } + + public ParseResult defaultSchemaTypeObject(boolean defaultSchemaTypeObject) { + this.defaultSchemaTypeObject = defaultSchemaTypeObject; + return this; + } + public ParseResult() { } diff --git a/modules/swagger-parser-v3/src/test/java/io/swagger/v3/parser/test/OAI31DeserializationTest.java b/modules/swagger-parser-v3/src/test/java/io/swagger/v3/parser/test/OAI31DeserializationTest.java index 05e93c2b7c..d8b8fd55b7 100644 --- a/modules/swagger-parser-v3/src/test/java/io/swagger/v3/parser/test/OAI31DeserializationTest.java +++ b/modules/swagger-parser-v3/src/test/java/io/swagger/v3/parser/test/OAI31DeserializationTest.java @@ -719,6 +719,7 @@ public void testTuplesJSONSchema() { @Test(description = "Test for not setting the schema type as default") public void testNotDefaultSchemaType() { ParseOptions options = new ParseOptions(); + options.setDefaultSchemaTypeObject(false); String defaultSchemaType = "openapi: 3.1.0\n" + "info:\n" + " title: ping test\n" + @@ -785,42 +786,39 @@ public void testNotDefaultSchemaType() { //map_any_value as object when it should be null assertNotNull(openAPI.getComponents().getSchemas().get("AnyValueModelInline").getProperties().get("map_any_value")); assertTrue(openAPI.getComponents().getSchemas().get("AnyValueModelInline").getProperties().get("map_any_value") instanceof Schema); - Schema mapProperty = (Schema)openAPI.getComponents().getSchemas().get("AnyValueModelInline").getProperties().get("map_any_value"); - assertNull(mapProperty.getType()); + Schema schema = (Schema)openAPI.getComponents().getSchemas().get("AnyValueModelInline").getProperties().get("map_any_value"); + assertNull(schema.getType()); //map_any_value_with_desc as object when it should be null assertNotNull(openAPI.getComponents().getSchemas().get("AnyValueModelInline").getProperties().get("map_any_value_with_desc")); assertTrue(openAPI.getComponents().getSchemas().get("AnyValueModelInline").getProperties().get("map_any_value_with_desc") instanceof Schema); - mapProperty = (Schema)openAPI.getComponents().getSchemas().get("AnyValueModelInline").getProperties().get("map_any_value_with_desc"); - assertNull(mapProperty.getType()); + schema = (Schema)openAPI.getComponents().getSchemas().get("AnyValueModelInline").getProperties().get("map_any_value_with_desc"); + assertNull(schema.getType()); //map_any_value_nullable as object when it should be null assertNotNull(openAPI.getComponents().getSchemas().get("AnyValueModelInline").getProperties().get("map_any_value_nullable")); assertTrue(openAPI.getComponents().getSchemas().get("AnyValueModelInline").getProperties().get("map_any_value_nullable") instanceof Schema); - mapProperty = (Schema)openAPI.getComponents().getSchemas().get("AnyValueModelInline").getProperties().get("map_any_value_nullable"); - assertNull(mapProperty.getType()); + schema = (Schema)openAPI.getComponents().getSchemas().get("AnyValueModelInline").getProperties().get("map_any_value_nullable"); + assertNull(schema.getType()); //array_any_value as array when it should be null assertNotNull(openAPI.getComponents().getSchemas().get("AnyValueModelInline").getProperties().get("array_any_value")); assertTrue(openAPI.getComponents().getSchemas().get("AnyValueModelInline").getProperties().get("array_any_value") instanceof Schema); - mapProperty = (Schema)openAPI.getComponents().getSchemas().get("AnyValueModelInline").getProperties().get("array_any_value"); - assertNull(mapProperty.getType()); + schema = (Schema)openAPI.getComponents().getSchemas().get("AnyValueModelInline").getProperties().get("array_any_value"); + assertNull(schema.getType()); //array_any_value_with_desc as array when it should be null assertNotNull(openAPI.getComponents().getSchemas().get("AnyValueModelInline").getProperties().get("array_any_value_with_desc")); assertTrue(openAPI.getComponents().getSchemas().get("AnyValueModelInline").getProperties().get("array_any_value_with_desc") instanceof Schema); - mapProperty = (Schema)openAPI.getComponents().getSchemas().get("AnyValueModelInline").getProperties().get("array_any_value_with_desc"); - assertNull(mapProperty.getType()); + schema = (Schema)openAPI.getComponents().getSchemas().get("AnyValueModelInline").getProperties().get("array_any_value_with_desc"); + assertNull(schema.getType()); //array_any_value_nullable assertNotNull(openAPI.getComponents().getSchemas().get("AnyValueModelInline").getProperties().get("array_any_value_nullable")); assertTrue(openAPI.getComponents().getSchemas().get("AnyValueModelInline").getProperties().get("array_any_value_nullable") instanceof Schema); - mapProperty = (Schema)openAPI.getComponents().getSchemas().get("AnyValueModelInline").getProperties().get("array_any_value_nullable"); - assertNull(mapProperty.getType()); - assertNull(mapProperty.getItems().getNullable()); - Yaml31.prettyPrint(mapProperty); - assertNotNull(mapProperty.getItems().getExtensions().get("nullable")); - //TODO check the toString Method difference between SOUT and YAML31 prettyprint - System.out.println(mapProperty); + schema = (Schema)openAPI.getComponents().getSchemas().get("AnyValueModelInline").getProperties().get("array_any_value_nullable"); + assertNull(schema.getType()); + assertNull(schema.getItems().getNullable()); + assertNotNull(schema.getItems().getExtensions().get("nullable")); } } diff --git a/modules/swagger-parser-v3/src/test/java/io/swagger/v3/parser/test/OpenAPIV3ParserTest.java b/modules/swagger-parser-v3/src/test/java/io/swagger/v3/parser/test/OpenAPIV3ParserTest.java index 2158b0a9d2..e39bed8f58 100644 --- a/modules/swagger-parser-v3/src/test/java/io/swagger/v3/parser/test/OpenAPIV3ParserTest.java +++ b/modules/swagger-parser-v3/src/test/java/io/swagger/v3/parser/test/OpenAPIV3ParserTest.java @@ -83,6 +83,99 @@ public class OpenAPIV3ParserTest { protected int serverPort = getDynamicPort(); protected WireMockServer wireMockServer; + @Test(description = "Test for not setting the schema type as default") + public void testNotDefaultSchemaType() { + ParseOptions options = new ParseOptions(); + options.setDefaultSchemaTypeObject(false); + String defaultSchemaType = "openapi: 3.0.0\n" + + "info:\n" + + " title: ping test\n" + + " version: '1.0'\n" + + "servers:\n" + + " - url: 'http://localhost:8000/'\n" + + "paths:\n" + + " /ping:\n" + + " get:\n" + + " operationId: pingGet\n" + + " responses:\n" + + " '201':\n" + + " description: OK\n" + + "components:\n" + + " schemas:\n" + + " AnyValueModelInline:\n" + + " description: test any value inline\n" + + " type: object\n" + + " properties:\n" + + " array_any_value:\n" + + " items: {}\n" + + " map_any_value:\n" + + " additionalProperties: {}\n" + + " any_value: {}\n" + + " any_value_with_desc:\n" + + " description: inline any value\n" + + " any_value_nullable:\n" + + " nullable: true\n" + + " description: inline any value nullable\n" + + " map_any_value_with_desc:\n" + + " additionalProperties: \n" + + " description: inline any value\n" + + " map_any_value_nullable:\n" + + " additionalProperties:\n" + + " nullable: true\n" + + " description: inline any value nullable\n" + + " array_any_value_with_desc:\n" + + " items: \n" + + " description: inline any value\n" + + " array_any_value_nullable:\n" + + " items:\n" + + " nullable: true\n" + + " description: inline any value nullable"; + SwaggerParseResult result = new OpenAPIV3Parser().readContents(defaultSchemaType, null, options); + OpenAPI openAPI = result.getOpenAPI(); + assertNotNull(openAPI); + + //map_any_value as object when it should be null + assertNotNull(openAPI.getComponents().getSchemas().get("AnyValueModelInline").getProperties().get("map_any_value")); + assertTrue(openAPI.getComponents().getSchemas().get("AnyValueModelInline").getProperties().get("map_any_value") instanceof Schema); + Schema schema = (Schema)openAPI.getComponents().getSchemas().get("AnyValueModelInline").getProperties().get("map_any_value"); + assertNull(schema.getType()); + + //map_any_value_with_desc as object when it should be null + assertNotNull(openAPI.getComponents().getSchemas().get("AnyValueModelInline").getProperties().get("map_any_value_with_desc")); + assertTrue(openAPI.getComponents().getSchemas().get("AnyValueModelInline").getProperties().get("map_any_value_with_desc") instanceof Schema); + schema = (Schema)openAPI.getComponents().getSchemas().get("AnyValueModelInline").getProperties().get("map_any_value_with_desc"); + assertNull(schema.getType()); + + //map_any_value_nullable as object when it should be null + assertNotNull(openAPI.getComponents().getSchemas().get("AnyValueModelInline").getProperties().get("map_any_value_nullable")); + assertTrue(openAPI.getComponents().getSchemas().get("AnyValueModelInline").getProperties().get("map_any_value_nullable") instanceof Schema); + schema = (Schema)openAPI.getComponents().getSchemas().get("AnyValueModelInline").getProperties().get("map_any_value_nullable"); + assertNull(schema.getType()); + + //array_any_value as array when it should be null + assertNotNull(openAPI.getComponents().getSchemas().get("AnyValueModelInline").getProperties().get("array_any_value")); + assertTrue(openAPI.getComponents().getSchemas().get("AnyValueModelInline").getProperties().get("array_any_value") instanceof Schema); + schema = (Schema)openAPI.getComponents().getSchemas().get("AnyValueModelInline").getProperties().get("array_any_value"); + assertNotNull(schema.getItems()); + assertNull(schema.getType()); + + //array_any_value_with_desc as array when it should be null + assertNotNull(openAPI.getComponents().getSchemas().get("AnyValueModelInline").getProperties().get("array_any_value_with_desc")); + assertTrue(openAPI.getComponents().getSchemas().get("AnyValueModelInline").getProperties().get("array_any_value_with_desc") instanceof Schema); + schema = (Schema)openAPI.getComponents().getSchemas().get("AnyValueModelInline").getProperties().get("array_any_value_with_desc"); + assertNotNull(((Schema)openAPI.getComponents().getSchemas().get("AnyValueModelInline").getProperties().get("array_any_value_with_desc")).getItems().getDescription()); + assertNull(schema.getType()); + + //array_any_value_nullable + assertNotNull(openAPI.getComponents().getSchemas().get("AnyValueModelInline").getProperties().get("array_any_value_nullable")); + assertTrue(openAPI.getComponents().getSchemas().get("AnyValueModelInline").getProperties().get("array_any_value_nullable") instanceof Schema); + schema = (Schema)openAPI.getComponents().getSchemas().get("AnyValueModelInline").getProperties().get("array_any_value_nullable"); + assertNull(schema.getType()); + assertNotNull(schema.getItems()); + assertNotNull(schema.getItems().getDescription()); + assertTrue(schema.getItems().getNullable()); + } + @Test public void testIssue1561() { ParseOptions options = new ParseOptions();