View Javadoc
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 &lt;guinetik@gmail.com&gt;
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 }