Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 935b984

Browse files
Polymorphic fields on polymorphic parents don't get correct oneOf docs generated. Fixes #2597
1 parent 8a1e0ad commit 935b984

File tree

10 files changed

+666
-202
lines changed

10 files changed

+666
-202
lines changed

‎springdoc-openapi-starter-common/src/main/java/org/springdoc/core/converters/PolymorphicModelConverter.java

Lines changed: 32 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,9 @@
2727
import java.lang.reflect.Modifier;
2828
import java.util.ArrayList;
2929
import java.util.Collection;
30+
import java.util.Collections;
3031
import java.util.Iterator;
3132
import java.util.List;
32-
import java.util.stream.Collectors;
3333

3434
import com.fasterxml.jackson.databind.JavaType;
3535
import io.swagger.v3.core.converter.AnnotatedType;
@@ -43,6 +43,7 @@
4343

4444
/**
4545
* The type Polymorphic model converter.
46+
*
4647
* @author bnasslahsen
4748
*/
4849
public class PolymorphicModelConverter implements ModelConverter {
@@ -52,6 +53,17 @@ public class PolymorphicModelConverter implements ModelConverter {
5253
*/
5354
private final ObjectMapperProvider springDocObjectMapper;
5455

56+
/**
57+
* The constant PARENT_TYPES_TO_IGNORE.
58+
*/
59+
private static final List<String> PARENT_TYPES_TO_IGNORE = Collections.synchronizedList(new ArrayList<>());
60+
61+
static {
62+
PARENT_TYPES_TO_IGNORE.add("JsonSchema");
63+
PARENT_TYPES_TO_IGNORE.add("Pageable");
64+
PARENT_TYPES_TO_IGNORE.add("EntityModel");
65+
}
66+
5567
/**
5668
* Instantiates a new Polymorphic model converter.
5769
*
@@ -61,12 +73,21 @@ public PolymorphicModelConverter(ObjectMapperProvider springDocObjectMapper) {
6173
this.springDocObjectMapper = springDocObjectMapper;
6274
}
6375

64-
private static Schema<?> getResolvedSchema(JavaType javaType, Schema<?> resolvedSchema) {
76+
/**
77+
* Add parent type.
78+
*
79+
* @param parentTypes the parent types
80+
*/
81+
public static void addParentType(String... parentTypes) {
82+
PARENT_TYPES_TO_IGNORE.addAll(List.of(parentTypes));
83+
}
84+
85+
private Schema<?> getResolvedSchema(JavaType javaType, Schema<?> resolvedSchema) {
6586
if (resolvedSchema instanceof ObjectSchema && resolvedSchema.getProperties() != null) {
66-
if (resolvedSchema.getProperties().containsKey(javaType.getRawClass().getName())){
87+
if (resolvedSchema.getProperties().containsKey(javaType.getRawClass().getName())){
6788
resolvedSchema = resolvedSchema.getProperties().get(javaType.getRawClass().getName());
6889
}
69-
else if (resolvedSchema.getProperties().containsKey(javaType.getRawClass().getSimpleName())){
90+
else if (resolvedSchema.getProperties().containsKey(javaType.getRawClass().getSimpleName())){
7091
resolvedSchema = resolvedSchema.getProperties().get(javaType.getRawClass().getSimpleName());
7192
}
7293
}
@@ -78,6 +99,9 @@ public Schema resolve(AnnotatedType type, ModelConverterContext context, Iterato
7899
JavaType javaType = springDocObjectMapper.jsonMapper().constructType(type.getType());
79100
if (javaType != null) {
80101
if (chain.hasNext()) {
102+
if (!type.isResolveAsRef() && type.getParent() != null
103+
&& PARENT_TYPES_TO_IGNORE.stream().noneMatch(ignore -> type.getParent().getName().startsWith(ignore)))
104+
type.resolveAsRef(true);
81105
Schema<?> resolvedSchema = chain.next().resolve(type, context, chain);
82106
resolvedSchema = getResolvedSchema(javaType, resolvedSchema);
83107
if (resolvedSchema == null || resolvedSchema.get$ref() == null)
@@ -91,8 +115,8 @@ public Schema resolve(AnnotatedType type, ModelConverterContext context, Iterato
91115
/**
92116
* Compose polymorphic schema.
93117
*
94-
* @param type the type
95-
* @param schema the schema
118+
* @param type the type
119+
* @param schema the schema
96120
* @param schemas the schemas
97121
* @return the schema
98122
*/
@@ -111,7 +135,7 @@ private Schema composePolymorphicSchema(AnnotatedType type, Schema schema, Colle
111135
/**
112136
* Find composed schemas recursively.
113137
*
114-
* @param ref the reference of the schema
138+
* @param ref the reference of the schema
115139
* @param schemas the collection of schemas to search in
116140
* @return the list of composed schemas
117141
*/
@@ -133,6 +157,7 @@ private List<Schema> findComposedSchemas(String ref, Collection<Schema> schemas)
133157

134158
return resultSchemas;
135159
}
160+
136161
/**
137162
* Is concrete class boolean.
138163
*

‎springdoc-openapi-starter-common/src/main/java/org/springdoc/core/utils/SpringDocUtils.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
import org.springdoc.api.AbstractOpenApiResource;
3333
import org.springdoc.core.converters.AdditionalModelsConverter;
3434
import org.springdoc.core.converters.ConverterUtils;
35+
import org.springdoc.core.converters.PolymorphicModelConverter;
3536
import org.springdoc.core.converters.SchemaPropertyDeprecatingConverter;
3637
import org.springdoc.core.extractor.MethodParameterPojoExtractor;
3738
import org.springdoc.core.service.AbstractRequestService;
@@ -388,5 +389,16 @@ public static boolean isValidPath(String path) {
388389
return true;
389390
return false;
390391
}
392+
393+
/**
394+
* Add parent type spring doc utils.
395+
*
396+
* @param parentTypes the parent types
397+
* @return the spring doc utils
398+
*/
399+
public SpringDocUtils addParentType(String ...parentTypes) {
400+
PolymorphicModelConverter.addParentType(parentTypes);
401+
return this;
402+
}
391403
}
392404

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package test.org.springdoc.api.v30.app223;
2+
3+
4+
import test.org.springdoc.api.v30.app223.apiobjects.AbstractChild;
5+
import test.org.springdoc.api.v30.app223.apiobjects.AbstractParent;
6+
import test.org.springdoc.api.v30.app223.apiobjects.Response;
7+
8+
import org.springframework.web.bind.annotation.PostMapping;
9+
import org.springframework.web.bind.annotation.RequestBody;
10+
import org.springframework.web.bind.annotation.RestController;
11+
12+
@RestController
13+
public class ARestController {
14+
@PostMapping("/parent")
15+
public Response parentEndpoint(@RequestBody AbstractParent parent) {
16+
return null;
17+
}
18+
19+
@PostMapping("/child")
20+
public Response childEndpoint(@RequestBody AbstractChild child) {
21+
return null;
22+
}
23+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
/*
2+
*
3+
* *
4+
* * *
5+
* * * *
6+
* * * * * Copyright 2019-2022 the original author or authors.
7+
* * * * *
8+
* * * * * Licensed under the Apache License, Version 2.0 (the "License");
9+
* * * * * you may not use this file except in compliance with the License.
10+
* * * * * You may obtain a copy of the License at
11+
* * * * *
12+
* * * * * https://www.apache.org/licenses/LICENSE-2.0
13+
* * * * *
14+
* * * * * Unless required by applicable law or agreed to in writing, software
15+
* * * * * distributed under the License is distributed on an "AS IS" BASIS,
16+
* * * * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17+
* * * * * See the License for the specific language governing permissions and
18+
* * * * * limitations under the License.
19+
* * * *
20+
* * *
21+
* *
22+
*
23+
*/
24+
25+
package test.org.springdoc.api.v30.app223;
26+
27+
import test.org.springdoc.api.v30.AbstractSpringDocV30Test;
28+
29+
import org.springframework.boot.autoconfigure.SpringBootApplication;
30+
31+
public class SpringDocApp223Test extends AbstractSpringDocV30Test {
32+
33+
@SpringBootApplication
34+
static class SpringDocTestApp {}
35+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
package test.org.springdoc.api.v30.app223.apiobjects;
2+
3+
import com.fasterxml.jackson.annotation.JsonSubTypes;
4+
import com.fasterxml.jackson.annotation.JsonSubTypes.Type;
5+
import com.fasterxml.jackson.annotation.JsonTypeInfo;
6+
import com.fasterxml.jackson.annotation.JsonTypeInfo.Id;
7+
8+
@JsonTypeInfo(use = Id.NAME, property = "type")
9+
@JsonSubTypes({
10+
@Type(ChildType1.class),
11+
@Type(ChildType2.class)
12+
})
13+
public abstract class AbstractChild {
14+
private int id;
15+
16+
public AbstractChild(int id) {
17+
this.id = id;
18+
}
19+
20+
public int getId() {
21+
return id;
22+
}
23+
24+
public void setId(int id) {
25+
this.id = id;
26+
}
27+
}
28+
29+
class ChildType1 extends AbstractChild {
30+
private String childType1Param;
31+
32+
public ChildType1(int id, String childType1Param) {
33+
super(id);
34+
this.childType1Param = childType1Param;
35+
}
36+
37+
public String getChildType1Param() {
38+
return childType1Param;
39+
}
40+
41+
public void setChildType1Param(String childType1Param) {
42+
this.childType1Param = childType1Param;
43+
}
44+
}
45+
46+
class ChildType2 extends AbstractChild {
47+
private String childType2Param;
48+
49+
public ChildType2(int id, String childType2Param) {
50+
super(id);
51+
this.childType2Param = childType2Param;
52+
}
53+
54+
public String getChildType2Param() {
55+
return childType2Param;
56+
}
57+
58+
public void setChildType2Param(String childType2Param) {
59+
this.childType2Param = childType2Param;
60+
}
61+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
package test.org.springdoc.api.v30.app223.apiobjects;
2+
3+
import com.fasterxml.jackson.annotation.JsonSubTypes;
4+
import com.fasterxml.jackson.annotation.JsonSubTypes.Type;
5+
import com.fasterxml.jackson.annotation.JsonTypeInfo;
6+
import com.fasterxml.jackson.annotation.JsonTypeInfo.Id;
7+
8+
9+
@JsonTypeInfo(use = Id.NAME, property = "type")
10+
@JsonSubTypes({
11+
@Type(ParentType1.class),
12+
@Type(ParentType2.class)
13+
})
14+
public abstract class AbstractParent {
15+
private int id;
16+
17+
public AbstractParent(int id) {
18+
this.id = id;
19+
}
20+
21+
public int getId() {
22+
return id;
23+
}
24+
25+
public void setId(int id) {
26+
this.id = id;
27+
}
28+
}
29+
30+
class ParentType1 extends AbstractParent {
31+
private String parentType1Param;
32+
private AbstractChild abstractChild;
33+
34+
public ParentType1(int id, String parentType1Param, AbstractChild abstractChild) {
35+
super(id);
36+
this.parentType1Param = parentType1Param;
37+
this.abstractChild = abstractChild;
38+
}
39+
40+
public String getParentType1Param() {
41+
return parentType1Param;
42+
}
43+
44+
public void setParentType1Param(String parentType1Param) {
45+
this.parentType1Param = parentType1Param;
46+
}
47+
48+
public AbstractChild getAbstractChild() {
49+
return abstractChild;
50+
}
51+
52+
public void setAbstractChild(AbstractChild abstractChild) {
53+
this.abstractChild = abstractChild;
54+
}
55+
}
56+
57+
class ParentType2 extends AbstractParent {
58+
private String parentType2Param;
59+
60+
public ParentType2(int id, String parentType2Param) {
61+
super(id);
62+
this.parentType2Param = parentType2Param;
63+
}
64+
65+
public String getParentType2Param() {
66+
return parentType2Param;
67+
}
68+
69+
public void setParentType2Param(String parentType2Param) {
70+
this.parentType2Param = parentType2Param;
71+
}
72+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
package test.org.springdoc.api.v30.app223.apiobjects;
2+
3+
public record Response(AbstractParent abstractParent, AbstractChild abstractChild) {
4+
}

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /