44
55namespace WordPress \AiClient \Providers \Http \Exception ;
66
7+ use Psr \Http \Message \RequestInterface ;
8+ use WordPress \AiClient \Common \Exception \InvalidArgumentException ;
9+ use WordPress \AiClient \Providers \Http \DTO \Request ;
710use WordPress \AiClient \Providers \Http \DTO \Response ;
11+ use WordPress \AiClient \Providers \Http \Utilities \ErrorMessageExtractor ;
812
913/**
1014 * Exception thrown for 4xx HTTP client errors.
1418 *
1519 * @since n.e.x.t
1620 */
17- class ClientException extends RequestException
21+ class ClientException extends InvalidArgumentException
1822{
23+ /**
24+ * The request that failed.
25+ *
26+ * @var Request|null
27+ */
28+ protected ?Request $ request = null ;
29+
30+ /**
31+ * Returns the request that failed as our Request DTO.
32+ *
33+ * @since n.e.x.t
34+ *
35+ * @return Request
36+ * @throws \RuntimeException If no request is available
37+ */
38+ public function getRequest (): Request
39+ {
40+ if ($ this ->request === null ) {
41+ throw new \RuntimeException (
42+ 'Request object not available. This exception was directly instantiated. ' .
43+ 'Use a factory method that provides request context. '
44+ );
45+ }
46+
47+ return $ this ->request ;
48+ }
49+
1950 /**
2051 * Creates a ClientException from a 400 Bad Request response.
2152 *
@@ -30,6 +61,43 @@ public static function fromBadRequestResponse(string $errorDetail = 'Invalid req
3061 return new self ($ message , 400 );
3162 }
3263
64+ /**
65+ * Creates a ClientException from a bad request.
66+ *
67+ * @since n.e.x.t
68+ *
69+ * @param RequestInterface $psrRequest The PSR-7 request that failed.
70+ * @param string $errorDetail Details about what made the request bad.
71+ * @return self
72+ */
73+ public static function fromBadRequest (
74+ RequestInterface $ psrRequest ,
75+ string $ errorDetail = 'Invalid request parameters '
76+ ): self {
77+ $ request = Request::fromPsrRequest ($ psrRequest );
78+ $ message = sprintf ('Bad request to %s (400): %s ' , $ request ->getUri (), $ errorDetail );
79+
80+ $ exception = new self ($ message , 400 );
81+ $ exception ->request = $ request ;
82+ return $ exception ;
83+ }
84+
85+ /**
86+ * Creates a ClientException from a bad request to a specific URI.
87+ *
88+ * @since n.e.x.t
89+ *
90+ * @param string $uri The URI that was requested.
91+ * @param string $errorDetail Details about what made the request bad.
92+ * @return self
93+ *
94+ * @deprecated Use fromBadRequest() with RequestInterface for better type safety
95+ */
96+ public static function fromBadRequestToUri (string $ uri , string $ errorDetail = 'Invalid request parameters ' ): self
97+ {
98+ return new self (sprintf ('Bad request to %s (400): %s ' , $ uri , $ errorDetail ), 400 );
99+ }
100+
33101 /**
34102 * Creates a ClientException from a client error response (4xx).
35103 *
@@ -48,28 +116,10 @@ public static function fromClientError(Response $response): self
48116 $ response ->getStatusCode ()
49117 );
50118
51- // Handle common error formats in API responses
52- $ data = $ response ->getData ();
53- if (
54- is_array ($ data ) &&
55- isset ($ data ['error ' ]) &&
56- is_array ($ data ['error ' ]) &&
57- isset ($ data ['error ' ]['message ' ]) &&
58- is_string ($ data ['error ' ]['message ' ])
59- ) {
60- $ errorMessage .= ' - ' . $ data ['error ' ]['message ' ];
61- } elseif (
62- is_array ($ data ) &&
63- isset ($ data ['error ' ]) &&
64- is_string ($ data ['error ' ])
65- ) {
66- $ errorMessage .= ' - ' . $ data ['error ' ];
67- } elseif (
68- is_array ($ data ) &&
69- isset ($ data ['message ' ]) &&
70- is_string ($ data ['message ' ])
71- ) {
72- $ errorMessage .= ' - ' . $ data ['message ' ];
119+ // Extract error message from response data using centralized utility
120+ $ extractedError = ErrorMessageExtractor::extractFromResponseData ($ response ->getData ());
121+ if ($ extractedError !== null ) {
122+ $ errorMessage .= ' - ' . $ extractedError ;
73123 }
74124
75125 return new self ($ errorMessage , $ response ->getStatusCode ());
0 commit comments