1 package com.guinetik.rr.request;
2
3 import com.guinetik.rr.http.RocketHeaders;
4
5 import java.util.HashMap;
6 import java.util.Map;
7
8 /**
9 * Immutable specification of an HTTP request containing all parameters needed for execution.
10 *
11 * <p>A {@code RequestSpec} encapsulates the complete definition of an API request including
12 * the endpoint, HTTP method, query parameters, headers, request body, and expected response type.
13 * Instances are created using the {@link RequestBuilder} fluent API.
14 *
15 * <h2>Request Components</h2>
16 * <ul>
17 * <li><b>Endpoint</b> - The API path (relative or absolute URL)</li>
18 * <li><b>Method</b> - HTTP method (GET, POST, PUT, DELETE, etc.)</li>
19 * <li><b>Query Parameters</b> - URL query string parameters</li>
20 * <li><b>Headers</b> - HTTP request headers via {@link RocketHeaders}</li>
21 * <li><b>Body</b> - Request payload (for POST, PUT, PATCH)</li>
22 * <li><b>Response Type</b> - Expected Java class for response deserialization</li>
23 * </ul>
24 *
25 * <h2>Creating Request Specifications</h2>
26 * <pre class="language-java"><code>
27 * // Simple GET request
28 * RequestSpec<Void, User> getUser = RequestBuilder.<Void, User>get("/users/1")
29 * .responseType(User.class)
30 * .build();
31 *
32 * // POST request with body
33 * CreateUserRequest body = new CreateUserRequest("John", "john@example.com");
34 * RequestSpec<CreateUserRequest, User> createUser = RequestBuilder
35 * .<CreateUserRequest, User>post("/users")
36 * .body(body)
37 * .responseType(User.class)
38 * .build();
39 *
40 * // GET with query parameters
41 * Map<String, String> params = new HashMap<>();
42 * params.put("page", "1");
43 * params.put("limit", "20");
44 * RequestSpec<Void, UserList> listUsers = RequestBuilder.<Void, UserList>get("/users")
45 * .queryParams(params)
46 * .responseType(UserList.class)
47 * .build();
48 * </code></pre>
49 *
50 * <h2>Executing Requests</h2>
51 * <pre class="language-java"><code>
52 * // With synchronous client
53 * User user = client.sync().execute(getUser);
54 *
55 * // With async client
56 * CompletableFuture<User> future = client.async().execute(getUser);
57 *
58 * // With fluent client
59 * Result<User, ApiError> result = client.fluent().execute(getUser);
60 * </code></pre>
61 *
62 * @param <Req> the type of the request body (use {@code Void} for requests without body)
63 * @param <Res> the type of the expected response
64 * @author guinetik <guinetik@gmail.com>
65 * @see RequestBuilder
66 * @see RocketHeaders
67 * @since 1.0.0
68 */
69 public class RequestSpec<Req, Res> {
70 private final String endpoint;
71 private final String method;
72 private final Map<String, String> queryParams;
73 private final RocketHeaders headers;
74 private final Req body;
75 private final Class<Res> responseType;
76
77 public RequestSpec(String endpoint, String method, Map<String, String> queryParams,
78 RocketHeaders headers, Req body, Class<Res> responseType) {
79 this.endpoint = endpoint;
80 this.method = method;
81 this.queryParams = queryParams != null ? queryParams : new HashMap<>();
82 this.headers = headers != null ? headers : RocketHeaders.defaultJson();
83 this.body = body;
84 this.responseType = responseType;
85 }
86
87 public String getEndpoint() {
88 return endpoint;
89 }
90
91 public String getMethod() {
92 return method;
93 }
94
95 public Map<String, String> getQueryParams() {
96 return queryParams;
97 }
98
99 public RocketHeaders getHeaders() {
100 return headers;
101 }
102
103 public Req getBody() {
104 return body;
105 }
106
107 public Class<Res> getResponseType() {
108 return responseType;
109 }
110 }