Skip to content

Commit be925d4

Browse files
committed
Objects properties order is not preserved with springdoc.api-docs.resolve-schema-properties = true. Fixes #1274.
1 parent e1d9f10 commit be925d4

File tree

6 files changed

+157
-4
lines changed

6 files changed

+157
-4
lines changed

springdoc-openapi-common/src/main/java/org/springdoc/core/OpenAPIService.java

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,11 @@
2626
import java.util.Collections;
2727
import java.util.HashMap;
2828
import java.util.HashSet;
29+
import java.util.LinkedHashMap;
2930
import java.util.List;
3031
import java.util.Locale;
3132
import java.util.Map;
33+
import java.util.Map.Entry;
3234
import java.util.Optional;
3335
import java.util.Set;
3436
import java.util.function.Consumer;
@@ -396,10 +398,11 @@ public Schema resolveProperties(Schema schema, PropertyResolverUtils propertyRes
396398

397399
Map<String, Schema> properties = schema.getProperties();
398400
if (!CollectionUtils.isEmpty(properties)) {
399-
Map<String, Schema> resolvedSchemas = properties.entrySet().stream().map(es -> {
401+
LinkedHashMap<String, Schema> resolvedSchemas = properties.entrySet().stream().map(es -> {
400402
es.setValue(resolveProperties(es.getValue(), propertyResolverUtils, locale));
401403
return es;
402-
}).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
404+
}).collect(Collectors.toMap(Entry::getKey, Entry::getValue, (e1, e2) -> e2,
405+
LinkedHashMap::new));
403406
schema.setProperties(resolvedSchemas);
404407
}
405408

@@ -544,7 +547,7 @@ private void resolveProperty(Supplier<String> getProperty, Consumer<String> setP
544547
*
545548
* @param components the components
546549
*/
547-
private void calculateSecuritySchemes(Components components,Locale locale) {
550+
private void calculateSecuritySchemes(Components components, Locale locale) {
548551
// Look for SecurityScheme in a spring managed bean
549552
Map<String, Object> securitySchemeBeans = context
550553
.getBeansWithAnnotation(io.swagger.v3.oas.annotations.security.SecurityScheme.class);

springdoc-openapi-common/src/main/java/org/springdoc/core/SpringDocConfiguration.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@
7171
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
7272
import org.springframework.http.HttpStatus;
7373
import org.springframework.http.ResponseEntity;
74+
import org.springframework.util.CollectionUtils;
7475
import org.springframework.web.bind.annotation.ExceptionHandler;
7576
import org.springframework.web.bind.annotation.ResponseStatus;
7677
import org.springframework.web.bind.annotation.RestControllerAdvice;
@@ -326,7 +327,8 @@ OpenApiCustomiser propertiesResolverForSchema(PropertyResolverUtils propertyReso
326327
return openApi -> {
327328
Components components = openApi.getComponents();
328329
Map<String, Schema> schemas = components.getSchemas();
329-
schemas.values().forEach(schema -> openAPIService.resolveProperties(schema, propertyResolverUtils, Locale.getDefault()));
330+
if (!CollectionUtils.isEmpty(schemas))
331+
schemas.values().forEach(schema -> openAPIService.resolveProperties(schema, propertyResolverUtils, Locale.getDefault()));
330332
};
331333
}
332334

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
package test.org.springdoc.api.app164;
2+
3+
public class SampleResponseClass {
4+
5+
private String idAsFirstParameter;
6+
7+
private String nameAsSecondParamater;
8+
9+
private String lastNameAsThirdParameter;
10+
11+
private boolean booleanValueAsFourthParameter;
12+
13+
public String getIdAsFirstParameter() {
14+
return idAsFirstParameter;
15+
}
16+
17+
public void setIdAsFirstParameter(String idAsFirstParameter) {
18+
this.idAsFirstParameter = idAsFirstParameter;
19+
}
20+
21+
public String getNameAsSecondParamater() {
22+
return nameAsSecondParamater;
23+
}
24+
25+
public void setNameAsSecondParamater(String nameAsSecondParamater) {
26+
this.nameAsSecondParamater = nameAsSecondParamater;
27+
}
28+
29+
public String getLastNameAsThirdParameter() {
30+
return lastNameAsThirdParameter;
31+
}
32+
33+
public void setLastNameAsThirdParameter(String lastNameAsThirdParameter) {
34+
this.lastNameAsThirdParameter = lastNameAsThirdParameter;
35+
}
36+
37+
public boolean isBooleanValueAsFourthParameter() {
38+
return booleanValueAsFourthParameter;
39+
}
40+
41+
public void setBooleanValueAsFourthParameter(boolean aBooleanValueAsFourthParameter) {
42+
this.booleanValueAsFourthParameter = aBooleanValueAsFourthParameter;
43+
}
44+
45+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
/*
2+
*
3+
* * Copyright 2019-2021 the original author or authors.
4+
* *
5+
* * Licensed under the Apache License, Version 2.0 (the "License");
6+
* * you may not use this file except in compliance with the License.
7+
* * You may obtain a copy of the License at
8+
* *
9+
* * https://www.apache.org/licenses/LICENSE-2.0
10+
* *
11+
* * Unless required by applicable law or agreed to in writing, software
12+
* * distributed under the License is distributed on an "AS IS" BASIS,
13+
* * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* * See the License for the specific language governing permissions and
15+
* * limitations under the License.
16+
*
17+
*/
18+
19+
package test.org.springdoc.api.app164;
20+
21+
import test.org.springdoc.api.AbstractSpringDocTest;
22+
23+
import org.springframework.boot.autoconfigure.SpringBootApplication;
24+
import org.springframework.test.context.TestPropertySource;
25+
26+
@TestPropertySource(properties = "springdoc.api-docs.resolve-schema-properties=true")
27+
public class SpringDocApp164Test extends AbstractSpringDocTest {
28+
29+
@SpringBootApplication
30+
static class SpringDocTestApp {
31+
}
32+
33+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package test.org.springdoc.api.app164;
2+
3+
import org.springframework.web.bind.annotation.GetMapping;
4+
import org.springframework.web.bind.annotation.RestController;
5+
6+
@RestController
7+
public class TestApiController {
8+
9+
@GetMapping(value = "/test")
10+
public SampleResponseClass getInvoices() {
11+
return null;
12+
}
13+
14+
}
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
{
2+
"openapi": "3.0.1",
3+
"info": {
4+
"title": "OpenAPI definition",
5+
"version": "v0"
6+
},
7+
"servers": [
8+
{
9+
"url": "http://localhost",
10+
"description": "Generated server url"
11+
}
12+
],
13+
"paths": {
14+
"/test": {
15+
"get": {
16+
"tags": [
17+
"test-api-controller"
18+
],
19+
"operationId": "getInvoices",
20+
"responses": {
21+
"200": {
22+
"description": "OK",
23+
"content": {
24+
"*/*": {
25+
"schema": {
26+
"$ref": "#/components/schemas/SampleResponseClass"
27+
}
28+
}
29+
}
30+
}
31+
}
32+
}
33+
}
34+
},
35+
"components": {
36+
"schemas": {
37+
"SampleResponseClass": {
38+
"type": "object",
39+
"properties": {
40+
"idAsFirstParameter": {
41+
"type": "string"
42+
},
43+
"nameAsSecondParamater": {
44+
"type": "string"
45+
},
46+
"lastNameAsThirdParameter": {
47+
"type": "string"
48+
},
49+
"booleanValueAsFourthParameter": {
50+
"type": "boolean"
51+
}
52+
}
53+
}
54+
}
55+
}
56+
}

0 commit comments

Comments
 (0)