Skip to content

Commit 3787ff1

Browse files
chore: add support for new twilio error response (#898)
1 parent fc8f4ba commit 3787ff1

File tree

4 files changed

+188
-29
lines changed

4 files changed

+188
-29
lines changed

src/Twilio/ApiV1Version.php

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
<?php
2+
3+
namespace Twilio;
4+
5+
use Twilio\Exceptions\RestException;
6+
use Twilio\Exceptions\RestExceptionV1;
7+
use Twilio\Exceptions\TwilioException;
8+
use Twilio\Http\Response;
9+
10+
class ApiV1Version extends Version {
11+
12+
protected $apiVersion;
13+
14+
/**
15+
* @param Domain $domain
16+
* @param ?string $version
17+
*/
18+
public function __construct(Domain $domain, ?string $version) {
19+
parent::__construct($domain);
20+
$this->version = $version;
21+
$this->apiVersion = "V1";
22+
}
23+
24+
/**
25+
* Create the best possible exception for the response as per Twilio API Standard V1.
26+
*
27+
* Attempts to parse the response for Twilio Standard error as defined in Twilio API Standards V1
28+
* and use those to populate the exception, falls back to generic error message and
29+
* HTTP status code.
30+
*
31+
* @param Response $response Error response
32+
* @param string $header Header for exception message
33+
* @return TwilioException
34+
*/
35+
protected function exception(Response $response, string $header): TwilioException {
36+
$message = '[HTTP ' . $response->getStatusCode() . '] ' . $header;
37+
38+
$content = $response->getContent();
39+
if (\is_array($content)) {
40+
$message .= isset($content['message']) ? ': ' . $content['message'] : '';
41+
$code = $content['code'] ?? $response->getStatusCode();
42+
$httpStatusCode = $content['httpStatusCode'] ?? $response->getStatusCode();
43+
$params = $content['params'] ?? [];
44+
$userError = $content['userError'] ?? false;
45+
return new RestExceptionV1($code, $message, $httpStatusCode, $params, $userError);
46+
}
47+
48+
return new RestExceptionV1($response->getStatusCode(), $message, $response->getStatusCode());
49+
}
50+
}
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
<?php
2+
3+
4+
namespace Twilio\Exceptions;
5+
6+
7+
class RestExceptionV1 extends TwilioException {
8+
protected $code;
9+
protected $message;
10+
protected $httpStatusCode;
11+
protected $params;
12+
protected $userError;
13+
14+
/**
15+
* Construct the exception.
16+
* @param int $code A unique error code.
17+
* @param string $message A human-readable error message.
18+
* @param int $httpStatusCode The HTTP status code.
19+
* @param array $params [optional] More information about the error.
20+
* @param bool $userError [optional] true if it is an error that depends on the end users actions
21+
*/
22+
public function __construct(int $code, string $message, int $httpStatusCode, array $params = [], bool $userError = false) {
23+
$this->code = $code;
24+
$this->message = $message;
25+
$this->httpStatusCode = $httpStatusCode;
26+
$this->params = $params;
27+
$this->userError = $userError;
28+
parent::__construct($message, $code);
29+
}
30+
31+
/**
32+
* Get the HTTP Status Code of the RestException
33+
* @return int HTTP Status Code
34+
*/
35+
public function getHttpStatusCode(): int {
36+
return $this->httpStatusCode;
37+
}
38+
39+
/**
40+
* Get more information to additional information about the error
41+
* @return array additional information about the error
42+
*/
43+
public function getParams(): array {
44+
return $this->params;
45+
}
46+
47+
/**
48+
* Get the user error flag of the RestException
49+
* @return bool user error flag
50+
*/
51+
public function getUserError(): bool {
52+
return $this->userError;
53+
}
54+
}

src/Twilio/Version.php

Lines changed: 38 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ abstract class Version {
2020
/**
2121
* @var string $version
2222
*/
23-
protected $version;
23+
public $version;
2424

2525
/**
2626
* @param Domain $domain
@@ -82,7 +82,7 @@ protected function exception(Response $response, string $header): TwilioExceptio
8282
$content = $response->getContent();
8383
if (\is_array($content)) {
8484
$message .= isset($content['message']) ? ': ' . $content['message'] : '';
85-
$code = isset($content['code']) ? $content['code'] : $response->getStatusCode();
85+
$code = $content['code'] ?? $response->getStatusCode();
8686
$moreInfo = $content['more_info'] ?? '';
8787
$details = $content['details'] ?? [];
8888
return new RestException($message, $code, $response->getStatusCode(), $moreInfo, $details);
@@ -98,22 +98,17 @@ public function fetch(string $method, string $uri,
9898
array $params = [], array $data = [], array $headers = [],
9999
?string $username = null, ?string $password = null,
100100
?int $timeout = null) {
101-
$response = $this->request(
101+
$response = $this->handleException(
102102
$method,
103103
$uri,
104104
$params,
105105
$data,
106106
$headers,
107107
$username,
108108
$password,
109-
$timeout
109+
$timeout,
110+
"fetch"
110111
);
111-
112-
// 3XX response codes are allowed here to allow for 307 redirect from Deactivations API.
113-
if ($response->getStatusCode() < 200 || $response->getStatusCode() >= 400) {
114-
throw $this->exception($response, 'Unable to fetch record');
115-
}
116-
117112
return $response->getContent();
118113
}
119114

@@ -134,21 +129,17 @@ public function update(string $method, string $uri,
134129
array $params = [], array $data = [], array $headers = [],
135130
?string $username = null, ?string $password = null,
136131
?int $timeout = null) {
137-
$response = $this->request(
132+
$response = $this->handleException(
138133
$method,
139134
$uri,
140135
$params,
141136
$data,
142137
$headers,
143138
$username,
144139
$password,
145-
$timeout
140+
$timeout,
141+
"update"
146142
);
147-
148-
if ($response->getStatusCode() < 200 || $response->getStatusCode() >= 300) {
149-
throw $this->exception($response, 'Unable to update record');
150-
}
151-
152143
return $response->getContent();
153144
}
154145

@@ -159,23 +150,18 @@ public function delete(string $method, string $uri,
159150
array $params = [], array $data = [], array $headers = [],
160151
?string $username = null, ?string $password = null,
161152
?int $timeout = null): bool {
162-
$response = $this->request(
153+
$this->handleException(
163154
$method,
164155
$uri,
165156
$params,
166157
$data,
167158
$headers,
168159
$username,
169160
$password,
170-
$timeout
161+
$timeout,
162+
"delete"
171163
);
172-
173-
// check for 2xx status code is already present here
174-
if ($response->getStatusCode() < 200 || $response->getStatusCode() >= 300) {
175-
throw $this->exception($response, 'Unable to delete record');
176-
}
177-
178-
return true; // if response code is 2XX, deletion was successful
164+
return true;
179165
}
180166

181167
public function readLimits(?int $limit = null, ?int $pageSize = null): array {
@@ -219,6 +205,28 @@ public function create(string $method, string $uri,
219205
array $params = [], array $data = [], array $headers = [],
220206
?string $username = null, ?string $password = null,
221207
?int $timeout = null) {
208+
$response = $this->handleException(
209+
$method,
210+
$uri,
211+
$params,
212+
$data,
213+
$headers,
214+
$username,
215+
$password,
216+
$timeout,
217+
"create"
218+
);
219+
return $response->getContent();
220+
}
221+
222+
/**
223+
* @throws TwilioException
224+
*/
225+
public function handleException(string $method, string $uri,
226+
array $params = [], array $data = [], array $headers = [],
227+
?string $username = null, ?string $password = null,
228+
?int $timeout = null, ?string $operation = ""): Response
229+
{
222230
$response = $this->request(
223231
$method,
224232
$uri,
@@ -230,11 +238,12 @@ public function create(string $method, string $uri,
230238
$timeout
231239
);
232240

233-
if ($response->getStatusCode() < 200 || $response->getStatusCode() >= 300) {
234-
throw $this->exception($response, 'Unable to create record');
241+
if ($response->getStatusCode() < 200 || $response->getStatusCode() >= 400) {
242+
$exceptionHeader = 'Unable to ' . $operation . ' record';
243+
throw $this->exception($response, $exceptionHeader);
235244
}
236245

237-
return $response->getContent();
246+
return $response;
238247
}
239248

240249
public function getDomain(): Domain {

tests/Twilio/Unit/VersionTest.php

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,11 @@
33

44
namespace Twilio\Tests\Unit;
55

6+
use Twilio\ApiV1Version;
67
use Twilio\Domain;
78
use Twilio\Exceptions\RestException;
9+
use Twilio\Exceptions\RestExceptionV1;
10+
use Twilio\Exceptions\TwilioException;
811
use Twilio\Http\CurlClient;
912
use Twilio\Http\Response;
1013
use Twilio\Page;
@@ -440,6 +443,49 @@ public function testRestException(): void {
440443
}
441444
}
442445

446+
public function testRestExceptionV1(): void {
447+
$this->curlClient
448+
->expects(self::once())
449+
->method('request')
450+
->willReturn(new Response(404, '{
451+
"code": 20404,
452+
"message": "The requested resource was not found",
453+
"httpStatusCode": 404,
454+
"userError": true,
455+
"params": {
456+
"twilioErrorCodeUrl": "https://www.twilio.com/docs/errors/20404" }
457+
}'));
458+
try {
459+
$this->version = new ApiV1Version($this->version->getDomain(), $this->version->version);
460+
$this->version->fetch('get', 'http://foo.bar');
461+
self::fail();
462+
}catch (RestExceptionV1 $rex){
463+
self::assertEquals(20404, $rex->getCode());
464+
self::assertEquals(404, $rex->getHttpStatusCode());
465+
self::assertEquals('[HTTP 404] Unable to fetch record: The requested resource was not found', $rex->getMessage());
466+
self::assertEquals(['twilioErrorCodeUrl' => 'https://www.twilio.com/docs/errors/20404'], $rex->getParams());
467+
self::assertTrue($rex->getUserError());
468+
}
469+
}
470+
471+
public function testRestExceptionV1WithoutParams(): void {
472+
$this->curlClient
473+
->expects(self::once())
474+
->method('request')
475+
->willReturn(new Response(400, ''));
476+
try {
477+
$this->version = new ApiV1Version($this->version->getDomain(), $this->version->version);
478+
$this->version->fetch('get', 'http://foo.bar');
479+
self::fail();
480+
}catch (RestExceptionV1 $rex){
481+
self::assertEquals(400, $rex->getCode());
482+
self::assertEquals(400, $rex->getHttpStatusCode());
483+
self::assertEquals('[HTTP 400] Unable to fetch record', $rex->getMessage());
484+
self::assertEmpty($rex->getParams());
485+
self::assertFalse($rex->getUserError());
486+
}
487+
}
488+
443489
public function absoluteUrlProvider(): array {
444490
$cases = $this->relativeUriProvider();
445491
foreach ($cases as &$case) {

0 commit comments

Comments
 (0)