Skip to content

Commit 736bc11

Browse files
committed
Merge branch 'e-build-trim-indent-apply-schema', fixes #2645
2 parents b7777e4 + b07e360 commit 736bc11

File tree

5 files changed

+246
-24
lines changed

5 files changed

+246
-24
lines changed

Diff for: springdoc-openapi-starter-common/src/main/java/org/springdoc/api/AbstractOpenApiResource.java

+73-3
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@
6969
import io.swagger.v3.oas.models.PathItem.HttpMethod;
7070
import io.swagger.v3.oas.models.Paths;
7171
import io.swagger.v3.oas.models.SpecVersion;
72+
import io.swagger.v3.oas.models.media.Schema;
7273
import io.swagger.v3.oas.models.media.StringSchema;
7374
import io.swagger.v3.oas.models.parameters.Parameter;
7475
import io.swagger.v3.oas.models.responses.ApiResponses;
@@ -100,6 +101,7 @@
100101
import org.springdoc.core.service.GenericResponseService;
101102
import org.springdoc.core.service.OpenAPIService;
102103
import org.springdoc.core.service.OperationService;
104+
import org.springdoc.core.utils.PropertyResolverUtils;
103105
import org.springdoc.core.utils.SpringDocUtils;
104106

105107
import org.springframework.aop.support.AopUtils;
@@ -352,6 +354,9 @@ protected OpenAPI getOpenApi(Locale locale) {
352354
}
353355
getPaths(mappingsMap, finalLocale, openAPI);
354356

357+
if (springDocConfigProperties.isTrimKotlinIndent())
358+
this.trimIndent(openAPI);
359+
355360
Optional<CloudFunctionProvider> cloudFunctionProviderOptional = springDocProviders.getSpringCloudFunctionProvider();
356361
cloudFunctionProviderOptional.ifPresent(cloudFunctionProvider -> {
357362
List<RouterOperation> routerOperationList = cloudFunctionProvider.getRouterOperations(openAPI);
@@ -384,7 +389,6 @@ protected OpenAPI getOpenApi(Locale locale) {
384389
if (!CollectionUtils.isEmpty(openAPI.getServers()) && !openAPI.getServers().equals(serversCopy))
385390
openAPIService.setServersPresent(true);
386391

387-
388392
openAPIService.setCachedOpenAPI(openAPI, finalLocale);
389393

390394
LOGGER.info("Init duration for springdoc-openapi is: {} ms",
@@ -396,12 +400,78 @@ protected OpenAPI getOpenApi(Locale locale) {
396400
openAPIService.updateServers(openAPI);
397401
}
398402
openAPIService.updateServers(openAPI);
399-
return openAPI; }
400-
finally {
403+
return openAPI;
404+
} finally {
401405
this.reentrantLock.unlock();
402406
}
403407
}
404408

409+
/**
410+
* Indents are removed for properties that are mainly used as “explanations” using Open API.
411+
* @param openAPI the open api
412+
*/
413+
private void trimIndent(OpenAPI openAPI) {
414+
trimComponents(openAPI);
415+
trimPaths(openAPI);
416+
}
417+
418+
/**
419+
* Trim the indent for descriptions in the 'components' of open api.
420+
* @param openAPI the open api
421+
*/
422+
private void trimComponents(OpenAPI openAPI) {
423+
final PropertyResolverUtils propertyResolverUtils = operationParser.getPropertyResolverUtils();
424+
if (openAPI.getComponents() == null || openAPI.getComponents().getSchemas() == null) {
425+
return;
426+
}
427+
for (Schema<?> schema : openAPI.getComponents().getSchemas().values()) {
428+
schema.description(propertyResolverUtils.trimIndent(schema.getDescription()));
429+
if (schema.getProperties() == null) {
430+
continue;
431+
}
432+
for (Object prop : schema.getProperties().values()) {
433+
if (prop instanceof Schema<?> schemaProp) {
434+
schemaProp.setDescription(propertyResolverUtils.trimIndent(schemaProp.getDescription()));
435+
}
436+
}
437+
}
438+
}
439+
440+
/**
441+
* Trim the indent for descriptions in the 'paths' of open api.
442+
* @param openAPI the open api
443+
*/
444+
private void trimPaths(OpenAPI openAPI) {
445+
final PropertyResolverUtils propertyResolverUtils = operationParser.getPropertyResolverUtils();
446+
if (openAPI.getPaths() == null) {
447+
return;
448+
}
449+
for (PathItem value : openAPI.getPaths().values()) {
450+
value.setDescription(propertyResolverUtils.trimIndent(value.getDescription()));
451+
trimIndentOperation(value.getGet());
452+
trimIndentOperation(value.getPut());
453+
trimIndentOperation(value.getPost());
454+
trimIndentOperation(value.getDelete());
455+
trimIndentOperation(value.getOptions());
456+
trimIndentOperation(value.getHead());
457+
trimIndentOperation(value.getPatch());
458+
trimIndentOperation(value.getTrace());
459+
}
460+
}
461+
462+
/**
463+
* Trim the indent for 'operation'
464+
* @param operation the operation
465+
*/
466+
private void trimIndentOperation(Operation operation) {
467+
final PropertyResolverUtils propertyResolverUtils = operationParser.getPropertyResolverUtils();
468+
if (operation == null) {
469+
return;
470+
}
471+
operation.setSummary(propertyResolverUtils.trimIndent(operation.getSummary()));
472+
operation.setDescription(propertyResolverUtils.trimIndent(operation.getDescription()));
473+
}
474+
405475
/**
406476
* Gets paths.
407477
*

Diff for: springdoc-openapi-starter-common/src/main/java/org/springdoc/core/service/OperationService.java

+8
Original file line numberDiff line numberDiff line change
@@ -646,4 +646,12 @@ public Operation mergeOperation(Operation operation, Operation operationModel) {
646646
public JavadocProvider getJavadocProvider() {
647647
return parameterBuilder.getJavadocProvider();
648648
}
649+
650+
/**
651+
* Gets propertyResolverUtils
652+
* @return propertyResolverUtils
653+
*/
654+
public PropertyResolverUtils getPropertyResolverUtils(){
655+
return parameterBuilder.getPropertyResolverUtils();
656+
}
649657
}

Diff for: springdoc-openapi-starter-common/src/main/java/org/springdoc/core/utils/PropertyResolverUtils.java

+17-15
Original file line numberDiff line numberDiff line change
@@ -99,9 +99,6 @@ public String resolve(String parameterProperty, Locale locale) {
9999
}
100100
if (parameterProperty.equals(result))
101101
try {
102-
if (springDocConfigProperties.isTrimKotlinIndent()) {
103-
parameterProperty = trimIndent(parameterProperty);
104-
}
105102
result = factory.resolveEmbeddedValue(parameterProperty);
106103
}
107104
catch (IllegalArgumentException ex) {
@@ -119,18 +116,23 @@ public String resolve(String parameterProperty, Locale locale) {
119116
* @param text The original string with possible leading indentation.
120117
* @return The string with leading indentation removed from each line.
121118
*/
122-
private String trimIndent(String text) {
123-
if (text == null) {
124-
return null;
119+
public String trimIndent(String text) {
120+
try {
121+
if (text == null) {
122+
return null;
123+
}
124+
final String newLine = "\n";
125+
String[] lines = text.split(newLine);
126+
int minIndent = resolveMinIndent(lines);
127+
return Arrays.stream(lines)
128+
.map(line -> line.substring(Math.min(line.length(), minIndent)))
129+
.reduce((a, b) -> a + newLine + b)
130+
.orElse(StringUtils.EMPTY);
131+
} catch (Exception ex){
132+
LOGGER.warn(ex.getMessage());
133+
return text;
125134
}
126-
final String newLine = "\n";
127-
String[] lines = text.split(newLine);
128-
int minIndent = resolveMinIndent(lines);
129-
return Arrays.stream(lines)
130-
.map(line -> line.substring(Math.min(line.length(), minIndent)))
131-
.reduce((a, b) -> a + newLine + b)
132-
.orElse(StringUtils.EMPTY);
133-
}
135+
}
134136

135137
private int resolveMinIndent(String[] lines) {
136138
return Arrays.stream(lines)
@@ -222,4 +224,4 @@ public Map<String, Object> resolveExtensions(Locale locale, Map<String, Object>
222224
else
223225
return extensions;
224226
}
225-
}
227+
}

Diff for: springdoc-openapi-tests/springdoc-openapi-kotlin-webmvc-tests/src/test/kotlin/test/org/springdoc/api/app11/ExampleController.kt

+69-3
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
package test.org.springdoc.api.app11
22

33
import io.swagger.v3.oas.annotations.Operation
4+
import io.swagger.v3.oas.annotations.media.Schema
45
import org.springframework.web.bind.annotation.GetMapping
6+
import org.springframework.web.bind.annotation.RequestBody
57
import org.springframework.web.bind.annotation.RequestParam
68
import org.springframework.web.bind.annotation.RestController
79

@@ -20,11 +22,75 @@ class ExampleController {
2022
| 400 | STORE_NOT_FOUND |Store not found. | | |
2123
"""
2224
)
23-
@GetMapping("/foo/remove-kotlin-indent")
25+
@GetMapping("/foo/trim-kotlin-indent")
2426
fun readFoo(@RequestParam name: String?) = FooResponse("Hello ${name ?: "world"}")
2527

2628
}
2729

2830
data class FooResponse(
29-
private val name: String,
30-
)
31+
val name: String,
32+
)
33+
34+
@RestController
35+
class ExampleController2 {
36+
37+
@GetMapping("/foo/trim-kotlin-indent/schema")
38+
fun readFoo(
39+
@RequestBody request: FooRequestWithSchema,
40+
) = FooResponseWithSchema(
41+
name = "Hello ${request.age ?: "world"}",
42+
subFoo = SubFooResponseWithSchema(subName = "sub foo name"),
43+
)
44+
}
45+
46+
@Schema(
47+
name = "foo request",
48+
description = """
49+
foo request class description
50+
with kotlin indent
51+
"""
52+
)
53+
data class FooRequestWithSchema(
54+
@Schema(
55+
name = "age",
56+
description = """
57+
foo request field with kotlin indent
58+
"""
59+
)
60+
val age: Int,
61+
)
62+
63+
@Schema(
64+
name = "foo response",
65+
description = """
66+
foo response class description
67+
with kotlin indent
68+
"""
69+
)
70+
data class FooResponseWithSchema(
71+
@Schema(
72+
name = "name",
73+
description = """
74+
foo response fields with kotlin indent
75+
"""
76+
)
77+
val name: String,
78+
val subFoo: SubFooResponseWithSchema
79+
)
80+
81+
@Schema(
82+
name = "sub foo response",
83+
description = """
84+
sub foo response class description
85+
with kotlin indent
86+
"""
87+
)
88+
data class SubFooResponseWithSchema(
89+
@Schema(
90+
name = "subName",
91+
description = """
92+
sub foo response fields with kotlin indent
93+
"""
94+
)
95+
val subName: String,
96+
)

Diff for: springdoc-openapi-tests/springdoc-openapi-kotlin-webmvc-tests/src/test/resources/results/app11.json

+79-3
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
}
1212
],
1313
"paths": {
14-
"/foo/remove-kotlin-indent": {
14+
"/foo/trim-kotlin-indent": {
1515
"get": {
1616
"tags": [
1717
"example-controller"
@@ -42,18 +42,94 @@
4242
}
4343
}
4444
}
45+
},
46+
"/foo/trim-kotlin-indent/schema": {
47+
"get": {
48+
"tags": [
49+
"example-controller-2"
50+
],
51+
"operationId": "readFoo_1",
52+
"parameters": [
53+
{
54+
"name": "request",
55+
"in": "query",
56+
"required": true,
57+
"schema": {
58+
"$ref": "#/components/schemas/foo request"
59+
}
60+
}
61+
],
62+
"responses": {
63+
"200": {
64+
"description": "OK",
65+
"content": {
66+
"*/*": {
67+
"schema": {
68+
"$ref": "#/components/schemas/foo response"
69+
}
70+
}
71+
}
72+
}
73+
}
74+
}
4575
}
4676
},
4777
"components": {
4878
"schemas": {
4979
"FooResponse": {
80+
"required": [
81+
"name"
82+
],
5083
"type": "object",
5184
"properties": {
5285
"name": {
53-
"type": "string",
54-
"writeOnly": true
86+
"type": "string"
5587
}
5688
}
89+
},
90+
"foo request": {
91+
"required": [
92+
"age"
93+
],
94+
"type": "object",
95+
"properties": {
96+
"age": {
97+
"type": "integer",
98+
"description": "\nfoo request field with kotlin indent\n",
99+
"format": "int32"
100+
}
101+
},
102+
"description": "\nfoo request class description\nwith kotlin indent\n"
103+
},
104+
"foo response": {
105+
"required": [
106+
"name",
107+
"subFoo"
108+
],
109+
"type": "object",
110+
"properties": {
111+
"name": {
112+
"type": "string",
113+
"description": "\nfoo response fields with kotlin indent\n"
114+
},
115+
"subFoo": {
116+
"$ref": "#/components/schemas/sub foo response"
117+
}
118+
},
119+
"description": "\nfoo response class description\nwith kotlin indent\n"
120+
},
121+
"sub foo response": {
122+
"required": [
123+
"subName"
124+
],
125+
"type": "object",
126+
"properties": {
127+
"subName": {
128+
"type": "string",
129+
"description": "\nsub foo response fields with kotlin indent\n"
130+
}
131+
},
132+
"description": "\nsub foo response class description\nwith kotlin indent\n"
57133
}
58134
}
59135
}

0 commit comments

Comments
 (0)