Skip to content

Commit c303f55

Browse files
tweiandmp911de
authored andcommitted
ReactiveVaultTemplate for the key-value backend version 2.
Closes gh-576 Original pull request: gh-807
1 parent c7f47b2 commit c303f55

27 files changed

+2032
-159
lines changed

spring-vault-core/src/main/java/org/springframework/vault/client/VaultResponses.java

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
import org.springframework.util.Assert;
3434
import org.springframework.util.StringUtils;
3535
import org.springframework.vault.VaultException;
36+
import org.springframework.vault.support.VaultResponseDataVersion2;
3637
import org.springframework.vault.support.VaultResponseSupport;
3738
import org.springframework.web.client.HttpStatusCodeException;
3839

@@ -134,6 +135,68 @@ public Type getType() {
134135
};
135136
}
136137

138+
public static <T> ParameterizedTypeReference<VaultResponseSupport<T>> getTypeReference(
139+
final ParameterizedTypeReference<T> responseType) {
140+
141+
Assert.notNull(responseType, "Response type must not be null");
142+
143+
final Type supportType = new ParameterizedType() {
144+
145+
@Override
146+
public Type[] getActualTypeArguments() {
147+
return new Type[] { responseType.getType() };
148+
}
149+
150+
@Override
151+
public Type getRawType() {
152+
return VaultResponseSupport.class;
153+
}
154+
155+
@Override
156+
public Type getOwnerType() {
157+
return VaultResponseSupport.class;
158+
}
159+
};
160+
161+
return new ParameterizedTypeReference<VaultResponseSupport<T>>() {
162+
@Override
163+
public Type getType() {
164+
return supportType;
165+
}
166+
};
167+
}
168+
169+
public static <T> ParameterizedTypeReference<VaultResponseDataVersion2<T>> getDataTypeReference(
170+
final Class<T> responseType) {
171+
172+
Assert.notNull(responseType, "Response type must not be null");
173+
174+
final Type supportType = new ParameterizedType() {
175+
176+
@Override
177+
public Type[] getActualTypeArguments() {
178+
return new Type[] { responseType };
179+
}
180+
181+
@Override
182+
public Type getRawType() {
183+
return VaultResponseDataVersion2.class;
184+
}
185+
186+
@Override
187+
public Type getOwnerType() {
188+
return VaultResponseDataVersion2.class;
189+
}
190+
};
191+
192+
return new ParameterizedTypeReference<VaultResponseDataVersion2<T>>() {
193+
@Override
194+
public Type getType() {
195+
return supportType;
196+
}
197+
};
198+
}
199+
137200
/**
138201
* Obtain the error message from a JSON response.
139202
* @param json must not be {@literal null}.
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
/*
2+
* Copyright 2018-2022 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.vault.core;
17+
18+
import java.util.Collections;
19+
import java.util.HashMap;
20+
import java.util.LinkedHashMap;
21+
import java.util.Map;
22+
import org.springframework.vault.support.VaultResponseSupport;
23+
import reactor.core.publisher.Mono;
24+
25+
/**
26+
* Helper for wrapping imperative operations.
27+
*
28+
* @author Timothy R. Weiand
29+
* @since 3.1
30+
*/
31+
class ReactiveKeyValueHelper {
32+
33+
ReactiveKeyValueHelper() {
34+
}
35+
36+
static <T> Mono<T> getRequiredData(VaultResponseSupport<T> support) {
37+
return Mono.fromCallable(support::getRequiredData);
38+
}
39+
40+
static Map<String, Object> makeMetadata(final Map<String, Object> metadata, final Map<String, Object> requiredData,
41+
Map<String, ?> patch) {
42+
Map<String, Object> data = new LinkedHashMap<>(requiredData);
43+
data.putAll(patch);
44+
45+
Map<String, Object> body = new HashMap<>();
46+
body.put("data", data);
47+
body.put("options", Collections.singletonMap("cas", metadata.get("version")));
48+
49+
return body;
50+
}
51+
52+
}
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
/*
2+
* Copyright 2018-2022 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.vault.core;
17+
18+
import java.util.Map;
19+
import org.springframework.core.ParameterizedTypeReference;
20+
import org.springframework.util.Assert;
21+
import org.springframework.vault.core.VaultKeyValueOperationsSupport.KeyValueBackend;
22+
import org.springframework.vault.support.VaultResponse;
23+
import org.springframework.vault.support.VaultResponseSupport;
24+
import org.springframework.web.reactive.function.client.WebClientResponseException;
25+
import reactor.core.publisher.Flux;
26+
import reactor.core.publisher.Mono;
27+
28+
/**
29+
* Default implementation of {@link ReactiveVaultKeyValueOperations} for the Key/Value
30+
* backend version 1.
31+
*
32+
* @author Timothy R. Weiand
33+
* @since 3.1
34+
*/
35+
class ReactiveVaultKeyValue1Template extends ReactiveVaultKeyValueAccessor implements ReactiveVaultKeyValueOperations {
36+
37+
/**
38+
* Create a new {@link ReactiveVaultKeyValue1Template} given
39+
* {@link ReactiveVaultOperations} and the mount {@code path}.
40+
* @param vaultOperations must not be {@literal null}.
41+
* @param path must not be empty or {@literal null}.
42+
*/
43+
public ReactiveVaultKeyValue1Template(ReactiveVaultOperations vaultOperations, String path) {
44+
45+
super(vaultOperations, path);
46+
}
47+
48+
@Override
49+
public Flux<String> list(String path) {
50+
return reactiveVaultOperations.list(createDataPath(path));
51+
}
52+
53+
@Override
54+
@SuppressWarnings("unchecked")
55+
public Mono<VaultResponse> get(String path) {
56+
ParameterizedTypeReference<Map<String, Object>> ref = new ParameterizedTypeReference<>() {
57+
};
58+
59+
return doRead(path, ref).onErrorResume(WebClientResponseException.NotFound.class, e -> Mono.empty())
60+
.map(response -> {
61+
VaultResponse vaultResponse = new VaultResponse();
62+
VaultResponseSupport.updateWithoutData(vaultResponse, response);
63+
vaultResponse.setData(response.getData());
64+
return vaultResponse;
65+
});
66+
}
67+
68+
@Override
69+
public <T> Mono<VaultResponseSupport<T>> get(String path, Class<T> responseType) {
70+
71+
return doRead(path, responseType).onErrorResume(WebClientResponseException.NotFound.class, e -> Mono.empty());
72+
}
73+
74+
@Override
75+
public Mono<Boolean> patch(String path, Map<String, ?> patch) {
76+
throw new IllegalStateException("K/V engine mount must be version 2 for patch support");
77+
}
78+
79+
@Override
80+
public Mono<Void> put(String path, Object body) {
81+
82+
Assert.hasText(path, "Path must not be empty");
83+
84+
return doWrite(createDataPath(path), body).then();
85+
}
86+
87+
@Override
88+
public KeyValueBackend getApiVersion() {
89+
return KeyValueBackend.KV_1;
90+
}
91+
92+
@Override
93+
String createDataPath(String path) {
94+
return String.format("%s/%s", this.path, path);
95+
}
96+
97+
}
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
/*
2+
* Copyright 2018-2022 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.vault.core;
17+
18+
import java.util.List;
19+
import java.util.Map;
20+
import org.springframework.core.ParameterizedTypeReference;
21+
import org.springframework.vault.client.VaultResponses;
22+
import org.springframework.vault.core.VaultKeyValueOperationsSupport.KeyValueBackend;
23+
import org.springframework.vault.support.VaultResponseSupport;
24+
import reactor.core.publisher.Flux;
25+
26+
/**
27+
* Support class to build accessor methods for the Vault key-value backend version 2.
28+
*
29+
* @author Timothy R. Weiand
30+
* @since 3.1
31+
* @see KeyValueBackend#KV_2
32+
*/
33+
abstract class ReactiveVaultKeyValue2Accessor extends ReactiveVaultKeyValueAccessor {
34+
35+
final String path;
36+
37+
/**
38+
* Create a new {@link ReactiveVaultKeyValue2Accessor} given {@link VaultOperations}
39+
* and the mount {@code path}.
40+
* @param reactiveVaultOperations must not be {@literal null}.
41+
* @param path must not be empty or {@literal null}.
42+
*/
43+
ReactiveVaultKeyValue2Accessor(ReactiveVaultOperations reactiveVaultOperations, String path) {
44+
45+
super(reactiveVaultOperations, path);
46+
47+
this.path = path;
48+
}
49+
50+
@Override
51+
@SuppressWarnings("unchecked")
52+
public Flux<String> list(String path) {
53+
54+
String pathToUse = path.equals("/") ? "" : path.endsWith("/") ? path : (path + "/");
55+
56+
// TODO: to test - null returns empty
57+
ParameterizedTypeReference<VaultResponseSupport<Map<String, Object>>> type = VaultResponses
58+
.getTypeReference(new ParameterizedTypeReference<>() {
59+
});
60+
61+
return doReadRaw(String.format("%s?list=true", createBackendPath("metadata", pathToUse)), type, false)
62+
.flatMap(ReactiveKeyValueHelper::getRequiredData)
63+
.flatMapMany(response -> {
64+
final List<String> list = (List<String>) response.get("keys");
65+
if (null == list) {
66+
return Flux.empty();
67+
}
68+
return Flux.fromIterable(list);
69+
70+
});
71+
}
72+
73+
@Override
74+
public KeyValueBackend getApiVersion() {
75+
return KeyValueBackend.KV_2;
76+
}
77+
78+
String createDataPath(String path) {
79+
return createBackendPath("data", path);
80+
}
81+
82+
String createBackendPath(String segment, String path) {
83+
return String.format("%s/%s/%s", this.path, segment, path);
84+
}
85+
86+
}

0 commit comments

Comments
 (0)