diff --git a/spring-vault-core/src/main/java/org/springframework/vault/core/ReactiveVaultOperations.java b/spring-vault-core/src/main/java/org/springframework/vault/core/ReactiveVaultOperations.java
index df0c9425..ffceb314 100644
--- a/spring-vault-core/src/main/java/org/springframework/vault/core/ReactiveVaultOperations.java
+++ b/spring-vault-core/src/main/java/org/springframework/vault/core/ReactiveVaultOperations.java
@@ -64,6 +64,12 @@ public interface ReactiveVaultOperations {
*/
ReactiveVaultTransitOperations opsForTransit(String path);
+ /**
+ * @return the operations interface administrative Vault access.
+ * @since 3.1
+ */
+ ReactiveVaultSysOperations opsForSys();
+
/**
* Read from a Vault path. Reading data using this method is suitable for API
* calls/secret backends that do not require a request body.
diff --git a/spring-vault-core/src/main/java/org/springframework/vault/core/ReactiveVaultSysOperations.java b/spring-vault-core/src/main/java/org/springframework/vault/core/ReactiveVaultSysOperations.java
new file mode 100644
index 00000000..2a6d87fc
--- /dev/null
+++ b/spring-vault-core/src/main/java/org/springframework/vault/core/ReactiveVaultSysOperations.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2016-2023 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.vault.core;
+
+import reactor.core.publisher.Mono;
+
+import org.springframework.vault.VaultException;
+import org.springframework.vault.support.VaultHealth;
+
+/**
+ * Interface that specifies a basic set of administrative Vault operations using reactive
+ * infrastructure.
+ *
+ * @author Mark Paluch
+ * @since 3.1
+ */
+public interface ReactiveVaultSysOperations {
+
+ /**
+ * @return {@literal true} if Vault is initialized.
+ * @see GET
+ * /sys/init
+ */
+ Mono isInitialized() throws VaultException;
+
+ /**
+ * Return the health status of Vault.
+ * @return the {@link VaultHealth}.
+ * @see GET
+ * /sys/health
+ */
+ Mono health() throws VaultException;
+
+}
diff --git a/spring-vault-core/src/main/java/org/springframework/vault/core/ReactiveVaultSysTemplate.java b/spring-vault-core/src/main/java/org/springframework/vault/core/ReactiveVaultSysTemplate.java
new file mode 100644
index 00000000..67c8761b
--- /dev/null
+++ b/spring-vault-core/src/main/java/org/springframework/vault/core/ReactiveVaultSysTemplate.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2016-2023 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.vault.core;
+
+import java.util.Map;
+
+import org.springframework.http.HttpEntity;
+import org.springframework.util.Assert;
+import org.springframework.vault.client.VaultHttpHeaders;
+import org.springframework.vault.support.VaultHealth;
+
+import reactor.core.publisher.Mono;
+
+/**
+ * Default implementation of {@link ReactiveVaultSysOperations}.
+ *
+ * @author Mark Paluch
+ */
+public class ReactiveVaultSysTemplate implements ReactiveVaultSysOperations {
+
+ private final ReactiveVaultOperations vaultOperations;
+
+ /**
+ * Create a new {@link ReactiveVaultSysTemplate} with the given
+ * {@link ReactiveVaultOperations}.
+ * @param vaultOperations must not be {@literal null}.
+ */
+ public ReactiveVaultSysTemplate(ReactiveVaultOperations vaultOperations) {
+
+ Assert.notNull(vaultOperations, "ReactiveVaultOperations must not be null");
+
+ this.vaultOperations = vaultOperations;
+
+ }
+
+ @Override
+ public Mono isInitialized() {
+
+ return this.vaultOperations.doWithSession(webClient -> {
+ return webClient.get()
+ .uri("sys/init")
+ .header(VaultHttpHeaders.VAULT_NAMESPACE, "")
+ .exchangeToMono(clientResponse -> clientResponse.toEntity(Map.class))
+ .map(it -> (Boolean) it.getBody().get("initialized"));
+ });
+ }
+
+ @Override
+ public Mono health() {
+
+ return this.vaultOperations.doWithVault(webClient -> {
+
+ return webClient.get()
+ .uri("sys/health")
+ .header(VaultHttpHeaders.VAULT_NAMESPACE, "")
+ .exchangeToMono(clientResponse -> {
+ return clientResponse.toEntity(VaultSysTemplate.VaultHealthImpl.class).map(HttpEntity::getBody);
+ });
+ });
+ }
+
+}
diff --git a/spring-vault-core/src/main/java/org/springframework/vault/core/ReactiveVaultTemplate.java b/spring-vault-core/src/main/java/org/springframework/vault/core/ReactiveVaultTemplate.java
index aa5dbfbd..3ad44a11 100644
--- a/spring-vault-core/src/main/java/org/springframework/vault/core/ReactiveVaultTemplate.java
+++ b/spring-vault-core/src/main/java/org/springframework/vault/core/ReactiveVaultTemplate.java
@@ -227,6 +227,11 @@ public class ReactiveVaultTemplate implements ReactiveVaultOperations {
}));
}
+ @Override
+ public ReactiveVaultSysOperations opsForSys() {
+ return new ReactiveVaultSysTemplate(this);
+ }
+
@Override
public ReactiveVaultTransitOperations opsForTransit() {
return opsForTransit("transit");
diff --git a/spring-vault-core/src/main/java/org/springframework/vault/core/VaultSysTemplate.java b/spring-vault-core/src/main/java/org/springframework/vault/core/VaultSysTemplate.java
index a119aad5..841bfba5 100644
--- a/spring-vault-core/src/main/java/org/springframework/vault/core/VaultSysTemplate.java
+++ b/spring-vault-core/src/main/java/org/springframework/vault/core/VaultSysTemplate.java
@@ -398,8 +398,7 @@ public class VaultSysTemplate implements VaultSysOperations {
catch (RestClientResponseException responseError) {
try {
- ObjectMapper mapper = new ObjectMapper();
- return mapper.readValue(responseError.getResponseBodyAsString(), VaultHealthImpl.class);
+ return OBJECT_MAPPER.readValue(responseError.getResponseBodyAsString(), VaultHealthImpl.class);
}
catch (Exception jsonError) {
throw responseError;
diff --git a/spring-vault-core/src/test/java/org/springframework/vault/core/ReactiveVaultSysTemplateIntegrationTests.java b/spring-vault-core/src/test/java/org/springframework/vault/core/ReactiveVaultSysTemplateIntegrationTests.java
new file mode 100644
index 00000000..552a515d
--- /dev/null
+++ b/spring-vault-core/src/test/java/org/springframework/vault/core/ReactiveVaultSysTemplateIntegrationTests.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2016-2023 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.vault.core;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.junit.jupiter.SpringExtension;
+import org.springframework.vault.util.IntegrationTestSupport;
+import org.springframework.vault.util.RequiresVaultVersion;
+
+import reactor.test.StepVerifier;
+
+import static org.assertj.core.api.Assertions.*;
+
+/**
+ * Integration tests for {@link ReactiveVaultSysTemplate} through
+ * {@link ReactiveVaultSysOperations}.
+ *
+ * @author Mark Paluch
+ */
+@ExtendWith(SpringExtension.class)
+@ContextConfiguration(classes = VaultIntegrationTestConfiguration.class)
+class ReactiveVaultSysTemplateIntegrationTests extends IntegrationTestSupport {
+
+ @Autowired
+ ReactiveVaultOperations vaultOperations;
+
+ ReactiveVaultSysOperations adminOperations;
+
+ @BeforeEach
+ void before() {
+ this.adminOperations = this.vaultOperations.opsForSys();
+ }
+
+ @Test
+ @RequiresVaultVersion("0.6.1")
+ void shouldReportHealth() {
+
+ this.adminOperations.health().as(StepVerifier::create).assertNext(health -> {
+ assertThat(health.isInitialized()).isTrue();
+ assertThat(health.isSealed()).isFalse();
+ assertThat(health.isPerformanceStandby()).isFalse();
+ assertThat(health.isRecoveryReplicationSecondary()).isFalse();
+ assertThat(health.isStandby()).isFalse();
+ }).verifyComplete();
+ }
+
+ @Test
+ void isInitializedShouldReturnTrue() {
+ this.adminOperations.isInitialized().as(StepVerifier::create).expectNext(true).verifyComplete();
+ }
+
+}