Add HealthEndpointGroupsRegistry and its Customizer

Prior to this commit, `HealthContributor` would be exposed under the
main `HealthEndpoint` and subgroups, `HealthEndpointGroups`. Groups are
driven by configuration properties and there was no way to contribute
programmatically new groups.

This commit introduces the `HealthEndpointGroupsRegistry` (a mutable
version of `HealthEndpointGroups`) and a
`HealthEndpointGroupsRegistryCustomizer`. This allows configurations to
add/remove groups during Actuator auto-configuration.

Closes gh-20554
pull/20577/head
Brian Clozel 5 years ago
parent 4b7ed5efef
commit b680db6cd8

@ -22,6 +22,7 @@ import java.util.LinkedHashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.function.Consumer;
import java.util.function.Predicate; import java.util.function.Predicate;
import java.util.function.Supplier; import java.util.function.Supplier;
@ -31,10 +32,12 @@ import org.springframework.beans.factory.ListableBeanFactory;
import org.springframework.beans.factory.NoSuchBeanDefinitionException; import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.annotation.BeanFactoryAnnotationUtils; import org.springframework.beans.factory.annotation.BeanFactoryAnnotationUtils;
import org.springframework.boot.actuate.autoconfigure.health.HealthEndpointProperties.Group; import org.springframework.boot.actuate.autoconfigure.health.HealthEndpointProperties.Group;
import org.springframework.boot.actuate.autoconfigure.health.HealthProperties.Show;
import org.springframework.boot.actuate.autoconfigure.health.HealthProperties.Status; import org.springframework.boot.actuate.autoconfigure.health.HealthProperties.Status;
import org.springframework.boot.actuate.health.HealthEndpointGroup; import org.springframework.boot.actuate.health.HealthEndpointGroup;
import org.springframework.boot.actuate.health.HealthEndpointGroup.Show;
import org.springframework.boot.actuate.health.HealthEndpointGroupConfigurer;
import org.springframework.boot.actuate.health.HealthEndpointGroups; import org.springframework.boot.actuate.health.HealthEndpointGroups;
import org.springframework.boot.actuate.health.HealthEndpointGroupsRegistry;
import org.springframework.boot.actuate.health.HttpCodeStatusMapper; import org.springframework.boot.actuate.health.HttpCodeStatusMapper;
import org.springframework.boot.actuate.health.SimpleHttpCodeStatusMapper; import org.springframework.boot.actuate.health.SimpleHttpCodeStatusMapper;
import org.springframework.boot.actuate.health.SimpleStatusAggregator; import org.springframework.boot.actuate.health.SimpleStatusAggregator;
@ -45,71 +48,113 @@ import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils; import org.springframework.util.ObjectUtils;
/** /**
* Auto-configured {@link HealthEndpointGroups}. * Auto-configured {@link HealthEndpointGroupsRegistry}.
* *
* @author Phillip Webb * @author Phillip Webb
* @author Brian Clozel
*/ */
class AutoConfiguredHealthEndpointGroups implements HealthEndpointGroups { class AutoConfiguredHealthEndpointGroupsRegistry implements HealthEndpointGroupsRegistry {
private static Predicate<String> ALL = (name) -> true; private static Predicate<String> ALL = (name) -> true;
private final HealthEndpointGroup primaryGroup; private final StatusAggregator defaultStatusAggregator;
private final HttpCodeStatusMapper defaultHttpCodeStatusMapper;
private final Show defaultShowComponents;
private final Show defaultShowDetails;
private final Set<String> defaultRoles;
private HealthEndpointGroup primaryGroup;
private final Map<String, HealthEndpointGroup> groups; private final Map<String, HealthEndpointGroup> groups;
/** /**
* Create a new {@link AutoConfiguredHealthEndpointGroups} instance. * Create a new {@link AutoConfiguredHealthEndpointGroupsRegistry} instance.
* @param applicationContext the application context used to check for override beans * @param applicationContext the application context used to check for override beans
* @param properties the health endpoint properties * @param properties the health endpoint properties
*/ */
AutoConfiguredHealthEndpointGroups(ApplicationContext applicationContext, HealthEndpointProperties properties) { AutoConfiguredHealthEndpointGroupsRegistry(ApplicationContext applicationContext,
HealthEndpointProperties properties) {
ListableBeanFactory beanFactory = (applicationContext instanceof ConfigurableApplicationContext) ListableBeanFactory beanFactory = (applicationContext instanceof ConfigurableApplicationContext)
? ((ConfigurableApplicationContext) applicationContext).getBeanFactory() : applicationContext; ? ((ConfigurableApplicationContext) applicationContext).getBeanFactory() : applicationContext;
Show showComponents = properties.getShowComponents(); this.defaultShowComponents = convertVisibility(properties.getShowComponents());
Show showDetails = properties.getShowDetails(); this.defaultShowDetails = convertVisibility(properties.getShowDetails());
Set<String> roles = properties.getRoles(); this.defaultRoles = properties.getRoles();
StatusAggregator statusAggregator = getNonQualifiedBean(beanFactory, StatusAggregator.class); StatusAggregator statusAggregator = getNonQualifiedBean(beanFactory, StatusAggregator.class);
if (statusAggregator == null) { if (statusAggregator == null) {
statusAggregator = new SimpleStatusAggregator(properties.getStatus().getOrder()); statusAggregator = new SimpleStatusAggregator(properties.getStatus().getOrder());
} }
this.defaultStatusAggregator = statusAggregator;
HttpCodeStatusMapper httpCodeStatusMapper = getNonQualifiedBean(beanFactory, HttpCodeStatusMapper.class); HttpCodeStatusMapper httpCodeStatusMapper = getNonQualifiedBean(beanFactory, HttpCodeStatusMapper.class);
if (httpCodeStatusMapper == null) { if (httpCodeStatusMapper == null) {
httpCodeStatusMapper = new SimpleHttpCodeStatusMapper(properties.getStatus().getHttpMapping()); httpCodeStatusMapper = new SimpleHttpCodeStatusMapper(properties.getStatus().getHttpMapping());
} }
this.primaryGroup = new AutoConfiguredHealthEndpointGroup(ALL, statusAggregator, httpCodeStatusMapper, this.defaultHttpCodeStatusMapper = httpCodeStatusMapper;
showComponents, showDetails, roles);
this.groups = createGroups(properties.getGroup(), beanFactory, statusAggregator, httpCodeStatusMapper, this.primaryGroup = new DefaultHealthEndpointGroup(ALL, statusAggregator, httpCodeStatusMapper,
showComponents, showDetails, roles); this.defaultShowComponents, this.defaultShowDetails, this.defaultRoles);
this.groups = createGroups(properties.getGroup(), beanFactory);
}
@Override
public HealthEndpointGroupsRegistry add(String groupName, Consumer<HealthEndpointGroupConfigurer> consumer) {
DefaultHealthEndpointGroupConfigurer groupConfigurer = new DefaultHealthEndpointGroupConfigurer(
this.defaultStatusAggregator, this.defaultHttpCodeStatusMapper, this.defaultShowComponents,
this.defaultShowDetails, this.defaultRoles);
consumer.accept(groupConfigurer);
this.groups.put(groupName, groupConfigurer.toHealthEndpointGroup());
return this;
}
@Override
public HealthEndpointGroupsRegistry remove(String groupName) {
this.groups.remove(groupName);
return this;
} }
private Map<String, HealthEndpointGroup> createGroups(Map<String, Group> groupProperties, BeanFactory beanFactory, @Override
StatusAggregator defaultStatusAggregator, HttpCodeStatusMapper defaultHttpCodeStatusMapper, public HealthEndpointGroups toGroups() {
Show defaultShowComponents, Show defaultShowDetails, Set<String> defaultRoles) { return HealthEndpointGroups.of(this.primaryGroup, Collections.unmodifiableMap(this.groups));
}
private Map<String, HealthEndpointGroup> createGroups(Map<String, Group> groupProperties, BeanFactory beanFactory) {
Map<String, HealthEndpointGroup> groups = new LinkedHashMap<>(); Map<String, HealthEndpointGroup> groups = new LinkedHashMap<>();
groupProperties.forEach((groupName, group) -> { groupProperties
Status status = group.getStatus(); .forEach((groupName, group) -> groups.put(groupName, createGroup(groupName, group, beanFactory)));
Show showComponents = (group.getShowComponents() != null) ? group.getShowComponents() return groups;
: defaultShowComponents; }
Show showDetails = (group.getShowDetails() != null) ? group.getShowDetails() : defaultShowDetails;
Set<String> roles = !CollectionUtils.isEmpty(group.getRoles()) ? group.getRoles() : defaultRoles; private HealthEndpointGroup createGroup(String groupName, Group groupProperties, BeanFactory beanFactory) {
StatusAggregator statusAggregator = getQualifiedBean(beanFactory, StatusAggregator.class, groupName, () -> { Status status = groupProperties.getStatus();
if (!CollectionUtils.isEmpty(status.getOrder())) { Show showComponents = (groupProperties.getShowComponents() != null)
return new SimpleStatusAggregator(status.getOrder()); ? convertVisibility(groupProperties.getShowComponents()) : this.defaultShowComponents;
} Show showDetails = (groupProperties.getShowDetails() != null)
return defaultStatusAggregator; ? convertVisibility(groupProperties.getShowDetails()) : this.defaultShowDetails;
}); Set<String> roles = !CollectionUtils.isEmpty(groupProperties.getRoles()) ? groupProperties.getRoles()
HttpCodeStatusMapper httpCodeStatusMapper = getQualifiedBean(beanFactory, HttpCodeStatusMapper.class, : this.defaultRoles;
groupName, () -> { StatusAggregator statusAggregator = getQualifiedBean(beanFactory, StatusAggregator.class, groupName, () -> {
if (!CollectionUtils.isEmpty(status.getHttpMapping())) { if (!CollectionUtils.isEmpty(status.getOrder())) {
return new SimpleHttpCodeStatusMapper(status.getHttpMapping()); return new SimpleStatusAggregator(status.getOrder());
} }
return defaultHttpCodeStatusMapper; return this.defaultStatusAggregator;
});
Predicate<String> members = new IncludeExcludeGroupMemberPredicate(group.getInclude(), group.getExclude());
groups.put(groupName, new AutoConfiguredHealthEndpointGroup(members, statusAggregator, httpCodeStatusMapper,
showComponents, showDetails, roles));
}); });
return Collections.unmodifiableMap(groups); HttpCodeStatusMapper httpCodeStatusMapper = getQualifiedBean(beanFactory, HttpCodeStatusMapper.class, groupName,
() -> {
if (!CollectionUtils.isEmpty(status.getHttpMapping())) {
return new SimpleHttpCodeStatusMapper(status.getHttpMapping());
}
return this.defaultHttpCodeStatusMapper;
});
Predicate<String> members = new IncludeExcludeGroupMemberPredicate(groupProperties.getInclude(),
groupProperties.getExclude());
return new DefaultHealthEndpointGroup(members, statusAggregator, httpCodeStatusMapper, showComponents,
showDetails, roles);
} }
private <T> T getNonQualifiedBean(ListableBeanFactory beanFactory, Class<T> type) { private <T> T getNonQualifiedBean(ListableBeanFactory beanFactory, Class<T> type) {
@ -140,6 +185,21 @@ class AutoConfiguredHealthEndpointGroups implements HealthEndpointGroups {
} }
} }
private Show convertVisibility(HealthProperties.Show show) {
if (show == null) {
return null;
}
switch (show) {
case ALWAYS:
return Show.ALWAYS;
case NEVER:
return Show.NEVER;
case WHEN_AUTHORIZED:
return Show.WHEN_AUTHORIZED;
}
throw new IllegalStateException("Unsupported 'show' value " + show);
}
@Override @Override
public HealthEndpointGroup getPrimary() { public HealthEndpointGroup getPrimary() {
return this.primaryGroup; return this.primaryGroup;

@ -20,7 +20,6 @@ import java.security.Principal;
import java.util.Collection; import java.util.Collection;
import java.util.function.Predicate; import java.util.function.Predicate;
import org.springframework.boot.actuate.autoconfigure.health.HealthProperties.Show;
import org.springframework.boot.actuate.endpoint.SecurityContext; import org.springframework.boot.actuate.endpoint.SecurityContext;
import org.springframework.boot.actuate.health.HealthEndpointGroup; import org.springframework.boot.actuate.health.HealthEndpointGroup;
import org.springframework.boot.actuate.health.HttpCodeStatusMapper; import org.springframework.boot.actuate.health.HttpCodeStatusMapper;
@ -31,12 +30,12 @@ import org.springframework.util.ClassUtils;
import org.springframework.util.CollectionUtils; import org.springframework.util.CollectionUtils;
/** /**
* Auto-configured {@link HealthEndpointGroup} backed by {@link HealthProperties}. * Auto-configured {@link HealthEndpointGroup}.
* *
* @author Phillip Webb * @author Phillip Webb
* @author Andy Wilkinson * @author Andy Wilkinson
*/ */
class AutoConfiguredHealthEndpointGroup implements HealthEndpointGroup { class DefaultHealthEndpointGroup implements HealthEndpointGroup {
private final Predicate<String> members; private final Predicate<String> members;
@ -51,7 +50,7 @@ class AutoConfiguredHealthEndpointGroup implements HealthEndpointGroup {
private final Collection<String> roles; private final Collection<String> roles;
/** /**
* Create a new {@link AutoConfiguredHealthEndpointGroup} instance. * Create a new {@link DefaultHealthEndpointGroup} instance.
* @param members a predicate used to test for group membership * @param members a predicate used to test for group membership
* @param statusAggregator the status aggregator to use * @param statusAggregator the status aggregator to use
* @param httpCodeStatusMapper the HTTP code status mapper to use * @param httpCodeStatusMapper the HTTP code status mapper to use
@ -59,7 +58,7 @@ class AutoConfiguredHealthEndpointGroup implements HealthEndpointGroup {
* @param showDetails the show details setting * @param showDetails the show details setting
* @param roles the roles to match * @param roles the roles to match
*/ */
AutoConfiguredHealthEndpointGroup(Predicate<String> members, StatusAggregator statusAggregator, DefaultHealthEndpointGroup(Predicate<String> members, StatusAggregator statusAggregator,
HttpCodeStatusMapper httpCodeStatusMapper, Show showComponents, Show showDetails, HttpCodeStatusMapper httpCodeStatusMapper, Show showComponents, Show showDetails,
Collection<String> roles) { Collection<String> roles) {
this.members = members; this.members = members;

@ -0,0 +1,110 @@
/*
* Copyright 2012-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.actuate.autoconfigure.health;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import org.springframework.boot.actuate.health.HealthEndpointGroup;
import org.springframework.boot.actuate.health.HealthEndpointGroup.Show;
import org.springframework.boot.actuate.health.HealthEndpointGroupConfigurer;
import org.springframework.boot.actuate.health.HttpCodeStatusMapper;
import org.springframework.boot.actuate.health.StatusAggregator;
/**
* Mutable {@link HealthEndpointGroupConfigurer configurer} for
* {@link HealthEndpointGroup}.
*
* @author Brian Clozel
*/
class DefaultHealthEndpointGroupConfigurer implements HealthEndpointGroupConfigurer {
Set<String> includedIndicators;
Set<String> excludedIndicators;
private StatusAggregator statusAggregator;
private HttpCodeStatusMapper httpCodeStatusMapper;
private Show showComponents;
private Show showDetails;
private Set<String> roles;
DefaultHealthEndpointGroupConfigurer(StatusAggregator defaultStatusAggregator,
HttpCodeStatusMapper defaultHttpCodeStatusMapper, Show defaultShowComponents, Show defaultShowDetails,
Set<String> defaultRoles) {
this.statusAggregator = defaultStatusAggregator;
this.httpCodeStatusMapper = defaultHttpCodeStatusMapper;
this.showComponents = defaultShowComponents;
this.showDetails = defaultShowDetails;
this.roles = new HashSet<>(defaultRoles);
}
@Override
public HealthEndpointGroupConfigurer include(String... indicators) {
this.includedIndicators = new HashSet<>(Arrays.asList(indicators));
return this;
}
@Override
public HealthEndpointGroupConfigurer exclude(String... exclude) {
this.excludedIndicators = new HashSet<>(Arrays.asList(exclude));
return this;
}
@Override
public HealthEndpointGroupConfigurer statusAggregator(StatusAggregator statusAggregator) {
this.statusAggregator = statusAggregator;
return this;
}
@Override
public HealthEndpointGroupConfigurer httpCodeStatusMapper(HttpCodeStatusMapper httpCodeStatusMapper) {
this.httpCodeStatusMapper = httpCodeStatusMapper;
return this;
}
@Override
public HealthEndpointGroupConfigurer showComponents(Show showComponents) {
this.showComponents = showComponents;
return this;
}
@Override
public HealthEndpointGroupConfigurer showDetails(Show showDetails) {
this.showDetails = showDetails;
return this;
}
@Override
public HealthEndpointGroupConfigurer roles(String... roles) {
this.roles = new HashSet<>(Arrays.asList(roles));
return this;
}
HealthEndpointGroup toHealthEndpointGroup() {
IncludeExcludeGroupMemberPredicate predicate = new IncludeExcludeGroupMemberPredicate(this.includedIndicators,
this.excludedIndicators);
return new DefaultHealthEndpointGroup(predicate, this.statusAggregator, this.httpCodeStatusMapper,
this.showComponents, this.showDetails, this.roles);
}
}

@ -1,5 +1,5 @@
/* /*
* Copyright 2012-2019 the original author or authors. * Copyright 2012-2020 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -21,6 +21,7 @@ import java.util.Iterator;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.Map; import java.util.Map;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.actuate.health.CompositeHealthContributor; import org.springframework.boot.actuate.health.CompositeHealthContributor;
import org.springframework.boot.actuate.health.CompositeReactiveHealthContributor; import org.springframework.boot.actuate.health.CompositeReactiveHealthContributor;
import org.springframework.boot.actuate.health.Health; import org.springframework.boot.actuate.health.Health;
@ -28,6 +29,7 @@ import org.springframework.boot.actuate.health.HealthContributor;
import org.springframework.boot.actuate.health.HealthContributorRegistry; import org.springframework.boot.actuate.health.HealthContributorRegistry;
import org.springframework.boot.actuate.health.HealthEndpoint; import org.springframework.boot.actuate.health.HealthEndpoint;
import org.springframework.boot.actuate.health.HealthEndpointGroups; import org.springframework.boot.actuate.health.HealthEndpointGroups;
import org.springframework.boot.actuate.health.HealthEndpointGroupsRegistryCustomizer;
import org.springframework.boot.actuate.health.HealthIndicator; import org.springframework.boot.actuate.health.HealthIndicator;
import org.springframework.boot.actuate.health.HttpCodeStatusMapper; import org.springframework.boot.actuate.health.HttpCodeStatusMapper;
import org.springframework.boot.actuate.health.NamedContributor; import org.springframework.boot.actuate.health.NamedContributor;
@ -66,8 +68,11 @@ class HealthEndpointConfiguration {
@Bean @Bean
@ConditionalOnMissingBean @ConditionalOnMissingBean
HealthEndpointGroups healthEndpointGroups(ApplicationContext applicationContext, HealthEndpointGroups healthEndpointGroups(ApplicationContext applicationContext,
HealthEndpointProperties properties) { HealthEndpointProperties properties, ObjectProvider<HealthEndpointGroupsRegistryCustomizer> customizers) {
return new AutoConfiguredHealthEndpointGroups(applicationContext, properties); AutoConfiguredHealthEndpointGroupsRegistry registry = new AutoConfiguredHealthEndpointGroupsRegistry(
applicationContext, properties);
customizers.orderedStream().forEach((customizer) -> customizer.customize(registry));
return registry.toGroups();
} }
@Bean @Bean

@ -40,11 +40,11 @@ import org.springframework.context.annotation.Primary;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
/** /**
* Tests for {@link AutoConfiguredHealthEndpointGroups}. * Tests for {@link AutoConfiguredHealthEndpointGroupsRegistry}.
* *
* @author Phillip Webb * @author Phillip Webb
*/ */
class AutoConfiguredHealthEndpointGroupsTests { class AutoConfiguredHealthEndpointGroupsBuilderTests {
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner() private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
.withConfiguration(AutoConfigurations.of(AutoConfiguredHealthEndpointGroupsTestConfiguration.class)); .withConfiguration(AutoConfigurations.of(AutoConfiguredHealthEndpointGroupsTestConfiguration.class));
@ -313,9 +313,9 @@ class AutoConfiguredHealthEndpointGroupsTests {
static class AutoConfiguredHealthEndpointGroupsTestConfiguration { static class AutoConfiguredHealthEndpointGroupsTestConfiguration {
@Bean @Bean
AutoConfiguredHealthEndpointGroups healthEndpointGroups(ConfigurableApplicationContext applicationContext, AutoConfiguredHealthEndpointGroupsRegistry healthEndpointGroups(
HealthEndpointProperties properties) { ConfigurableApplicationContext applicationContext, HealthEndpointProperties properties) {
return new AutoConfiguredHealthEndpointGroups(applicationContext, properties); return new AutoConfiguredHealthEndpointGroupsRegistry(applicationContext, properties);
} }
} }

@ -25,8 +25,8 @@ import org.junit.jupiter.api.Test;
import org.mockito.Mock; import org.mockito.Mock;
import org.mockito.MockitoAnnotations; import org.mockito.MockitoAnnotations;
import org.springframework.boot.actuate.autoconfigure.health.HealthProperties.Show;
import org.springframework.boot.actuate.endpoint.SecurityContext; import org.springframework.boot.actuate.endpoint.SecurityContext;
import org.springframework.boot.actuate.health.HealthEndpointGroup.Show;
import org.springframework.boot.actuate.health.HttpCodeStatusMapper; import org.springframework.boot.actuate.health.HttpCodeStatusMapper;
import org.springframework.boot.actuate.health.StatusAggregator; import org.springframework.boot.actuate.health.StatusAggregator;
import org.springframework.security.core.Authentication; import org.springframework.security.core.Authentication;
@ -37,11 +37,11 @@ import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
/** /**
* Tests for {@link AutoConfiguredHealthEndpointGroup}. * Tests for {@link DefaultHealthEndpointGroup}.
* *
* @author Phillip Webb * @author Phillip Webb
*/ */
class AutoConfiguredHealthEndpointGroupTests { class DefaultHealthEndpointGroupTests {
@Mock @Mock
private StatusAggregator statusAggregator; private StatusAggregator statusAggregator;
@ -62,7 +62,7 @@ class AutoConfiguredHealthEndpointGroupTests {
@Test @Test
void isMemberWhenMemberPredicateMatchesAcceptsTrue() { void isMemberWhenMemberPredicateMatchesAcceptsTrue() {
AutoConfiguredHealthEndpointGroup group = new AutoConfiguredHealthEndpointGroup((name) -> name.startsWith("a"), DefaultHealthEndpointGroup group = new DefaultHealthEndpointGroup((name) -> name.startsWith("a"),
this.statusAggregator, this.httpCodeStatusMapper, null, Show.ALWAYS, Collections.emptySet()); this.statusAggregator, this.httpCodeStatusMapper, null, Show.ALWAYS, Collections.emptySet());
assertThat(group.isMember("albert")).isTrue(); assertThat(group.isMember("albert")).isTrue();
assertThat(group.isMember("arnold")).isTrue(); assertThat(group.isMember("arnold")).isTrue();
@ -70,7 +70,7 @@ class AutoConfiguredHealthEndpointGroupTests {
@Test @Test
void isMemberWhenMemberPredicateRejectsReturnsTrue() { void isMemberWhenMemberPredicateRejectsReturnsTrue() {
AutoConfiguredHealthEndpointGroup group = new AutoConfiguredHealthEndpointGroup((name) -> name.startsWith("a"), DefaultHealthEndpointGroup group = new DefaultHealthEndpointGroup((name) -> name.startsWith("a"),
this.statusAggregator, this.httpCodeStatusMapper, null, Show.ALWAYS, Collections.emptySet()); this.statusAggregator, this.httpCodeStatusMapper, null, Show.ALWAYS, Collections.emptySet());
assertThat(group.isMember("bert")).isFalse(); assertThat(group.isMember("bert")).isFalse();
assertThat(group.isMember("ernie")).isFalse(); assertThat(group.isMember("ernie")).isFalse();
@ -78,39 +78,38 @@ class AutoConfiguredHealthEndpointGroupTests {
@Test @Test
void showDetailsWhenShowDetailsIsNeverReturnsFalse() { void showDetailsWhenShowDetailsIsNeverReturnsFalse() {
AutoConfiguredHealthEndpointGroup group = new AutoConfiguredHealthEndpointGroup((name) -> true, DefaultHealthEndpointGroup group = new DefaultHealthEndpointGroup((name) -> true, this.statusAggregator,
this.statusAggregator, this.httpCodeStatusMapper, null, Show.NEVER, Collections.emptySet()); this.httpCodeStatusMapper, null, Show.NEVER, Collections.emptySet());
assertThat(group.showDetails(SecurityContext.NONE)).isFalse(); assertThat(group.showDetails(SecurityContext.NONE)).isFalse();
} }
@Test @Test
void showDetailsWhenShowDetailsIsAlwaysReturnsTrue() { void showDetailsWhenShowDetailsIsAlwaysReturnsTrue() {
AutoConfiguredHealthEndpointGroup group = new AutoConfiguredHealthEndpointGroup((name) -> true, DefaultHealthEndpointGroup group = new DefaultHealthEndpointGroup((name) -> true, this.statusAggregator,
this.statusAggregator, this.httpCodeStatusMapper, null, Show.ALWAYS, Collections.emptySet()); this.httpCodeStatusMapper, null, Show.ALWAYS, Collections.emptySet());
assertThat(group.showDetails(SecurityContext.NONE)).isTrue(); assertThat(group.showDetails(SecurityContext.NONE)).isTrue();
} }
@Test @Test
void showDetailsWhenShowDetailsIsWhenAuthorizedAndPrincipalIsNullReturnsFalse() { void showDetailsWhenShowDetailsIsWhenAuthorizedAndPrincipalIsNullReturnsFalse() {
AutoConfiguredHealthEndpointGroup group = new AutoConfiguredHealthEndpointGroup((name) -> true, DefaultHealthEndpointGroup group = new DefaultHealthEndpointGroup((name) -> true, this.statusAggregator,
this.statusAggregator, this.httpCodeStatusMapper, null, Show.WHEN_AUTHORIZED, Collections.emptySet()); this.httpCodeStatusMapper, null, Show.WHEN_AUTHORIZED, Collections.emptySet());
given(this.securityContext.getPrincipal()).willReturn(null); given(this.securityContext.getPrincipal()).willReturn(null);
assertThat(group.showDetails(this.securityContext)).isFalse(); assertThat(group.showDetails(this.securityContext)).isFalse();
} }
@Test @Test
void showDetailsWhenShowDetailsIsWhenAuthorizedAndRolesAreEmptyReturnsTrue() { void showDetailsWhenShowDetailsIsWhenAuthorizedAndRolesAreEmptyReturnsTrue() {
AutoConfiguredHealthEndpointGroup group = new AutoConfiguredHealthEndpointGroup((name) -> true, DefaultHealthEndpointGroup group = new DefaultHealthEndpointGroup((name) -> true, this.statusAggregator,
this.statusAggregator, this.httpCodeStatusMapper, null, Show.WHEN_AUTHORIZED, Collections.emptySet()); this.httpCodeStatusMapper, null, Show.WHEN_AUTHORIZED, Collections.emptySet());
given(this.securityContext.getPrincipal()).willReturn(this.principal); given(this.securityContext.getPrincipal()).willReturn(this.principal);
assertThat(group.showDetails(this.securityContext)).isTrue(); assertThat(group.showDetails(this.securityContext)).isTrue();
} }
@Test @Test
void showDetailsWhenShowDetailsIsWhenAuthorizedAndUseIsInRoleReturnsTrue() { void showDetailsWhenShowDetailsIsWhenAuthorizedAndUseIsInRoleReturnsTrue() {
AutoConfiguredHealthEndpointGroup group = new AutoConfiguredHealthEndpointGroup((name) -> true, DefaultHealthEndpointGroup group = new DefaultHealthEndpointGroup((name) -> true, this.statusAggregator,
this.statusAggregator, this.httpCodeStatusMapper, null, Show.WHEN_AUTHORIZED, this.httpCodeStatusMapper, null, Show.WHEN_AUTHORIZED, Arrays.asList("admin", "root", "bossmode"));
Arrays.asList("admin", "root", "bossmode"));
given(this.securityContext.getPrincipal()).willReturn(this.principal); given(this.securityContext.getPrincipal()).willReturn(this.principal);
given(this.securityContext.isUserInRole("root")).willReturn(true); given(this.securityContext.isUserInRole("root")).willReturn(true);
assertThat(group.showDetails(this.securityContext)).isTrue(); assertThat(group.showDetails(this.securityContext)).isTrue();
@ -118,9 +117,8 @@ class AutoConfiguredHealthEndpointGroupTests {
@Test @Test
void showDetailsWhenShowDetailsIsWhenAuthorizedAndUseIsNotInRoleReturnsFalse() { void showDetailsWhenShowDetailsIsWhenAuthorizedAndUseIsNotInRoleReturnsFalse() {
AutoConfiguredHealthEndpointGroup group = new AutoConfiguredHealthEndpointGroup((name) -> true, DefaultHealthEndpointGroup group = new DefaultHealthEndpointGroup((name) -> true, this.statusAggregator,
this.statusAggregator, this.httpCodeStatusMapper, null, Show.WHEN_AUTHORIZED, this.httpCodeStatusMapper, null, Show.WHEN_AUTHORIZED, Arrays.asList("admin", "rot", "bossmode"));
Arrays.asList("admin", "rot", "bossmode"));
given(this.securityContext.getPrincipal()).willReturn(this.principal); given(this.securityContext.getPrincipal()).willReturn(this.principal);
given(this.securityContext.isUserInRole("root")).willReturn(true); given(this.securityContext.isUserInRole("root")).willReturn(true);
assertThat(group.showDetails(this.securityContext)).isFalse(); assertThat(group.showDetails(this.securityContext)).isFalse();
@ -128,9 +126,8 @@ class AutoConfiguredHealthEndpointGroupTests {
@Test @Test
void showDetailsWhenShowDetailsIsWhenAuthorizedAndUserHasRightAuthorityReturnsTrue() { void showDetailsWhenShowDetailsIsWhenAuthorizedAndUserHasRightAuthorityReturnsTrue() {
AutoConfiguredHealthEndpointGroup group = new AutoConfiguredHealthEndpointGroup((name) -> true, DefaultHealthEndpointGroup group = new DefaultHealthEndpointGroup((name) -> true, this.statusAggregator,
this.statusAggregator, this.httpCodeStatusMapper, null, Show.WHEN_AUTHORIZED, this.httpCodeStatusMapper, null, Show.WHEN_AUTHORIZED, Arrays.asList("admin", "root", "bossmode"));
Arrays.asList("admin", "root", "bossmode"));
Authentication principal = mock(Authentication.class); Authentication principal = mock(Authentication.class);
given(principal.getAuthorities()) given(principal.getAuthorities())
.willAnswer((invocation) -> Collections.singleton(new SimpleGrantedAuthority("admin"))); .willAnswer((invocation) -> Collections.singleton(new SimpleGrantedAuthority("admin")));
@ -140,9 +137,8 @@ class AutoConfiguredHealthEndpointGroupTests {
@Test @Test
void showDetailsWhenShowDetailsIsWhenAuthorizedAndUserDoesNotHaveRightAuthoritiesReturnsFalse() { void showDetailsWhenShowDetailsIsWhenAuthorizedAndUserDoesNotHaveRightAuthoritiesReturnsFalse() {
AutoConfiguredHealthEndpointGroup group = new AutoConfiguredHealthEndpointGroup((name) -> true, DefaultHealthEndpointGroup group = new DefaultHealthEndpointGroup((name) -> true, this.statusAggregator,
this.statusAggregator, this.httpCodeStatusMapper, null, Show.WHEN_AUTHORIZED, this.httpCodeStatusMapper, null, Show.WHEN_AUTHORIZED, Arrays.asList("admin", "rot", "bossmode"));
Arrays.asList("admin", "rot", "bossmode"));
Authentication principal = mock(Authentication.class); Authentication principal = mock(Authentication.class);
given(principal.getAuthorities()) given(principal.getAuthorities())
.willAnswer((invocation) -> Collections.singleton(new SimpleGrantedAuthority("other"))); .willAnswer((invocation) -> Collections.singleton(new SimpleGrantedAuthority("other")));
@ -152,50 +148,48 @@ class AutoConfiguredHealthEndpointGroupTests {
@Test @Test
void showComponentsWhenShowComponentsIsNullDelegatesToShowDetails() { void showComponentsWhenShowComponentsIsNullDelegatesToShowDetails() {
AutoConfiguredHealthEndpointGroup alwaysGroup = new AutoConfiguredHealthEndpointGroup((name) -> true, DefaultHealthEndpointGroup alwaysGroup = new DefaultHealthEndpointGroup((name) -> true, this.statusAggregator,
this.statusAggregator, this.httpCodeStatusMapper, null, Show.ALWAYS, Collections.emptySet()); this.httpCodeStatusMapper, null, Show.ALWAYS, Collections.emptySet());
assertThat(alwaysGroup.showComponents(SecurityContext.NONE)).isTrue(); assertThat(alwaysGroup.showComponents(SecurityContext.NONE)).isTrue();
AutoConfiguredHealthEndpointGroup neverGroup = new AutoConfiguredHealthEndpointGroup((name) -> true, DefaultHealthEndpointGroup neverGroup = new DefaultHealthEndpointGroup((name) -> true, this.statusAggregator,
this.statusAggregator, this.httpCodeStatusMapper, null, Show.NEVER, Collections.emptySet()); this.httpCodeStatusMapper, null, Show.NEVER, Collections.emptySet());
assertThat(neverGroup.showComponents(SecurityContext.NONE)).isFalse(); assertThat(neverGroup.showComponents(SecurityContext.NONE)).isFalse();
} }
@Test @Test
void showComponentsWhenShowComponentsIsNeverReturnsFalse() { void showComponentsWhenShowComponentsIsNeverReturnsFalse() {
AutoConfiguredHealthEndpointGroup group = new AutoConfiguredHealthEndpointGroup((name) -> true, DefaultHealthEndpointGroup group = new DefaultHealthEndpointGroup((name) -> true, this.statusAggregator,
this.statusAggregator, this.httpCodeStatusMapper, Show.NEVER, Show.ALWAYS, Collections.emptySet()); this.httpCodeStatusMapper, Show.NEVER, Show.ALWAYS, Collections.emptySet());
assertThat(group.showComponents(SecurityContext.NONE)).isFalse(); assertThat(group.showComponents(SecurityContext.NONE)).isFalse();
} }
@Test @Test
void showComponentsWhenShowComponentsIsAlwaysReturnsTrue() { void showComponentsWhenShowComponentsIsAlwaysReturnsTrue() {
AutoConfiguredHealthEndpointGroup group = new AutoConfiguredHealthEndpointGroup((name) -> true, DefaultHealthEndpointGroup group = new DefaultHealthEndpointGroup((name) -> true, this.statusAggregator,
this.statusAggregator, this.httpCodeStatusMapper, Show.ALWAYS, Show.NEVER, Collections.emptySet()); this.httpCodeStatusMapper, Show.ALWAYS, Show.NEVER, Collections.emptySet());
assertThat(group.showComponents(SecurityContext.NONE)).isTrue(); assertThat(group.showComponents(SecurityContext.NONE)).isTrue();
} }
@Test @Test
void showComponentsWhenShowComponentsIsWhenAuthorizedAndPrincipalIsNullReturnsFalse() { void showComponentsWhenShowComponentsIsWhenAuthorizedAndPrincipalIsNullReturnsFalse() {
AutoConfiguredHealthEndpointGroup group = new AutoConfiguredHealthEndpointGroup((name) -> true, DefaultHealthEndpointGroup group = new DefaultHealthEndpointGroup((name) -> true, this.statusAggregator,
this.statusAggregator, this.httpCodeStatusMapper, Show.WHEN_AUTHORIZED, Show.NEVER, this.httpCodeStatusMapper, Show.WHEN_AUTHORIZED, Show.NEVER, Collections.emptySet());
Collections.emptySet());
given(this.securityContext.getPrincipal()).willReturn(null); given(this.securityContext.getPrincipal()).willReturn(null);
assertThat(group.showComponents(this.securityContext)).isFalse(); assertThat(group.showComponents(this.securityContext)).isFalse();
} }
@Test @Test
void showComponentsWhenShowComponentsIsWhenAuthorizedAndRolesAreEmptyReturnsTrue() { void showComponentsWhenShowComponentsIsWhenAuthorizedAndRolesAreEmptyReturnsTrue() {
AutoConfiguredHealthEndpointGroup group = new AutoConfiguredHealthEndpointGroup((name) -> true, DefaultHealthEndpointGroup group = new DefaultHealthEndpointGroup((name) -> true, this.statusAggregator,
this.statusAggregator, this.httpCodeStatusMapper, Show.WHEN_AUTHORIZED, Show.NEVER, this.httpCodeStatusMapper, Show.WHEN_AUTHORIZED, Show.NEVER, Collections.emptySet());
Collections.emptySet());
given(this.securityContext.getPrincipal()).willReturn(this.principal); given(this.securityContext.getPrincipal()).willReturn(this.principal);
assertThat(group.showComponents(this.securityContext)).isTrue(); assertThat(group.showComponents(this.securityContext)).isTrue();
} }
@Test @Test
void showComponentsWhenShowComponentsIsWhenAuthorizedAndUseIsInRoleReturnsTrue() { void showComponentsWhenShowComponentsIsWhenAuthorizedAndUseIsInRoleReturnsTrue() {
AutoConfiguredHealthEndpointGroup group = new AutoConfiguredHealthEndpointGroup((name) -> true, DefaultHealthEndpointGroup group = new DefaultHealthEndpointGroup((name) -> true, this.statusAggregator,
this.statusAggregator, this.httpCodeStatusMapper, Show.WHEN_AUTHORIZED, Show.NEVER, this.httpCodeStatusMapper, Show.WHEN_AUTHORIZED, Show.NEVER,
Arrays.asList("admin", "root", "bossmode")); Arrays.asList("admin", "root", "bossmode"));
given(this.securityContext.getPrincipal()).willReturn(this.principal); given(this.securityContext.getPrincipal()).willReturn(this.principal);
given(this.securityContext.isUserInRole("root")).willReturn(true); given(this.securityContext.isUserInRole("root")).willReturn(true);
@ -204,9 +198,8 @@ class AutoConfiguredHealthEndpointGroupTests {
@Test @Test
void showComponentsWhenShowComponentsIsWhenAuthorizedAndUseIsNotInRoleReturnsFalse() { void showComponentsWhenShowComponentsIsWhenAuthorizedAndUseIsNotInRoleReturnsFalse() {
AutoConfiguredHealthEndpointGroup group = new AutoConfiguredHealthEndpointGroup((name) -> true, DefaultHealthEndpointGroup group = new DefaultHealthEndpointGroup((name) -> true, this.statusAggregator,
this.statusAggregator, this.httpCodeStatusMapper, Show.WHEN_AUTHORIZED, Show.NEVER, this.httpCodeStatusMapper, Show.WHEN_AUTHORIZED, Show.NEVER, Arrays.asList("admin", "rot", "bossmode"));
Arrays.asList("admin", "rot", "bossmode"));
given(this.securityContext.getPrincipal()).willReturn(this.principal); given(this.securityContext.getPrincipal()).willReturn(this.principal);
given(this.securityContext.isUserInRole("root")).willReturn(true); given(this.securityContext.isUserInRole("root")).willReturn(true);
assertThat(group.showComponents(this.securityContext)).isFalse(); assertThat(group.showComponents(this.securityContext)).isFalse();
@ -214,8 +207,8 @@ class AutoConfiguredHealthEndpointGroupTests {
@Test @Test
void showComponentsWhenShowComponentsIsWhenAuthorizedAndUserHasRightAuthoritiesReturnsTrue() { void showComponentsWhenShowComponentsIsWhenAuthorizedAndUserHasRightAuthoritiesReturnsTrue() {
AutoConfiguredHealthEndpointGroup group = new AutoConfiguredHealthEndpointGroup((name) -> true, DefaultHealthEndpointGroup group = new DefaultHealthEndpointGroup((name) -> true, this.statusAggregator,
this.statusAggregator, this.httpCodeStatusMapper, Show.WHEN_AUTHORIZED, Show.NEVER, this.httpCodeStatusMapper, Show.WHEN_AUTHORIZED, Show.NEVER,
Arrays.asList("admin", "root", "bossmode")); Arrays.asList("admin", "root", "bossmode"));
Authentication principal = mock(Authentication.class); Authentication principal = mock(Authentication.class);
given(principal.getAuthorities()) given(principal.getAuthorities())
@ -226,9 +219,8 @@ class AutoConfiguredHealthEndpointGroupTests {
@Test @Test
void showComponentsWhenShowComponentsIsWhenAuthorizedAndUserDoesNotHaveRightAuthoritiesReturnsFalse() { void showComponentsWhenShowComponentsIsWhenAuthorizedAndUserDoesNotHaveRightAuthoritiesReturnsFalse() {
AutoConfiguredHealthEndpointGroup group = new AutoConfiguredHealthEndpointGroup((name) -> true, DefaultHealthEndpointGroup group = new DefaultHealthEndpointGroup((name) -> true, this.statusAggregator,
this.statusAggregator, this.httpCodeStatusMapper, Show.WHEN_AUTHORIZED, Show.NEVER, this.httpCodeStatusMapper, Show.WHEN_AUTHORIZED, Show.NEVER, Arrays.asList("admin", "rot", "bossmode"));
Arrays.asList("admin", "rot", "bossmode"));
Authentication principal = mock(Authentication.class); Authentication principal = mock(Authentication.class);
given(principal.getAuthorities()) given(principal.getAuthorities())
.willAnswer((invocation) -> Collections.singleton(new SimpleGrantedAuthority("other"))); .willAnswer((invocation) -> Collections.singleton(new SimpleGrantedAuthority("other")));
@ -238,15 +230,15 @@ class AutoConfiguredHealthEndpointGroupTests {
@Test @Test
void getStatusAggregatorReturnsStatusAggregator() { void getStatusAggregatorReturnsStatusAggregator() {
AutoConfiguredHealthEndpointGroup group = new AutoConfiguredHealthEndpointGroup((name) -> true, DefaultHealthEndpointGroup group = new DefaultHealthEndpointGroup((name) -> true, this.statusAggregator,
this.statusAggregator, this.httpCodeStatusMapper, null, Show.ALWAYS, Collections.emptySet()); this.httpCodeStatusMapper, null, Show.ALWAYS, Collections.emptySet());
assertThat(group.getStatusAggregator()).isSameAs(this.statusAggregator); assertThat(group.getStatusAggregator()).isSameAs(this.statusAggregator);
} }
@Test @Test
void getHttpCodeStatusMapperReturnsHttpCodeStatusMapper() { void getHttpCodeStatusMapperReturnsHttpCodeStatusMapper() {
AutoConfiguredHealthEndpointGroup group = new AutoConfiguredHealthEndpointGroup((name) -> true, DefaultHealthEndpointGroup group = new DefaultHealthEndpointGroup((name) -> true, this.statusAggregator,
this.statusAggregator, this.httpCodeStatusMapper, null, Show.ALWAYS, Collections.emptySet()); this.httpCodeStatusMapper, null, Show.ALWAYS, Collections.emptySet());
assertThat(group.getHttpCodeStatusMapper()).isSameAs(this.httpCodeStatusMapper); assertThat(group.getHttpCodeStatusMapper()).isSameAs(this.httpCodeStatusMapper);
} }

@ -37,6 +37,7 @@ import org.springframework.boot.actuate.health.HealthComponent;
import org.springframework.boot.actuate.health.HealthContributorRegistry; import org.springframework.boot.actuate.health.HealthContributorRegistry;
import org.springframework.boot.actuate.health.HealthEndpoint; import org.springframework.boot.actuate.health.HealthEndpoint;
import org.springframework.boot.actuate.health.HealthEndpointGroups; import org.springframework.boot.actuate.health.HealthEndpointGroups;
import org.springframework.boot.actuate.health.HealthEndpointGroupsRegistryCustomizer;
import org.springframework.boot.actuate.health.HealthEndpointWebExtension; import org.springframework.boot.actuate.health.HealthEndpointWebExtension;
import org.springframework.boot.actuate.health.HealthIndicator; import org.springframework.boot.actuate.health.HealthIndicator;
import org.springframework.boot.actuate.health.HttpCodeStatusMapper; import org.springframework.boot.actuate.health.HttpCodeStatusMapper;
@ -154,7 +155,6 @@ class HealthEndpointAutoConfigurationTests {
void runCreatesHealthEndpointGroups() { void runCreatesHealthEndpointGroups() {
this.contextRunner.withPropertyValues("management.endpoint.health.group.ready.include=*").run((context) -> { this.contextRunner.withPropertyValues("management.endpoint.health.group.ready.include=*").run((context) -> {
HealthEndpointGroups groups = context.getBean(HealthEndpointGroups.class); HealthEndpointGroups groups = context.getBean(HealthEndpointGroups.class);
assertThat(groups).isInstanceOf(AutoConfiguredHealthEndpointGroups.class);
assertThat(groups.getNames()).containsOnly("ready"); assertThat(groups.getNames()).containsOnly("ready");
}); });
} }
@ -301,6 +301,15 @@ class HealthEndpointAutoConfigurationTests {
.run((context) -> assertThat(context).doesNotHaveBean(ReactiveHealthIndicatorRegistry.class)); .run((context) -> assertThat(context).doesNotHaveBean(ReactiveHealthIndicatorRegistry.class));
} }
@Test
void runWhenHealthEndpointGroupsRegistryCustomizerAddsHealthEndpointGroup() {
this.contextRunner.withUserConfiguration(HealthEndpointGroupsRegistryCustomizerConfig.class).run((context) -> {
assertThat(context).hasSingleBean(HealthEndpointGroupsRegistryCustomizer.class);
HealthEndpointGroups groups = context.getBean(HealthEndpointGroups.class);
assertThat(groups.getNames()).contains("test");
});
}
@Configuration(proxyBeanMethods = false) @Configuration(proxyBeanMethods = false)
static class HealthIndicatorsConfiguration { static class HealthIndicatorsConfiguration {
@ -420,4 +429,14 @@ class HealthEndpointAutoConfigurationTests {
} }
@Configuration(proxyBeanMethods = false)
static class HealthEndpointGroupsRegistryCustomizerConfig {
@Bean
HealthEndpointGroupsRegistryCustomizer customHealthEndpointGroup() {
return (registry) -> registry.add("test", (configurer) -> configurer.include("ping"));
}
}
} }

@ -1,5 +1,5 @@
/* /*
* Copyright 2012-2019 the original author or authors. * Copyright 2012-2020 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -23,6 +23,7 @@ import org.springframework.boot.actuate.endpoint.SecurityContext;
* by the {@link HealthEndpoint}. * by the {@link HealthEndpoint}.
* *
* @author Phillip Webb * @author Phillip Webb
* @author Brian Clozel
* @since 2.2.0 * @since 2.2.0
*/ */
public interface HealthEndpointGroup { public interface HealthEndpointGroup {
@ -62,4 +63,27 @@ public interface HealthEndpointGroup {
*/ */
HttpCodeStatusMapper getHttpCodeStatusMapper(); HttpCodeStatusMapper getHttpCodeStatusMapper();
/**
* Options for showing items in responses from the {@link HealthEndpointGroup} web
* extensions.
*/
enum Show {
/**
* Never show the item in the response.
*/
NEVER,
/**
* Show the item in the response when accessed by an authorized user.
*/
WHEN_AUTHORIZED,
/**
* Always show the item in the response.
*/
ALWAYS
}
} }

@ -0,0 +1,84 @@
/*
* Copyright 2012-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.actuate.health;
import org.springframework.boot.actuate.health.HealthEndpointGroup.Show;
/**
* A configurer for customizing an {@link HealthEndpointGroup} being built.
*
* @author Brian Clozel
* @since 2.3.0
*/
public interface HealthEndpointGroupConfigurer {
/**
* Configure the indicator endpoint ids to include in this group.
* @param indicators the indicator endpoint ids
* @return the configurer instance
*/
HealthEndpointGroupConfigurer include(String... indicators);
/**
* Configure the indicator endpoint ids to exclude from this group.
* @param indicators the indicator endpoint ids
* @return the configurer instance
*/
HealthEndpointGroupConfigurer exclude(String... indicators);
/**
* Configure the {@link StatusAggregator} to use for this group.
* <p>
* If none set, this will default to the globalmy configured {@link StatusAggregator}.
* @param statusAggregator the status aggregator
* @return the configurer instance
*/
HealthEndpointGroupConfigurer statusAggregator(StatusAggregator statusAggregator);
/**
* Configure the {@link HttpCodeStatusMapper} to use for this group.
* <p>
* If none set, this will default to the globalmy configured
* {@link HttpCodeStatusMapper}.
* @param httpCodeStatusMapper the status code mapper
* @return the configurer instance
*/
HealthEndpointGroupConfigurer httpCodeStatusMapper(HttpCodeStatusMapper httpCodeStatusMapper);
/**
* Configure the {@link Show visbility option} for showing components of this group.
* @param showComponents the components visibility
* @return the configurer instance
*/
HealthEndpointGroupConfigurer showComponents(Show showComponents);
/**
* Configure the {@link Show visbility option} for showing details of this group.
* @param showDetails the details visibility
* @return the configurer instance
*/
HealthEndpointGroupConfigurer showDetails(Show showDetails);
/**
* Configure roles used to determine whether or not a user is authorized to be shown
* details.
* @param roles the roles
* @return the configurer instance
*/
HealthEndpointGroupConfigurer roles(String... roles);
}

@ -0,0 +1,50 @@
/*
* Copyright 2012-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.actuate.health;
import java.util.function.Consumer;
/**
* Builder for an {@link HealthEndpointGroups} immutable instance.
*
* @author Brian Clozel
* @since 2.3.0
*/
public interface HealthEndpointGroupsRegistry extends HealthEndpointGroups {
/**
* Add a new {@link HealthEndpointGroup}.
* @param groupName the name of the group to add
* @param builder the group to add
* @return the builder instance
*/
HealthEndpointGroupsRegistry add(String groupName, Consumer<HealthEndpointGroupConfigurer> builder);
/**
* Remove an existing {@link HealthEndpointGroup}.
* @param groupName the name of the group to remove
* @return the builder instance
*/
HealthEndpointGroupsRegistry remove(String groupName);
/**
* Build an immutable {@link HealthEndpointGroups}.
* @return the {@link HealthEndpointGroups}
*/
HealthEndpointGroups toGroups();
}

@ -0,0 +1,35 @@
/*
* Copyright 2012-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.actuate.health;
/**
* Callback interface that can be used to customize a
* {@link HealthEndpointGroupsRegistry}.
*
* @author Brian Clozel
* @since 2.3.0
*/
@FunctionalInterface
public interface HealthEndpointGroupsRegistryCustomizer {
/**
* Callback to customize a {@link HealthEndpointGroupsRegistry} instance.
* @param healthEndpointGroupsRegistry the registry to customize
*/
void customize(HealthEndpointGroupsRegistry healthEndpointGroupsRegistry);
}
Loading…
Cancel
Save