Skip to content

Commit cf8776b

Browse files
committed
Expose originParents on actuator endpoints
Update `ConfigurationPropertiesReportEndpoint` and `EnvironmentEndpoint` so that they expose `originParents` when they are available. Closes gh-23018
1 parent 3c1e141 commit cf8776b

File tree

4 files changed

+129
-10
lines changed

4 files changed

+129
-10
lines changed

spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/context/properties/ConfigurationPropertiesReportEndpoint.java

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@
6060
import org.springframework.boot.context.properties.ConstructorBinding;
6161
import org.springframework.boot.context.properties.source.ConfigurationProperty;
6262
import org.springframework.boot.context.properties.source.ConfigurationPropertyName;
63+
import org.springframework.boot.origin.Origin;
6364
import org.springframework.context.ApplicationContext;
6465
import org.springframework.context.ApplicationContextAware;
6566
import org.springframework.core.KotlinDetector;
@@ -292,10 +293,14 @@ private Map<String, Object> applyInput(String qualifiedKey) {
292293

293294
private Map<String, Object> getInput(String property, ConfigurationProperty candidate) {
294295
Map<String, Object> input = new LinkedHashMap<>();
295-
String origin = (candidate.getOrigin() != null) ? candidate.getOrigin().toString() : "none";
296296
Object value = candidate.getValue();
297-
input.put("origin", origin);
297+
Origin origin = Origin.from(candidate);
298+
List<Origin> originParents = Origin.parentsFrom(candidate);
298299
input.put("value", this.sanitizer.sanitize(property, value));
300+
input.put("origin", (origin != null) ? origin.toString() : "none");
301+
if (!originParents.isEmpty()) {
302+
input.put("originParents", originParents.stream().map(Object::toString).toArray(String[]::new));
303+
}
299304
return input;
300305
}
301306

spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/env/EnvironmentEndpoint.java

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -142,15 +142,10 @@ private PropertySourceDescriptor describeSource(String sourceName, EnumerablePro
142142
private PropertyValueDescriptor describeValueOf(String name, PropertySource<?> source,
143143
PlaceholdersResolver resolver) {
144144
Object resolved = resolver.resolvePlaceholders(source.getProperty(name));
145-
String origin = ((source instanceof OriginLookup) ? getOrigin((OriginLookup<Object>) source, name) : null);
145+
Origin origin = ((source instanceof OriginLookup) ? ((OriginLookup<Object>) source).getOrigin(name) : null);
146146
return new PropertyValueDescriptor(sanitize(name, resolved), origin);
147147
}
148148

149-
private String getOrigin(OriginLookup<Object> lookup, String name) {
150-
Origin origin = lookup.getOrigin(name);
151-
return (origin != null) ? origin.toString() : null;
152-
}
153-
154149
private PlaceholdersResolver getResolver() {
155150
return new PropertySourcesPlaceholdersSanitizingResolver(getPropertySources(), this.sanitizer);
156151
}
@@ -353,9 +348,14 @@ public static final class PropertyValueDescriptor {
353348

354349
private final String origin;
355350

356-
private PropertyValueDescriptor(Object value, String origin) {
351+
private final String[] originParents;
352+
353+
private PropertyValueDescriptor(Object value, Origin origin) {
357354
this.value = value;
358-
this.origin = origin;
355+
this.origin = (origin != null) ? origin.toString() : null;
356+
List<Origin> originParents = Origin.parentsFrom(origin);
357+
this.originParents = originParents.isEmpty() ? null
358+
: originParents.stream().map(Object::toString).toArray(String[]::new);
359359
}
360360

361361
public Object getValue() {
@@ -366,6 +366,10 @@ public String getOrigin() {
366366
return this.origin;
367367
}
368368

369+
public String[] getOriginParents() {
370+
return this.originParents;
371+
}
372+
369373
}
370374

371375
}

spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/context/properties/ConfigurationPropertiesReportEndpointTests.java

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,12 +34,16 @@
3434
import org.springframework.boot.context.properties.ConstructorBinding;
3535
import org.springframework.boot.context.properties.EnableConfigurationProperties;
3636
import org.springframework.boot.context.properties.bind.DefaultValue;
37+
import org.springframework.boot.origin.Origin;
38+
import org.springframework.boot.origin.OriginLookup;
3739
import org.springframework.boot.test.context.assertj.AssertableApplicationContext;
3840
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
3941
import org.springframework.boot.test.context.runner.ContextConsumer;
42+
import org.springframework.context.ConfigurableApplicationContext;
4043
import org.springframework.context.annotation.Bean;
4144
import org.springframework.context.annotation.Configuration;
4245
import org.springframework.core.env.Environment;
46+
import org.springframework.mock.env.MockPropertySource;
4347

4448
import static org.assertj.core.api.Assertions.assertThat;
4549
import static org.assertj.core.api.Assertions.entry;
@@ -281,6 +285,23 @@ void listsOfListsAreSanitized() {
281285
}));
282286
}
283287

288+
@Test
289+
void originParents() {
290+
this.contextRunner.withUserConfiguration(SensiblePropertiesConfiguration.class)
291+
.withInitializer(this::initializeOriginParents).run(assertProperties("sensible", (properties) -> {
292+
}, (inputs) -> {
293+
Map<String, Object> stringInputs = (Map<String, Object>) inputs.get("string");
294+
String[] originParents = (String[]) stringInputs.get("originParents");
295+
assertThat(originParents).containsExactly("spring", "boot");
296+
}));
297+
}
298+
299+
private void initializeOriginParents(ConfigurableApplicationContext context) {
300+
MockPropertySource propertySource = new OriginParentMockPropertySource();
301+
propertySource.setProperty("sensible.string", "spring");
302+
context.getEnvironment().getPropertySources().addFirst(propertySource);
303+
}
304+
284305
private ContextConsumer<AssertableApplicationContext> assertProperties(String prefix,
285306
Consumer<Map<String, Object>> properties) {
286307
return assertProperties(prefix, properties, (inputs) -> {
@@ -310,6 +331,38 @@ private boolean findIdFromPrefix(String prefix, String id) {
310331
return prefix.equals(candidate);
311332
}
312333

334+
static class OriginParentMockPropertySource extends MockPropertySource implements OriginLookup<String> {
335+
336+
@Override
337+
public Origin getOrigin(String key) {
338+
return new MockOrigin(key, new MockOrigin("spring", new MockOrigin("boot", null)));
339+
}
340+
341+
}
342+
343+
static class MockOrigin implements Origin {
344+
345+
private final String value;
346+
347+
private final MockOrigin parent;
348+
349+
MockOrigin(String value, MockOrigin parent) {
350+
this.value = value;
351+
this.parent = parent;
352+
}
353+
354+
@Override
355+
public Origin getParent() {
356+
return this.parent;
357+
}
358+
359+
@Override
360+
public String toString() {
361+
return this.value;
362+
}
363+
364+
}
365+
313366
@Configuration(proxyBeanMethods = false)
314367
static class EndpointConfig {
315368

@@ -649,6 +702,8 @@ static class SensiblePropertiesConfiguration {
649702
@ConfigurationProperties("sensible")
650703
public static class SensibleProperties {
651704

705+
private String string;
706+
652707
private URI sensitiveUri = URI.create("http://user:password@localhost:8080");
653708

654709
private URI noPasswordUri = URI.create("http://user:@localhost:8080");
@@ -666,6 +721,14 @@ public static class SensibleProperties {
666721
this.listOfListItems.add(Collections.singletonList(new ListItem()));
667722
}
668723

724+
public void setString(String string) {
725+
this.string = string;
726+
}
727+
728+
public String getString() {
729+
return this.string;
730+
}
731+
669732
public void setSensitiveUri(URI sensitiveUri) {
670733
this.sensitiveUri = sensitiveUri;
671734
}

spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/env/EnvironmentEndpointTests.java

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@
2929
import org.springframework.boot.actuate.env.EnvironmentEndpoint.PropertySourceEntryDescriptor;
3030
import org.springframework.boot.actuate.env.EnvironmentEndpoint.PropertyValueDescriptor;
3131
import org.springframework.boot.context.properties.EnableConfigurationProperties;
32+
import org.springframework.boot.origin.Origin;
33+
import org.springframework.boot.origin.OriginLookup;
3234
import org.springframework.boot.test.util.TestPropertyValues;
3335
import org.springframework.context.annotation.Bean;
3436
import org.springframework.context.annotation.Configuration;
@@ -37,6 +39,7 @@
3739
import org.springframework.core.env.Environment;
3840
import org.springframework.core.env.MapPropertySource;
3941
import org.springframework.core.env.StandardEnvironment;
42+
import org.springframework.mock.env.MockPropertySource;
4043

4144
import static org.assertj.core.api.Assertions.assertThat;
4245

@@ -222,6 +225,18 @@ void propertyEntry() {
222225
});
223226
}
224227

228+
@Test
229+
void originAndOriginParents() {
230+
StandardEnvironment environment = new StandardEnvironment();
231+
OriginParentMockPropertySource propertySource = new OriginParentMockPropertySource();
232+
propertySource.setProperty("name", "test");
233+
environment.getPropertySources().addFirst(propertySource);
234+
EnvironmentEntryDescriptor descriptor = new EnvironmentEndpoint(environment).environmentEntry("name");
235+
PropertySourceEntryDescriptor entryDescriptor = propertySources(descriptor).get("mockProperties");
236+
assertThat(entryDescriptor.getProperty().getOrigin()).isEqualTo("name");
237+
assertThat(entryDescriptor.getProperty().getOriginParents()).containsExactly("spring", "boot");
238+
}
239+
225240
@Test
226241
void propertyEntryNotFound() {
227242
ConfigurableEnvironment environment = emptyEnvironment();
@@ -302,6 +317,38 @@ private void assertPropertySourceEntryDescriptor(PropertySourceEntryDescriptor a
302317

303318
}
304319

320+
static class OriginParentMockPropertySource extends MockPropertySource implements OriginLookup<String> {
321+
322+
@Override
323+
public Origin getOrigin(String key) {
324+
return new MockOrigin(key, new MockOrigin("spring", new MockOrigin("boot", null)));
325+
}
326+
327+
}
328+
329+
static class MockOrigin implements Origin {
330+
331+
private final String value;
332+
333+
private final MockOrigin parent;
334+
335+
MockOrigin(String value, MockOrigin parent) {
336+
this.value = value;
337+
this.parent = parent;
338+
}
339+
340+
@Override
341+
public Origin getParent() {
342+
return this.parent;
343+
}
344+
345+
@Override
346+
public String toString() {
347+
return this.value;
348+
}
349+
350+
}
351+
305352
@Configuration(proxyBeanMethods = false)
306353
@EnableConfigurationProperties
307354
static class Config {

0 commit comments

Comments
 (0)