Skip to content

Commit 95f1b91

Browse files
committed
Changes report: resolve-schema-properties is not replacing tokens from properties file. Fixes #1552.
1 parent 9d009c0 commit 95f1b91

File tree

6 files changed

+249
-29
lines changed

6 files changed

+249
-29
lines changed

springdoc-openapi-starter-common/src/main/java/org/springdoc/core/models/GroupedOpenApi.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -431,4 +431,14 @@ public GroupedOpenApi build() {
431431
return new GroupedOpenApi(this);
432432
}
433433
}
434+
435+
436+
/**
437+
* Add open api customizer.
438+
*
439+
* @param openApiCustomizer the open api customiser
440+
*/
441+
public void addOpenApiCustomizer(OpenApiCustomizer openApiCustomizer) {
442+
this.openApiCustomizers.add(openApiCustomizer);
443+
}
434444
}

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

Lines changed: 128 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,48 @@
11
/*
22
*
33
* *
4+
* * * Copyright 2019-2020 the original author or authors.
45
* * *
5-
* * * * Copyright 2019-2022 the original author or authors.
6-
* * * *
7-
* * * * Licensed under the Apache License, Version 2.0 (the "License");
8-
* * * * you may not use this file except in compliance with the License.
9-
* * * * You may obtain a copy of the License at
10-
* * * *
11-
* * * * https://www.apache.org/licenses/LICENSE-2.0
12-
* * * *
13-
* * * * Unless required by applicable law or agreed to in writing, software
14-
* * * * distributed under the License is distributed on an "AS IS" BASIS,
15-
* * * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16-
* * * * See the License for the specific language governing permissions and
17-
* * * * limitations under the License.
6+
* * * Licensed under the Apache License, Version 2.0 (the "License");
7+
* * * you may not use this file except in compliance with the License.
8+
* * * You may obtain a copy of the License at
189
* * *
10+
* * * https://www.apache.org/licenses/LICENSE-2.0
11+
* * *
12+
* * * Unless required by applicable law or agreed to in writing, software
13+
* * * distributed under the License is distributed on an "AS IS" BASIS,
14+
* * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* * * See the License for the specific language governing permissions and
16+
* * * limitations under the License.
1917
* *
2018
*
2119
*/
2220

2321
package org.springdoc.core.utils;
2422

23+
import java.util.LinkedHashMap;
24+
import java.util.List;
2525
import java.util.Locale;
26+
import java.util.Map;
27+
import java.util.Map.Entry;
28+
import java.util.function.Consumer;
29+
import java.util.function.Supplier;
30+
import java.util.stream.Collectors;
2631

32+
import io.swagger.v3.oas.models.info.Contact;
33+
import io.swagger.v3.oas.models.info.Info;
34+
import io.swagger.v3.oas.models.info.License;
35+
import io.swagger.v3.oas.models.media.Schema;
36+
import io.swagger.v3.oas.models.servers.Server;
37+
import org.apache.commons.lang3.StringUtils;
2738
import org.slf4j.Logger;
2839
import org.slf4j.LoggerFactory;
2940
import org.springdoc.core.properties.SpringDocConfigProperties;
3041

3142
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
3243
import org.springframework.context.MessageSource;
3344
import org.springframework.context.NoSuchMessageException;
45+
import org.springframework.util.CollectionUtils;
3446

3547
/**
3648
* The type Property resolver utils.
@@ -79,20 +91,111 @@ public PropertyResolverUtils(ConfigurableBeanFactory factory, MessageSource mess
7991
* @return the string
8092
*/
8193
public String resolve(String parameterProperty, Locale locale) {
82-
if (!springDocConfigProperties.isDisableI18n())
83-
try {
84-
return messageSource.getMessage(parameterProperty, null, locale);
85-
}
86-
catch (NoSuchMessageException ex) {
87-
LOGGER.trace(ex.getMessage());
88-
}
89-
try {
90-
return factory.resolveEmbeddedValue(parameterProperty);
94+
String result = parameterProperty;
95+
if (parameterProperty != null) {
96+
if (!springDocConfigProperties.isDisableI18n())
97+
try {
98+
result = messageSource.getMessage(parameterProperty, null, locale);
99+
}
100+
catch (NoSuchMessageException ex) {
101+
LOGGER.trace(ex.getMessage());
102+
}
103+
if (parameterProperty.equals(result))
104+
try {
105+
result = factory.resolveEmbeddedValue(parameterProperty);
106+
}
107+
catch (IllegalArgumentException ex) {
108+
LOGGER.warn(ex.getMessage());
109+
}
110+
}
111+
return result;
112+
}
113+
114+
/**
115+
* Resolve properties info.
116+
*
117+
* @param servers the servers
118+
* @param locale the locale
119+
* @return the servers
120+
*/
121+
public List<Server> resolveProperties(List<Server> servers, Locale locale) {
122+
servers.forEach(server -> {
123+
resolveProperty(server::getUrl, server::url, this, locale);
124+
resolveProperty(server::getDescription, server::description, this, locale);
125+
if (CollectionUtils.isEmpty(server.getVariables()))
126+
server.setVariables(null);
127+
});
128+
return servers;
129+
}
130+
131+
/**
132+
* Resolve properties info.
133+
*
134+
* @param info the info
135+
* @param locale the locale
136+
* @return the info
137+
*/
138+
public Info resolveProperties(Info info, Locale locale) {
139+
resolveProperty(info::getTitle, info::title, this, locale);
140+
resolveProperty(info::getDescription, info::description, this, locale);
141+
resolveProperty(info::getVersion, info::version, this, locale);
142+
resolveProperty(info::getTermsOfService, info::termsOfService, this, locale);
143+
144+
License license = info.getLicense();
145+
if (license != null) {
146+
resolveProperty(license::getName, license::name, this, locale);
147+
resolveProperty(license::getUrl, license::url, this, locale);
148+
}
149+
150+
Contact contact = info.getContact();
151+
if (contact != null) {
152+
resolveProperty(contact::getName, contact::name, this, locale);
153+
resolveProperty(contact::getEmail, contact::email, this, locale);
154+
resolveProperty(contact::getUrl, contact::url, this, locale);
155+
}
156+
return info;
157+
}
158+
159+
/**
160+
* Resolve properties schema.
161+
*
162+
* @param schema the schema
163+
* @param locale the locale
164+
* @return the schema
165+
*/
166+
@SuppressWarnings("unchecked")
167+
public Schema resolveProperties(Schema schema, Locale locale) {
168+
resolveProperty(schema::getName, schema::name, this, locale);
169+
resolveProperty(schema::getTitle, schema::title, this, locale);
170+
resolveProperty(schema::getDescription, schema::description, this, locale);
171+
172+
Map<String, Schema> properties = schema.getProperties();
173+
if (!CollectionUtils.isEmpty(properties)) {
174+
LinkedHashMap<String, Schema> resolvedSchemas = properties.entrySet().stream().map(es -> {
175+
es.setValue(resolveProperties(es.getValue(), locale));
176+
return es;
177+
}).collect(Collectors.toMap(Entry::getKey, Entry::getValue, (e1, e2) -> e2,
178+
LinkedHashMap::new));
179+
schema.setProperties(resolvedSchemas);
91180
}
92-
catch (IllegalArgumentException ex) {
93-
LOGGER.warn(ex.getMessage());
181+
182+
return schema;
183+
}
184+
185+
/**
186+
* Resolve property.
187+
*
188+
* @param getProperty the get property
189+
* @param setProperty the set property
190+
* @param propertyResolverUtils the property resolver utils
191+
* @param locale the locale
192+
*/
193+
private void resolveProperty(Supplier<String> getProperty, Consumer<String> setProperty,
194+
PropertyResolverUtils propertyResolverUtils, Locale locale) {
195+
String value = getProperty.get();
196+
if (StringUtils.isNotBlank(value)) {
197+
setProperty.accept(propertyResolverUtils.resolve(value, locale));
94198
}
95-
return parameterProperty;
96199
}
97200

98201
/**

springdoc-openapi-starter-webflux-api/src/main/java/org/springdoc/webflux/api/MultipleOpenApiResource.java

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
import java.util.stream.Collectors;
2929

3030
import org.springdoc.api.OpenApiResourceNotFoundException;
31+
import org.springdoc.core.customizers.OpenApiCustomizer;
3132
import org.springdoc.core.models.GroupedOpenApi;
3233
import org.springdoc.core.properties.SpringDocConfigProperties;
3334
import org.springdoc.core.properties.SpringDocConfigProperties.GroupConfig;
@@ -37,16 +38,19 @@
3738
import org.springdoc.core.service.OpenAPIService;
3839
import org.springdoc.core.service.OperationService;
3940

41+
import org.springframework.beans.BeansException;
4042
import org.springframework.beans.factory.InitializingBean;
4143
import org.springframework.beans.factory.ObjectFactory;
44+
import org.springframework.context.ApplicationContext;
45+
import org.springframework.context.ApplicationContextAware;
4246

4347
import static org.springdoc.core.utils.Constants.ACTUATOR_DEFAULT_GROUP;
4448

4549
/**
4650
* The type Multiple open api resource.
4751
* @author bnasslahsen
4852
*/
49-
public abstract class MultipleOpenApiResource implements InitializingBean {
53+
public abstract class MultipleOpenApiResource implements InitializingBean, ApplicationContextAware {
5054

5155
/**
5256
* The Grouped open apis.
@@ -88,7 +92,10 @@ public abstract class MultipleOpenApiResource implements InitializingBean {
8892
*/
8993
private final SpringDocProviders springDocProviders;
9094

91-
95+
/**
96+
* The Application context.
97+
*/
98+
protected ApplicationContext applicationContext;
9299
/**
93100
* Instantiates a new Multiple open api resource.
94101
*
@@ -115,6 +122,10 @@ protected MultipleOpenApiResource(List<GroupedOpenApi> groupedOpenApis,
115122

116123
@Override
117124
public void afterPropertiesSet() {
125+
if (springDocConfigProperties.getApiDocs().isResolveSchemaProperties()) {
126+
OpenApiCustomizer propertiesResolverForSchemaCustomizer = (OpenApiCustomizer) applicationContext.getBean("propertiesResolverForSchema");
127+
this.groupedOpenApis.forEach(groupedOpenApi -> groupedOpenApi.addOpenApiCustomizer(propertiesResolverForSchemaCustomizer));
128+
}
118129
this.groupedOpenApiResources = groupedOpenApis.stream()
119130
.collect(Collectors.toMap(GroupedOpenApi::getGroup, item ->
120131
{
@@ -170,4 +181,9 @@ protected OpenApiResource getOpenApiResourceOrThrow(String group) {
170181
}
171182
return openApiResource;
172183
}
184+
185+
@Override
186+
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
187+
this.applicationContext = applicationContext;
188+
}
173189
}

springdoc-openapi-starter-webmvc-api/src/main/java/org/springdoc/webmvc/api/MultipleOpenApiResource.java

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
import java.util.stream.Collectors;
2929

3030
import org.springdoc.api.OpenApiResourceNotFoundException;
31+
import org.springdoc.core.customizers.OpenApiCustomizer;
3132
import org.springdoc.core.models.GroupedOpenApi;
3233
import org.springdoc.core.properties.SpringDocConfigProperties;
3334
import org.springdoc.core.properties.SpringDocConfigProperties.GroupConfig;
@@ -37,16 +38,19 @@
3738
import org.springdoc.core.service.OpenAPIService;
3839
import org.springdoc.core.service.OperationService;
3940

41+
import org.springframework.beans.BeansException;
4042
import org.springframework.beans.factory.InitializingBean;
4143
import org.springframework.beans.factory.ObjectFactory;
44+
import org.springframework.context.ApplicationContext;
45+
import org.springframework.context.ApplicationContextAware;
4246

4347
import static org.springdoc.core.utils.Constants.ACTUATOR_DEFAULT_GROUP;
4448

4549
/**
4650
* The type Web mvc multiple open api resource.
4751
* @author bnasslahsen
4852
*/
49-
public abstract class MultipleOpenApiResource implements InitializingBean {
53+
public abstract class MultipleOpenApiResource implements InitializingBean, ApplicationContextAware {
5054

5155
/**
5256
* The Grouped open apis.
@@ -88,6 +92,10 @@ public abstract class MultipleOpenApiResource implements InitializingBean {
8892
*/
8993
private Map<String, OpenApiResource> groupedOpenApiResources;
9094

95+
/**
96+
* The Application context.
97+
*/
98+
protected ApplicationContext applicationContext;
9199
/**
92100
* Instantiates a new Multiple open api resource.
93101
*
@@ -114,6 +122,10 @@ public MultipleOpenApiResource(List<GroupedOpenApi> groupedOpenApis,
114122

115123
@Override
116124
public void afterPropertiesSet() {
125+
if (springDocConfigProperties.getApiDocs().isResolveSchemaProperties()) {
126+
OpenApiCustomizer propertiesResolverForSchemaCustomizer = (OpenApiCustomizer) applicationContext.getBean("propertiesResolverForSchema");
127+
this.groupedOpenApis.forEach(groupedOpenApi -> groupedOpenApi.addOpenApiCustomizer(propertiesResolverForSchemaCustomizer));
128+
}
117129
this.groupedOpenApiResources = groupedOpenApis.stream()
118130
.collect(Collectors.toMap(GroupedOpenApi::getGroup, item ->
119131
{
@@ -170,4 +182,9 @@ protected OpenApiResource getOpenApiResourceOrThrow(String group) {
170182
}
171183
return openApiResource;
172184
}
185+
186+
@Override
187+
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
188+
this.applicationContext = applicationContext;
189+
}
173190
}
Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,32 @@
11
package test.org.springdoc.api.app160;
22

3+
import org.junit.jupiter.api.Test;
4+
import org.springdoc.core.utils.Constants;
35
import test.org.springdoc.api.AbstractSpringDocTest;
46

57
import org.springframework.boot.autoconfigure.SpringBootApplication;
68
import org.springframework.test.context.TestPropertySource;
79

10+
import static org.hamcrest.Matchers.is;
11+
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
12+
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
13+
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
14+
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
15+
816
@TestPropertySource(properties = "springdoc.api-docs.resolve-schema-properties=true")
917
public class SpringDocApp160Test extends AbstractSpringDocTest {
1018

1119
@SpringBootApplication
12-
static class SpringDocTestApp {}
20+
static class SpringDocTestApp {
21+
}
22+
1323

24+
@Test
25+
public void testApp2() throws Exception {
26+
mockMvc.perform(get(Constants.DEFAULT_API_DOCS_URL + "/test"))
27+
.andExpect(status().isOk())
28+
.andExpect(jsonPath("$.openapi", is("3.0.1")))
29+
.andExpect(content().json(getContent("results/app160-1.json"), true));
30+
}
1431

1532
}

0 commit comments

Comments
 (0)