Skip to content

Commit

Permalink
Multiple Superclasses Are Not Mapped To Multiple allOf If Used In Dif…
Browse files Browse the repository at this point in the history
…ferent Services. Fixes #2601.
  • Loading branch information
bnasslahsen committed Jun 29, 2024
1 parent fa35308 commit 8a1e0ad
Show file tree
Hide file tree
Showing 5 changed files with 225 additions and 6 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ private List<Schema> findComposedSchemas(String ref, Collection<Schema> schemas)
.filter(s -> s.getAllOf() != null)
.filter(s -> s.getAllOf().stream().anyMatch(s2 -> ref.equals(s2.get$ref())))
.map(s -> new Schema().$ref(AnnotationsUtils.COMPONENTS_REF + s.getName()))
.collect(Collectors.toList());
.toList();

List<Schema> resultSchemas = new ArrayList<>(composedSchemas);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,11 @@
import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;

import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonView;
Expand Down Expand Up @@ -65,6 +67,7 @@

/**
* The type Spring doc annotations utils.
*
* @author bnasslahsen
*/
@SuppressWarnings({ "rawtypes" })
Expand Down Expand Up @@ -142,10 +145,21 @@ public static Schema extractSchema(Components components, Type returnType, JsonV
for (Map.Entry<String, Schema> entry : schemaMap.entrySet()) {
// If we've seen this schema before but find later it should be polymorphic,
// replace the existing schema with this richer version.
Schema existingSchema = componentSchemas.get(entry.getKey());
if (!componentSchemas.containsKey(entry.getKey()) ||
(!entry.getValue().getClass().equals(componentSchemas.get(entry.getKey()).getClass()) && entry.getValue().getAllOf() != null)) {
(!entry.getValue().getClass().equals(existingSchema.getClass()) && entry.getValue().getAllOf() != null)) {
componentSchemas.put(entry.getKey(), entry.getValue());
}
else if (componentSchemas.containsKey(entry.getKey()) && schemaMap.containsKey(entry.getKey())) {
// Check to merge polymorphic types
Set<Schema> existingAllOf = new LinkedHashSet<>();
if(existingSchema.getAllOf() != null)
existingAllOf.addAll(existingSchema.getAllOf());
if (schemaMap.get(entry.getKey()).getAllOf() != null){
existingAllOf.addAll(schemaMap.get(entry.getKey()).getAllOf());
existingSchema.setAllOf(new ArrayList<>(existingAllOf));
}
}
}
components.setSchemas(componentSchemas);
}
Expand Down Expand Up @@ -207,8 +221,8 @@ public static Optional<Content> getContent(io.swagger.v3.oas.annotations.media.C
* Merge schema.
*
* @param existingContent the existing content
* @param schemaN the schema n
* @param mediaTypeStr the media type str
* @param schemaN the schema n
* @param mediaTypeStr the media type str
*/
public static void mergeSchema(Content existingContent, Schema<?> schemaN, String mediaTypeStr) {
if (existingContent.containsKey(mediaTypeStr)) {
Expand Down Expand Up @@ -322,7 +336,7 @@ private static void addExtension(io.swagger.v3.oas.annotations.media.Content ann
* Sets examples.
*
* @param mediaType the media type
* @param examples the examples
* @param examples the examples
*/
private static void setExamples(MediaType mediaType, ExampleObject[] examples) {
if (examples.length == 1 && StringUtils.isBlank(examples[0].name())) {
Expand Down Expand Up @@ -436,7 +450,7 @@ private static boolean isArray(io.swagger.v3.oas.annotations.media.Content annot
* Resolve default value object.
*
* @param defaultValueStr the default value str
* @param objectMapper the object mapper
* @param objectMapper the object mapper
* @return the object
*/
public static Object resolveDefaultValue(String defaultValueStr, ObjectMapper objectMapper) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package test.org.springdoc.api.v30.app222;



import test.org.springdoc.api.v30.app222.SpringDocApp222Test.FirstHierarchyUser;
import test.org.springdoc.api.v30.app222.SpringDocApp222Test.SecondHierarchyUser;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

/**
* @author bnasslahsen
*/

@RestController
class HelloController {

@GetMapping("/hello1")
public FirstHierarchyUser getItems1() {
return null;
}

@GetMapping("/hello2")
public SecondHierarchyUser getItems2() {
return null;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/*
*
* *
* * *
* * * *
* * * * * Copyright 2019-2022 the original author or authors.
* * * * *
* * * * * Licensed under the Apache License, Version 2.0 (the "License");
* * * * * you may not use this file except in compliance with the License.
* * * * * You may obtain a copy of the License at
* * * * *
* * * * * https://www.apache.org/licenses/LICENSE-2.0
* * * * *
* * * * * Unless required by applicable law or agreed to in writing, software
* * * * * distributed under the License is distributed on an "AS IS" BASIS,
* * * * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* * * * * See the License for the specific language governing permissions and
* * * * * limitations under the License.
* * * *
* * *
* *
*
*/

package test.org.springdoc.api.v30.app222;

import com.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonSubTypes.Type;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.annotation.JsonTypeInfo.Id;
import test.org.springdoc.api.v30.AbstractSpringDocV30Test;

import org.springframework.boot.autoconfigure.SpringBootApplication;

public class SpringDocApp222Test extends AbstractSpringDocV30Test {

@SpringBootApplication
static class SpringDocTestApp {}

@JsonTypeInfo(use = Id.NAME, property = "@type")
@JsonSubTypes(@Type(CommonImplementor.class))
interface FirstHierarchy {}

@JsonTypeInfo(use = Id.NAME, property = "@type")
@JsonSubTypes(@Type(CommonImplementor.class))
interface SecondHierarchy {}

class CommonImplementor implements FirstHierarchy, SecondHierarchy {}

record CommonImplementorUser(FirstHierarchy firstHierarchy, SecondHierarchy secondHierarchy) {}

record FirstHierarchyUser(FirstHierarchy firstHierarchy) {}

record SecondHierarchyUser(SecondHierarchy secondHierarchy) {}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
{
"openapi": "3.0.1",
"info": {
"title": "OpenAPI definition",
"version": "v0"
},
"servers": [
{
"url": "http://localhost",
"description": "Generated server url"
}
],
"paths": {
"/hello2": {
"get": {
"tags": [
"hello-controller"
],
"operationId": "getItems2",
"responses": {
"200": {
"description": "OK",
"content": {
"*/*": {
"schema": {
"$ref": "#/components/schemas/SecondHierarchyUser"
}
}
}
}
}
}
},
"/hello1": {
"get": {
"tags": [
"hello-controller"
],
"operationId": "getItems1",
"responses": {
"200": {
"description": "OK",
"content": {
"*/*": {
"schema": {
"$ref": "#/components/schemas/FirstHierarchyUser"
}
}
}
}
}
}
}
},
"components": {
"schemas": {
"CommonImplementor": {
"type": "object",
"allOf": [
{
"$ref": "#/components/schemas/SecondHierarchy"
},
{
"$ref": "#/components/schemas/FirstHierarchy"
}
]
},
"SecondHierarchy": {
"required": [
"@type"
],
"type": "object",
"properties": {
"@type": {
"type": "string"
}
},
"discriminator": {
"propertyName": "@type"
}
},
"SecondHierarchyUser": {
"type": "object",
"properties": {
"secondHierarchy": {
"oneOf": [
{
"$ref": "#/components/schemas/CommonImplementor"
}
]
}
}
},
"FirstHierarchy": {
"required": [
"@type"
],
"type": "object",
"properties": {
"@type": {
"type": "string"
}
},
"discriminator": {
"propertyName": "@type"
}
},
"FirstHierarchyUser": {
"type": "object",
"properties": {
"firstHierarchy": {
"oneOf": [
{
"$ref": "#/components/schemas/CommonImplementor"
}
]
}
}
}
}
}
}

0 comments on commit 8a1e0ad

Please sign in to comment.