Skip to content
This repository was archived by the owner on Jan 17, 2023. It is now read-only.

Commit 53461bc

Browse files
committed
Merge pull request #3 from Stackla/SPS-1
SPS-1 Created ApiException to handle API errors
2 parents ba4e542 + eef59a2 commit 53461bc

File tree

5 files changed

+273
-34
lines changed

5 files changed

+273
-34
lines changed

README.md

+3-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ Welcome to the Stackla PHP SDK GitHub repository!
33

44
This SDK is intended for PHP developers to enable them to integrate Stackla RESTful APIs into their applications with greater ease. You can find more information on working with Stackla APIs on the [Stackla Developer Portal](http://developer.stackla.com/).
55

6-
We are continously working on improving both the SDK and the documentation around it, so please feel free to give us feedback and any reports of issues to [[email protected]](mailto:[email protected]).
6+
We are continuously working on improving both the SDK and the documentation around it, so please feel free to give us feedback and any reports of issues to [[email protected]](mailto:[email protected]).
77

88
## System requirements ##
99
* PHP >=5.3.3
@@ -488,3 +488,5 @@ delete*** | - | - | - | widget object / self or false
488488
* _delete_ will not work if the widget has child widget.
489489

490490
----------------
491+
492+
[Error Handling](doc/error_handling.md)

doc/error_handling.md

+33
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
## Error Handling ##
2+
3+
An `Stackla\Exception\ApiException` is thrown when there are errors.
4+
5+
```
6+
try {
7+
// do something with SDK that could cause errors
8+
} catch (\Stackla\Exception\ApiException $exception) {
9+
$httpStatusCode = $exception->getHttpStatusCode();
10+
$httpResponseBody = $exception->getHttpResponseBody();
11+
12+
// an array of Stackla\Exception\ApiError instances
13+
$errors = $exception->getErrors();
14+
foreach ($errors as $error) {
15+
$message = $error->getMessage(); // human readable message
16+
$errorCode = $error->getErrorCode(); // unique error code
17+
$messageId = $error->getMessageId(); // unique error message ID
18+
}
19+
20+
// some helper functions
21+
// check if a certain error exists by error code
22+
if ($exception->containsErrorByErrorCode(1070409)) {
23+
// tag name conflict error
24+
echo 'Tag name must be unique';
25+
}
26+
27+
// check if a certain error exists by message ID
28+
if ($exception->containsErrorByMessageId('term:not_found')) {
29+
// the given term cannot be found
30+
echo 'Term cannot be found';
31+
}
32+
}
33+
```

src/Stackla/Core/Request.php

+44-33
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,11 @@
22

33
namespace Stackla\Core;
44

5-
use \Stackla\Core\Credentials;
6-
use \Guzzle\Http\Client;
7-
use \Guzzle\Http\Exception\ClientErrorResponseException;
8-
use \Guzzle\Http\Exception\BadResponseException;
9-
use \Guzzle\Http\Exception\CurlException;
5+
use Guzzle\Http\EntityBodyInterface;
6+
use Guzzle\Http\Client;
7+
use Guzzle\Http\Exception\ClientErrorResponseException;
8+
use Guzzle\Http\Exception\BadResponseException;
9+
use Stackla\Exception\ApiException;
1010

1111
class Request implements RequestInterface
1212
{
@@ -30,13 +30,13 @@ class Request implements RequestInterface
3030

3131
/**
3232
* Response result placeholder
33-
* @var \Guzzle\Message\Response
33+
* @var \Guzzle\Http\Message\Response
3434
*/
3535
protected $response;
3636

3737
/**
3838
* Request placeholder
39-
* @var \Guzzle\Message\Request
39+
* @var \Guzzle\Http\Message\Request
4040
*/
4141
protected $request;
4242

@@ -48,9 +48,11 @@ class Request implements RequestInterface
4848

4949
private $querySeparator = '&';
5050

51+
protected $apiKey;
52+
5153
/**
5254
*
53-
* @var Guzzle\Http\Message\Response
55+
* @var \Guzzle\Http\Message\Response
5456
*/
5557
private $client;
5658

@@ -60,7 +62,7 @@ public function __construct(\Stackla\Core\Credentials $credentials, $host, $stac
6062
$this->stack = $stack;
6163
$this->credentials = $credentials;
6264
$this->client = new Client();
63-
if (class_exists("\Monolog\Logger")) {
65+
if (class_exists("\\Monolog\\Logger")) {
6466
$this->logger = new \Monolog\Logger(get_class($this));
6567
$this->logger->pushHandler(new \Monolog\Handler\StreamHandler(sys_get_temp_dir() . DIRECTORY_SEPARATOR . 'stackla-request.log', \Monolog\Logger::INFO));
6668
}
@@ -169,13 +171,12 @@ private function buildOptions(array $data = array())
169171
/**
170172
* Making request using Guzzle
171173
*
172-
* @param string $method Type of request, iether POST, GET, PUT or DELETE
173-
* @param string $endpoint The operation / task for API
174-
* @param array $data The parameter need to be passed
175-
* @param array $options The options like header, body, etc
176-
*
177-
* @return \Guzzle\Http\Message\Response This will return FALSE if something wrong happened
178-
* or return json object
174+
* @param string $method Type of request, either POST, GET, PUT or DELETE
175+
* @param string $endpoint The operation / task for API
176+
* @param array $data The parameter need to be passed
177+
* @param array $options The options like header, body, etc
178+
* @return EntityBodyInterface|string
179+
* @throws \Exception
179180
*/
180181
private function sendRequest($method, $endpoint, array $data = array(), array $options = array())
181182
{
@@ -242,30 +243,40 @@ private function sendRequest($method, $endpoint, array $data = array(), array $o
242243
}
243244
}
244245

245-
switch ($this->response->getStatusCode()) {
246+
$statusCode = $this->response->getStatusCode();
247+
switch ($statusCode) {
246248
case 200:
247249
return $this->response->getBody(true);
248-
break;
249250
case 400:
250-
throw new \Exception(sprintf("Server return %s error code. Bad request: The request could not be understood. %s", $this->response->getStatusCode(), $this->response->getBody(true)));
251-
return false;
252-
break;
251+
throw ApiException::create(
252+
sprintf("Server return %s error code. Bad request: The request could not be understood. %s", $this->response->getStatusCode(), $this->response->getBody(true)),
253+
$statusCode,
254+
$this->response->getBody(true)
255+
);
253256
case 401:
254-
throw new \Exception(sprintf("Server return %s error code. Unauthorized: Authentication credentials invalid or not authorised to access resource", $this->response->getStatusCode()));
255-
return false;
256-
break;
257+
throw ApiException::create(
258+
sprintf("Server return %s error code. Unauthorized: Authentication credentials invalid or not authorised to access resource", $this->response->getStatusCode()),
259+
$statusCode,
260+
$this->response->getBody(true)
261+
);
257262
case 403:
258-
throw new \Exception(sprintf("Server return %s error code. Rate limit exceeded: Too many requests in the current time window", $this->response->getStatusCode()));
259-
return false;
260-
break;
263+
throw ApiException::create(
264+
sprintf("Server return %s error code. Rate limit exceeded: Too many requests in the current time window", $this->response->getStatusCode()),
265+
$statusCode,
266+
$this->response->getBody(true)
267+
);
261268
case 404:
262-
throw new \Exception(sprintf("Server return %s error code. Invalid resource: Invalid resource specified or resource not found", $this->response->getStatusCode()));
263-
return false;
264-
break;
269+
throw ApiException::create(
270+
sprintf("Server return %s error code. Invalid resource: Invalid resource specified or resource not found", $this->response->getStatusCode()),
271+
$statusCode,
272+
$this->response->getBody(true)
273+
);
265274
default:
266-
throw new \Exception(sprintf("Server return %s error code.Server error: An error on the server prohibited a successful response; please contact support. %s", $this->response->getStatusCode(), $this->response->getBody(true)));
267-
return false;
268-
break;
275+
throw ApiException::create(
276+
sprintf("Server return %s error code.Server error: An error on the server prohibited a successful response; please contact support. %s", $this->response->getStatusCode(), $this->response->getBody(true)),
277+
$statusCode,
278+
$this->response->getBody(true)
279+
);
269280
}
270281

271282
}

src/Stackla/Exception/ApiError.php

+75
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
<?php
2+
3+
namespace Stackla\Exception;
4+
5+
class ApiError
6+
{
7+
/**
8+
* @var string
9+
*/
10+
protected $message;
11+
12+
/**
13+
* @var int
14+
*/
15+
protected $code;
16+
17+
/**
18+
* @var string
19+
*/
20+
protected $messageId;
21+
22+
/**
23+
* @var int
24+
*/
25+
protected $errorCode;
26+
27+
/**
28+
* @param string $message
29+
* @param int $code
30+
* @param string $messageId
31+
* @param int $errorCode
32+
*/
33+
public function __construct($message, $code, $messageId, $errorCode)
34+
{
35+
$this->message = $message;
36+
$this->code = $code;
37+
$this->messageId = $messageId;
38+
$this->errorCode = $errorCode;
39+
}
40+
41+
/**
42+
* @return string
43+
*/
44+
public function getMessage()
45+
{
46+
return $this->message;
47+
}
48+
49+
/**
50+
* @deprecated Please use getErrorCode() instead
51+
* @see getErrorCode()
52+
* @return int
53+
*/
54+
public function getCode()
55+
{
56+
return $this->code;
57+
}
58+
59+
/**
60+
* @return string
61+
*/
62+
public function getMessageId()
63+
{
64+
return $this->messageId;
65+
}
66+
67+
/**
68+
* @return int
69+
*/
70+
public function getErrorCode()
71+
{
72+
return $this->errorCode;
73+
}
74+
75+
}
+118
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
<?php
2+
3+
namespace Stackla\Exception;
4+
5+
use Exception;
6+
7+
class ApiException extends Exception
8+
{
9+
10+
/**
11+
* @var int
12+
*/
13+
protected $httpStatusCode;
14+
15+
/**
16+
* @var string
17+
*/
18+
protected $httpResponseBody;
19+
20+
/**
21+
* @var ApiError[]
22+
*/
23+
protected $_errors;
24+
25+
public static function create($message, $httpStatusCode, $httpResponseBody, Exception $previous = null)
26+
{
27+
$exception = new static($message, 0, $previous);
28+
$exception->httpStatusCode = $httpStatusCode;
29+
$exception->httpResponseBody = $httpResponseBody;
30+
return $exception;
31+
}
32+
33+
/**
34+
* @return int
35+
*/
36+
public function getHttpStatusCode()
37+
{
38+
return $this->httpStatusCode;
39+
}
40+
41+
/**
42+
* @return string
43+
*/
44+
public function getHttpResponseBody()
45+
{
46+
return $this->httpResponseBody;
47+
}
48+
49+
/**
50+
* @return ApiError[]
51+
*/
52+
public function getErrors()
53+
{
54+
if (!is_array($this->_errors)) {
55+
$errors = array();
56+
$json = $this->getHttpResponseBody();
57+
$response = json_decode($json, true);
58+
if (json_last_error() === JSON_ERROR_NONE) {
59+
$errors = $this->ifx($response, 'errors');
60+
if (is_array($errors)) {
61+
foreach ($errors as $error) {
62+
if (is_array($error)) {
63+
$errors[] = new ApiError(
64+
(string)$this->ifx($error, 'message'),
65+
(string)$this->ifx($error, 'code'),
66+
(string)$this->ifx($error, 'message_id'),
67+
(int)$this->ifx($error, 'error_code')
68+
);
69+
}
70+
}
71+
}
72+
}
73+
$this->_errors = $errors;
74+
}
75+
return $this->_errors;
76+
}
77+
78+
/**
79+
* @param int $errorCode
80+
* @return bool
81+
*/
82+
public function containsErrorByErrorCode($errorCode)
83+
{
84+
$errors = $this->getErrors();
85+
foreach ($errors as $error) {
86+
if ($error->getErrorCode() === $errorCode) {
87+
return true;
88+
}
89+
}
90+
return false;
91+
}
92+
93+
/**
94+
* @param string $messageId
95+
* @return bool
96+
*/
97+
public function containsErrorByMessageId($messageId)
98+
{
99+
$errors = $this->getErrors();
100+
foreach ($errors as $error) {
101+
if ($error->getMessageId() === $messageId) {
102+
return true;
103+
}
104+
}
105+
return false;
106+
}
107+
108+
final protected function ifx($arr, $key, $default = null)
109+
{
110+
if (!is_array($arr)) {
111+
return $default;
112+
} elseif (array_key_exists($key, $arr)) {
113+
return $arr[$key];
114+
} else {
115+
return $default;
116+
}
117+
}
118+
}

0 commit comments

Comments
 (0)