1 package com.guinetik.rr.api;
2
3 import com.guinetik.rr.http.RocketRestException;
4
5 /**
6 * Exception thrown when an API request fails with rich error details.
7 *
8 * <p>This exception extends {@link RocketRestException} and provides additional
9 * context about API failures including the error message from the server.
10 * Use this when you need more details than the base exception provides.
11 *
12 * <h2>Exception Hierarchy</h2>
13 * <pre>
14 * RuntimeException
15 * └── RocketRestException (base HTTP exception)
16 * ├── CircuitBreakerOpenException
17 * ├── TokenExpiredException
18 * └── ApiException (richer error details)
19 * └── errorMessage: Server error message
20 * </pre>
21 *
22 * <h2>Handling ApiException</h2>
23 * <pre class="language-java">{@code
24 * try {
25 * User user = client.get("/users/999", User.class);
26 * } catch (ApiException e) {
27 * System.err.println("Status: " + e.getStatusCode());
28 * System.err.println("Message: " + e.getErrorMessage());
29 * System.err.println("Body: " + e.getResponseBody());
30 * } catch (RocketRestException e) {
31 * // Handles all other HTTP exceptions
32 * System.err.println("HTTP error: " + e.getStatusCode());
33 * }
34 * }</pre>
35 *
36 * <h2>Avoiding Exceptions with Result Pattern</h2>
37 * <p>Consider using the fluent API with {@link com.guinetik.rr.result.Result} to avoid exceptions:
38 * <pre class="language-java">{@code
39 * Result<User, ApiError> result = client.fluent().get("/users/999", User.class);
40 * result.match(
41 * user -> handleSuccess(user),
42 * error -> handleError(error) // No exception thrown
43 * );
44 * }</pre>
45 *
46 * @author guinetik <guinetik@gmail.com>
47 * @see RocketRestException
48 * @see com.guinetik.rr.result.Result
49 * @see com.guinetik.rr.result.ApiError
50 * @since 1.0.0
51 */
52 public class ApiException extends RocketRestException {
53
54 private static final long serialVersionUID = 1L;
55
56 private final String errorMessage;
57
58 /**
59 * Creates a new ApiException with full error details.
60 *
61 * @param message The exception message
62 * @param responseBody The raw response body from the server
63 * @param errorMessage The parsed error message from the server
64 * @param statusCode The HTTP status code
65 */
66 public ApiException(String message, String responseBody, String errorMessage, int statusCode) {
67 super(message, statusCode, responseBody);
68 this.errorMessage = errorMessage;
69 }
70
71 /**
72 * Creates a new ApiException with just a message.
73 *
74 * @param message The exception message
75 */
76 public ApiException(String message) {
77 super(message);
78 this.errorMessage = null;
79 }
80
81 /**
82 * Creates a new ApiException with a message and cause.
83 *
84 * @param message The exception message
85 * @param cause The underlying cause
86 */
87 public ApiException(String message, Throwable cause) {
88 super(message, cause);
89 this.errorMessage = null;
90 }
91
92 /**
93 * Gets the parsed error message from the server response.
94 * This is often a more user-friendly message extracted from the response body.
95 *
96 * @return The server error message, or null if not available
97 */
98 public String getErrorMessage() {
99 return errorMessage;
100 }
101 }