11package com .retailsvc .http ;
22
3+ import com .retailsvc .http .spec .HttpMethod ;
34import java .net .URLDecoder ;
45import java .nio .charset .StandardCharsets ;
56import java .util .LinkedHashMap ;
910import java .util .function .UnaryOperator ;
1011
1112/**
12- * Read-only per-request handle passed to {@link RequestHandler}. Carries the parsed body, path
13- * parameters, query parameters, headers, and operation ID.
13+ * Read-only per-request handle passed to {@link RequestHandler}. Carries the HTTP method, parsed
14+ * body, path parameters, query parameters, headers, and operation ID.
1415 *
1516 * <p>{@code Request} is transport-neutral: it holds the body bytes, the raw query string, the path
1617 * parameter map, and a header lookup function. The transport adapter (today the built-in JDK {@code
@@ -28,6 +29,7 @@ public final class Request {
2829 private final String operationId ;
2930 private final Map <String , String > pathParameters ;
3031 private final String rawQuery ;
32+ private final HttpMethod method ;
3133 private final UnaryOperator <String > headerLookup ;
3234 private final Map <String , Object > principals ;
3335 private Map <String , String > queryParamCache ;
@@ -36,6 +38,8 @@ public final class Request {
3638 * Builds a {@code Request} from transport-neutral primitives. Adapters call this; handlers
3739 * receive the constructed instance.
3840 *
41+ * <p>{@code method} is {@code null}; use the 9-arg constructor to supply it.
42+ *
3943 * @param body raw request body bytes; never {@code null}, may be empty
4044 * @param parsed loose structural view of the body (Map / List / boxed primitive), or {@code null}
4145 * @param bodyMapper {@link TypeMapper} that produced {@code parsed}, used for typed conversion;
@@ -53,12 +57,23 @@ public Request(
5357 Map <String , String > pathParameters ,
5458 String rawQuery ,
5559 UnaryOperator <String > headerLookup ) {
56- this (body , parsed , bodyMapper , operationId , pathParameters , rawQuery , headerLookup , Map .of ());
60+ this (
61+ body ,
62+ parsed ,
63+ bodyMapper ,
64+ operationId ,
65+ pathParameters ,
66+ rawQuery ,
67+ headerLookup ,
68+ Map .of (),
69+ null );
5770 }
5871
5972 /**
6073 * Builds a {@code Request} from transport-neutral primitives with an explicit principals map.
6174 *
75+ * <p>{@code method} is {@code null}; use the 9-arg constructor to supply it.
76+ *
6277 * @param body raw request body bytes; never {@code null}, may be empty
6378 * @param parsed loose structural view of the body (Map / List / boxed primitive), or {@code null}
6479 * @param bodyMapper {@link TypeMapper} that produced {@code parsed}, used for typed conversion;
@@ -69,9 +84,47 @@ public Request(
6984 * @param headerLookup first-value, case-insensitive header lookup; returns {@code null} if absent
7085 * @param principals principals stashed by the security filter, keyed by scheme name
7186 */
87+ @ SuppressWarnings ("java:S107" )
88+ public Request (
89+ byte [] body ,
90+ Object parsed ,
91+ TypeMapper bodyMapper ,
92+ String operationId ,
93+ Map <String , String > pathParameters ,
94+ String rawQuery ,
95+ UnaryOperator <String > headerLookup ,
96+ Map <String , Object > principals ) {
97+ this (
98+ body ,
99+ parsed ,
100+ bodyMapper ,
101+ operationId ,
102+ pathParameters ,
103+ rawQuery ,
104+ headerLookup ,
105+ principals ,
106+ null );
107+ }
108+
109+ /**
110+ * Builds a {@code Request} from transport-neutral primitives with explicit principals and method.
111+ *
112+ * @param body raw request body bytes; never {@code null}, may be empty
113+ * @param parsed loose structural view of the body (Map / List / boxed primitive), or {@code null}
114+ * @param bodyMapper {@link TypeMapper} that produced {@code parsed}, used for typed conversion;
115+ * may be {@code null} if there is no body
116+ * @param operationId the OpenAPI {@code operationId} the request was routed to
117+ * @param pathParameters path variables extracted by the router
118+ * @param rawQuery raw (percent-encoded) query string, or {@code null} if absent
119+ * @param headerLookup first-value, case-insensitive header lookup; returns {@code null} if absent
120+ * @param principals principals stashed by the security filter, keyed by scheme name
121+ * @param method the HTTP method of the request. Never {@code null} when constructed through the
122+ * normal request pipeline. {@code null} only when constructed via the legacy 7- or 8-argument
123+ * constructors (kept for backward compatibility).
124+ */
72125 // Request is transport-neutral and assembled from primitives at the adapter boundary; collapsing
73126 // these into a holder type would just move the parameter count one level out without simplifying
74- // the call site, so the 8 -arg constructor is preferred over the rule's 7-param limit.
127+ // the call site, so the 9 -arg constructor is preferred over the rule's 7-param limit.
75128 @ SuppressWarnings ("java:S107" )
76129 public Request (
77130 byte [] body ,
@@ -81,13 +134,15 @@ public Request(
81134 Map <String , String > pathParameters ,
82135 String rawQuery ,
83136 UnaryOperator <String > headerLookup ,
84- Map <String , Object > principals ) {
137+ Map <String , Object > principals ,
138+ HttpMethod method ) {
85139 this .body = body ;
86140 this .parsed = parsed ;
87141 this .bodyMapper = bodyMapper ;
88142 this .operationId = operationId ;
89143 this .pathParameters = pathParameters ;
90144 this .rawQuery = rawQuery ;
145+ this .method = method ;
91146 this .headerLookup = headerLookup ;
92147 this .principals = Map .copyOf (principals );
93148 }
@@ -207,14 +262,31 @@ public Optional<Object> principal(String schemeName) {
207262 return Optional .ofNullable (principals .get (schemeName ));
208263 }
209264
265+ /**
266+ * HTTP method of the request. Never {@code null} for requests routed through the standard
267+ * pipeline; {@code null} only when the {@code Request} was constructed via a legacy constructor
268+ * without a method.
269+ */
270+ public HttpMethod method () {
271+ return method ;
272+ }
273+
210274 /**
211275 * Returns a new {@code Request} identical to this one except with the supplied principals. Used
212276 * by {@code SecurityFilter} on success; the returned instance carries the principals through to
213277 * the {@link RequestHandler}.
214278 */
215279 public Request withPrincipals (Map <String , Object > principals ) {
216280 return new Request (
217- body , parsed , bodyMapper , operationId , pathParameters , rawQuery , headerLookup , principals );
281+ body ,
282+ parsed ,
283+ bodyMapper ,
284+ operationId ,
285+ pathParameters ,
286+ rawQuery ,
287+ headerLookup ,
288+ principals ,
289+ method );
218290 }
219291
220292 private static Map <String , String > parseQuery (String query ) {
0 commit comments