Skip to content

Commit 4618041

Browse files
author
Anton Tkachenko
committed
Add support for externalizing strings in openapi groups
1 parent 2601b3c commit 4618041

File tree

5 files changed

+152
-1
lines changed

5 files changed

+152
-1
lines changed

springdoc-openapi-starter-common/src/main/java/org/springdoc/core/configuration/SpringDocSpecificationStringPropertiesConfiguration.java

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@
2525
package org.springdoc.core.configuration;
2626

2727
import org.springdoc.core.customizers.SpecificationStringPropertiesCustomizer;
28+
import org.springdoc.core.models.GroupedOpenApi;
29+
import org.springframework.beans.factory.config.BeanPostProcessor;
2830
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
2931
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
3032
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
@@ -33,6 +35,8 @@
3335
import org.springframework.context.annotation.Lazy;
3436
import org.springframework.core.env.PropertyResolver;
3537

38+
import java.util.List;
39+
3640
/**
3741
* The type Spring doc specification string properties configuration.
3842
*
@@ -46,6 +50,7 @@ public class SpringDocSpecificationStringPropertiesConfiguration {
4650

4751
/**
4852
* Springdoc customizer that takes care of the specification string properties customization.
53+
* Will be applied to general openapi schema.
4954
*
5055
* @return the springdoc customizer
5156
*/
@@ -58,4 +63,41 @@ SpecificationStringPropertiesCustomizer specificationStringPropertiesCustomizer(
5863
return new SpecificationStringPropertiesCustomizer(propertyResolverUtils);
5964
}
6065

66+
/**
67+
* Bean post processor that applies the specification string properties customization to
68+
* grouped openapi schemas by using group name as a prefix for properties.
69+
*
70+
* @return the bean post processor
71+
*/
72+
@Bean
73+
@ConditionalOnMissingBean
74+
@Lazy(false)
75+
SpecificationStringPropertiesCustomizerBeanPostProcessor specificationStringPropertiesCustomizerBeanPostProcessor(
76+
PropertyResolver propertyResolverUtils
77+
) {
78+
return new SpecificationStringPropertiesCustomizerBeanPostProcessor(propertyResolverUtils);
79+
}
80+
81+
82+
private static class SpecificationStringPropertiesCustomizerBeanPostProcessor implements BeanPostProcessor {
83+
84+
private final PropertyResolver propertyResolverUtils;
85+
86+
public SpecificationStringPropertiesCustomizerBeanPostProcessor(
87+
PropertyResolver propertyResolverUtils
88+
) {
89+
this.propertyResolverUtils = propertyResolverUtils;
90+
}
91+
92+
@Override
93+
public Object postProcessAfterInitialization(Object bean, String beanName) {
94+
if (bean instanceof GroupedOpenApi groupedOpenApi) {
95+
groupedOpenApi.addAllOpenApiCustomizer(List.of(new SpecificationStringPropertiesCustomizer(
96+
propertyResolverUtils, groupedOpenApi.getGroup()
97+
)));
98+
}
99+
return bean;
100+
}
101+
}
102+
61103
}

springdoc-openapi-starter-common/src/main/java/org/springdoc/core/customizers/SpecificationStringPropertiesCustomizer.java

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,14 @@
6363
* <li>springdoc.specification-strings.paths.{operationId}.description - to set description of {operationId}</li>
6464
* <li>springdoc.specification-strings.paths.{operationId}.summary - to set summary of {operationId}</li>
6565
* </ul>
66+
* <p>
67+
* Support for groped openapi customization is similar to the above, but with a group name prefix.
68+
* E.g.
69+
* <ul>
70+
* <li>springdoc.specification-strings.{group-name}.info.title - to set title of api-info</li>
71+
* <li>springdoc.specification-strings.{group-name}.components.User.description - to set description of User model</li>
72+
* <li>springdoc.specification-strings.{group-name}.paths.{operationId}.description - to set description of {operationId}</li>
73+
* </ul>
6674
*
6775
* @author Anton Tkachenko [email protected]
6876
*/
@@ -71,9 +79,16 @@ public class SpecificationStringPropertiesCustomizer implements GlobalOpenApiCus
7179
private static final String SPECIFICATION_STRINGS_PREFIX = "springdoc.specification-strings.";
7280

7381
private final PropertyResolver propertyResolver;
82+
private final String propertyPrefix;
7483

7584
public SpecificationStringPropertiesCustomizer(PropertyResolver resolverUtils) {
7685
this.propertyResolver = resolverUtils;
86+
this.propertyPrefix = SPECIFICATION_STRINGS_PREFIX;
87+
}
88+
89+
public SpecificationStringPropertiesCustomizer(PropertyResolver propertyResolver, String groupName) {
90+
this.propertyResolver = propertyResolver;
91+
this.propertyPrefix = SPECIFICATION_STRINGS_PREFIX + groupName + ".";
7792
}
7893

7994
@Override
@@ -140,7 +155,7 @@ private void setComponentsProperties(OpenAPI openApi) {
140155
private void resolveString(
141156
Consumer<String> setter, String node
142157
) {
143-
String nodeWithPrefix = SPECIFICATION_STRINGS_PREFIX + node;
158+
String nodeWithPrefix = propertyPrefix + node;
144159
String value = propertyResolver.getProperty(nodeWithPrefix);
145160
if (StringUtils.isNotBlank(value)) {
146161
setter.accept(value);

springdoc-openapi-tests/springdoc-openapi-groovy-tests/src/test/groovy/test/org/springdoc/api/app212/SpringDocApp212Test.java

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,17 @@
1818

1919
package test.org.springdoc.api.app212;
2020

21+
import org.junit.jupiter.api.Test;
22+
import org.springdoc.core.models.GroupedOpenApi;
2123
import org.springframework.boot.autoconfigure.SpringBootApplication;
24+
import org.springframework.context.annotation.Bean;
2225
import org.springframework.test.context.ActiveProfiles;
2326
import test.org.springdoc.api.AbstractSpringDocTest;
2427

28+
import static org.skyscreamer.jsonassert.JSONAssert.assertEquals;
29+
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
30+
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
31+
2532
/**
2633
* The type Spring doc app 192 test.
2734
* <p>
@@ -35,6 +42,23 @@ public class SpringDocApp212Test extends AbstractSpringDocTest {
3542
*/
3643
@SpringBootApplication
3744
static class SpringDocTestApp {
45+
46+
@Bean
47+
GroupedOpenApi apiGroupBeanName() {
48+
return GroupedOpenApi.builder()
49+
.group("apiGroupName")
50+
.packagesToScan("test.org.springdoc.api.app212")
51+
.build();
52+
}
53+
}
54+
55+
@Test
56+
void getGroupedOpenapi_shouldCustomizeFromPropertiesWithGroupNamePrefix() throws Exception {
57+
String result = mockMvc.perform(get("/v3/api-docs/apiGroupName"))
58+
.andExpect(status().isOk())
59+
.andReturn().getResponse().getContentAsString();
60+
String expected = getContent("results/app212-grouped.json");
61+
assertEquals(expected, result, true);
3862
}
3963

4064
}

springdoc-openapi-tests/springdoc-openapi-groovy-tests/src/test/resources/application-212.yml

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,3 +18,20 @@ springdoc:
1818
persons:
1919
description: Description of operationId 'persons'
2020
summary: Summary of operationId 'persons'
21+
apiGroupName:
22+
info:
23+
title: ApiGroupName info title
24+
description: ApiGroupName info description
25+
version: ApiGroupName info version
26+
components:
27+
schemas:
28+
PersonDTO:
29+
description: Description for PersonDTO component in ApiGroupName
30+
properties:
31+
name:
32+
description: Description for 'name' property in ApiGroupName
33+
example: Example value for 'name' property in ApiGroupName
34+
paths:
35+
persons:
36+
description: Description of operationId 'persons' in ApiGroupName
37+
summary: Summary of operationId 'persons' in ApiGroupName
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
{
2+
"openapi": "3.0.1",
3+
"info": {
4+
"title": "ApiGroupName info title",
5+
"description": "ApiGroupName info description",
6+
"version": "ApiGroupName info version"
7+
},
8+
"servers": [
9+
{
10+
"url": "http://localhost",
11+
"description": "Generated server url"
12+
}
13+
],
14+
"paths": {
15+
"/persons": {
16+
"get": {
17+
"tags": [
18+
"hello-controller"
19+
],
20+
"summary": "Summary of operationId 'persons' in ApiGroupName",
21+
"description": "Description of operationId 'persons' in ApiGroupName",
22+
"operationId": "persons",
23+
"responses": {
24+
"200": {
25+
"description": "OK",
26+
"content": {
27+
"*/*": {
28+
"schema": {
29+
"$ref": "#/components/schemas/PersonDTO"
30+
}
31+
}
32+
}
33+
}
34+
}
35+
}
36+
}
37+
},
38+
"components": {
39+
"schemas": {
40+
"PersonDTO": {
41+
"type": "object",
42+
"properties": {
43+
"name": {
44+
"type": "string",
45+
"description": "Description for 'name' property in ApiGroupName",
46+
"example": "Example value for 'name' property in ApiGroupName"
47+
}
48+
},
49+
"description": "Description for PersonDTO component in ApiGroupName"
50+
}
51+
}
52+
}
53+
}

0 commit comments

Comments
 (0)