Skip to content

Commit b2ef67c

Browse files
committed
Enhanced RequestContext with headerParams() methods, + fixed javadocs
1 parent 3c5ddaa commit b2ef67c

File tree

4 files changed

+105
-6
lines changed

4 files changed

+105
-6
lines changed

services-api/src/main/java/io/scalecube/services/RequestContext.java

Lines changed: 45 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import io.scalecube.services.exceptions.ForbiddenException;
1111
import io.scalecube.services.methods.MethodInfo;
1212
import java.util.Collections;
13+
import java.util.HashMap;
1314
import java.util.Map;
1415
import java.util.Map.Entry;
1516
import java.util.Objects;
@@ -100,17 +101,40 @@ public Context delete(Object key) {
100101
/**
101102
* Returns request headers.
102103
*
103-
* @return headers, or {@code null} if not set
104+
* @return headers, or empty map if not set
104105
*/
105106
public Map<String, String> headers() {
106107
return source.getOrDefault(HEADERS_KEY, Collections.emptyMap());
107108
}
108109

110+
/**
111+
* Returns typed access to all request headers.
112+
*
113+
* @return typed parameters for headers
114+
*/
115+
public TypedParameters headerParams() {
116+
return new TypedParameters(headers());
117+
}
118+
119+
/**
120+
* Returns typed access to request headers filtered by the given prefix. Headers matching
121+
* "{prefix}." are included with the prefix stripped from the key.
122+
*
123+
* @param prefix header prefix to filter by (if null or empty, returns all headers)
124+
* @return typed parameters for filtered headers
125+
*/
126+
public TypedParameters headerParams(String prefix) {
127+
if (prefix == null || prefix.isEmpty()) {
128+
return headerParams();
129+
}
130+
return new TypedParameters(filterByPrefix(headers(), prefix));
131+
}
132+
109133
/**
110134
* Puts request headers to the context.
111135
*
112136
* @param headers headers
113-
* @return new {@code RequestContext} instance with updated headers
137+
* @return new {@code RequestContext}
114138
*/
115139
public RequestContext headers(Map<String, String> headers) {
116140
return put(HEADERS_KEY, headers);
@@ -129,7 +153,7 @@ public Object request() {
129153
* Puts request to the context.
130154
*
131155
* @param request request
132-
* @return new {@code RequestContext} instance with updated request
156+
* @return new {@code RequestContext}
133157
*/
134158
public RequestContext request(Object request) {
135159
return put(REQUEST_KEY, request);
@@ -185,7 +209,7 @@ public Principal principal() {
185209
* Puts principal to the context.
186210
*
187211
* @param principal principal
188-
* @return new {@code RequestContext} instance with the updated principal
212+
* @return new {@code RequestContext}
189213
*/
190214
public RequestContext principal(Principal principal) {
191215
return put(PRINCIPAL_KEY, principal);
@@ -224,7 +248,7 @@ public RequestContext methodInfo(MethodInfo methodInfo) {
224248
/**
225249
* Returns path parameters associated with the request.
226250
*
227-
* @return path parameters, or {@code null} if not set
251+
* @return path parameters, or empty map if not set
228252
*/
229253
public TypedParameters pathParams() {
230254
return new TypedParameters(source.getOrDefault(PATH_PARAMS_KEY, Collections.emptyMap()));
@@ -233,7 +257,7 @@ public TypedParameters pathParams() {
233257
/**
234258
* Puts path parameters associated with the request.
235259
*
236-
* @return path parameters, or {@code null} if not set
260+
* @return new {@code RequestContext}
237261
*/
238262
public RequestContext pathParams(Map<String, String> pathParams) {
239263
return put(PATH_PARAMS_KEY, pathParams);
@@ -310,6 +334,21 @@ public static Mono<RequestContext> deferSecured() {
310334
});
311335
}
312336

337+
private static Map<String, String> filterByPrefix(Map<String, String> map, String prefix) {
338+
if (map == null || map.isEmpty()) {
339+
return Map.of();
340+
}
341+
final var finalPrefix = prefix + ".";
342+
final var result = new HashMap<String, String>();
343+
map.forEach(
344+
(k, v) -> {
345+
if (k.startsWith(finalPrefix)) {
346+
result.put(k.substring(finalPrefix.length()), v);
347+
}
348+
});
349+
return result;
350+
}
351+
313352
@Override
314353
public String toString() {
315354
return new StringJoiner(", ", RequestContext.class.getSimpleName() + "[", "]")

services-gateway/src/test/java/io/scalecube/services/gateway/rest/RestGatewayTest.java

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -275,6 +275,29 @@ void testTrace() {
275275
})
276276
.verifyComplete();
277277
}
278+
279+
@Test
280+
void testAttributesPropagation() {
281+
StepVerifier.create(
282+
serviceCall.requestOne(
283+
ServiceMessage.builder()
284+
.header("http.method", "GET")
285+
.header("http.header.X-String-Header", "abc")
286+
.header("http.header.X-Int-Header", "123456789")
287+
.header("http.query.debug", "true")
288+
.header("http.query.x", "1")
289+
.header("http.query.y", "2")
290+
.qualifier("v1/restService/propagate/123/bar456/baz789")
291+
.build(),
292+
SomeResponse.class))
293+
.assertNext(
294+
message -> {
295+
final var someResponse = message.<SomeResponse>data();
296+
assertNotNull(someResponse, "data");
297+
assertNotNull(someResponse.name(), "someResponse.name");
298+
})
299+
.verifyComplete();
300+
}
278301
}
279302

280303
@Nested

services-gateway/src/test/java/io/scalecube/services/gateway/rest/RestService.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package io.scalecube.services.gateway.rest;
22

3+
import io.scalecube.services.annotations.RestMethod;
34
import io.scalecube.services.annotations.Service;
45
import io.scalecube.services.annotations.ServiceMethod;
56
import reactor.core.publisher.Mono;
@@ -30,4 +31,8 @@ public interface RestService {
3031

3132
@ServiceMethod("trace/:foo")
3233
Mono<SomeResponse> trace();
34+
35+
@RestMethod("GET")
36+
@ServiceMethod("propagate/:foo/:bar/:baz")
37+
Mono<SomeResponse> propagateRequestAttributes();
3338
}

services-gateway/src/test/java/io/scalecube/services/gateway/rest/RestServiceImpl.java

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import static org.junit.jupiter.api.Assertions.assertNotNull;
99

1010
import io.scalecube.services.RequestContext;
11+
import java.util.UUID;
1112
import reactor.core.publisher.Mono;
1213

1314
public class RestServiceImpl implements RestService {
@@ -142,4 +143,35 @@ public Mono<SomeResponse> trace() {
142143
return new SomeResponse().name(foo);
143144
});
144145
}
146+
147+
@Override
148+
public Mono<SomeResponse> propagateRequestAttributes() {
149+
return RequestContext.deferContextual()
150+
.map(
151+
context -> {
152+
final var pathParams = context.pathParams();
153+
assertEquals(123, pathParams.intValue("foo"), "foo");
154+
assertEquals("bar456", pathParams.stringValue("bar"), "bar");
155+
assertEquals("baz789", pathParams.stringValue("baz"), "baz");
156+
157+
final var headers = context.headers();
158+
assertEquals("GET", headers.get("http.method"));
159+
assertEquals("abc", headers.get("http.header.X-String-Header"));
160+
assertEquals("123456789", headers.get("http.header.X-Int-Header"));
161+
assertEquals("true", headers.get("http.query.debug"));
162+
assertEquals("1", headers.get("http.query.x"));
163+
assertEquals("2", headers.get("http.query.y"));
164+
165+
final var httpHeaders = context.headerParams("http.header");
166+
assertEquals("abc", httpHeaders.stringValue("X-String-Header"));
167+
assertEquals(123456789, httpHeaders.intValue("X-Int-Header"));
168+
169+
final var queryParams = context.headerParams("http.query");
170+
assertEquals(true, queryParams.booleanValue("debug"));
171+
assertEquals(1, queryParams.intValue("x"));
172+
assertEquals(2, queryParams.intValue("y"));
173+
174+
return new SomeResponse().name(UUID.randomUUID().toString());
175+
});
176+
}
145177
}

0 commit comments

Comments
 (0)