Skip to content

Commit c9b33cd

Browse files
Art4kbsali
andauthored
Improve exceptions (#300)
Co-authored-by: Kevin Saliou <[email protected]>
1 parent e464dc9 commit c9b33cd

35 files changed

+276
-78
lines changed

CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased](https://github.com/kbsali/php-redmine-api/compare/v2.0.1...v2.x)
99

10+
### Added
11+
12+
- New interface `Redmine\Exception` that is implemented by every library-related exception
13+
- New exception `Redmine\Exception\ClientException` for client related exceptions
14+
- New exception `Redmine\Exception\InvalidApiNameException` if an invalid API instance is requested
15+
- New exception `Redmine\Exception\InvalidParameterException` for invalid parameter provided to an API instance
16+
- New exception `Redmine\Exception\MissingParameterException` for missing parameter while using an API instance
17+
1018
### Changed
1119

1220
- Switched from Travis-CI to Github Actions

docs/migrate-to-psr18client.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Migrate from `Redmine\Client` to `Redmine\Client\Psr18Client`
22

3-
Since `php-redmine-api` v1.7.0 there is a new PSR-18 based client `Redmine\Client\Psr18Client`. This guide will help you to migrate your code if you want to use an app-wide PSR-18 HTTP client.
3+
Since `v1.7.0` there is a new PSR-18 based client `Redmine\Client\Psr18Client`. This guide will help you to migrate your code if you want to use an app-wide PSR-18 HTTP client.
44

55
## 1. Use new client methods
66

docs/usage.md

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,8 @@ client.
5151

5252
#### Native cURL Client `Redmine\Client\NativeCurlClient`
5353

54+
> :bulb: The `Redmine\Client\NativeCurlClient` class was introduced in `v1.8.0`.
55+
5456
Every client requires a URL to your Redmine instance and either a valid
5557
Apikey...
5658

@@ -100,6 +102,8 @@ $client = new \Redmine\Client('https://redmine.example.com', '1234567890abcdfgh'
100102

101103
#### Psr-18 compatible Client `Redmine\Client\Psr18Client`
102104

105+
> :bulb: The `Redmine\Client\Psr18Client` class was introduced in `v1.7.0`.
106+
103107
The `Psr18Client` requires
104108

105109
- a `Psr\Http\Client\ClientInterface` implementation (like guzzlehttp/guzzle) ([possible implementations](https://packagist.org/providers/psr/http-client-implementation))
@@ -117,7 +121,7 @@ The `Psr18Client` requires
117121
require_once 'vendor/autoload.php';
118122
+
119123
+$guzzle = \GuzzleHttp\Client();
120-
+$psr17Factory = new \Nyholm\Psr7\Factory\Psr17Factory();
124+
+$psr17Factory = new \GuzzleHttp\Psr7\HttpFactory();
121125
+
122126
+// Instantiate with ApiKey
123127
+$client = new Redmine\Client\Prs18Client($guzzle, $psr17Factory, $psr17Factory, 'https://redmine.example.com', '1234567890abcdfgh');
@@ -147,7 +151,7 @@ require_once 'vendor/autoload.php';
147151
+use Psr\Http\Message\ResponseInterface;
148152
+
149153
$guzzle = \GuzzleHttp\Client();
150-
$psr17Factory = new \Nyholm\Psr7\Factory\Psr17Factory();
154+
$psr17Factory = new \GuzzleHttp\Psr7\HttpFactory();
151155

152156
+$guzzleWrapper = new class(\GuzzleHttp\Client $guzzle) implements ClientInterface
153157
+{
@@ -195,6 +199,22 @@ $client->startImpersonateUser('kim');
195199
$client->stopImpersonateUser();
196200
```
197201

202+
## Error handling
203+
204+
Every exception implement the interface `Redmine\Exception` making it easy to catch Redmine specific issues.
205+
206+
> :bulb: The `Redmine\Exception` interface was introduced in `v2.1.0`.
207+
208+
```php
209+
try {
210+
$client->getApi('issue')->create($data);
211+
} catch (\Redmine\Exception $e) {
212+
// exceptions from kbsali/redmine-api
213+
} catch (\Throwable $e) {
214+
// other errors
215+
}
216+
```
217+
198218
## API
199219

200220
You can now use the `getApi()` method to create and get a specific Redmine API.

src/Redmine/Api/Group.php

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22

33
namespace Redmine\Api;
44

5+
use Exception;
6+
use Redmine\Exception\MissingParameterException;
7+
58
/**
69
* Handling of groups.
710
*
@@ -56,7 +59,7 @@ public function listing($forceUpdate = false)
5659
*
5760
* @param array $params the new group data
5861
*
59-
* @throws \Exception Missing mandatory parameters
62+
* @throws MissingParameterException Missing mandatory parameters
6063
*
6164
* @return \SimpleXMLElement
6265
*/
@@ -71,7 +74,7 @@ public function create(array $params = [])
7174
if (
7275
!isset($params['name'])
7376
) {
74-
throw new \Exception('Missing mandatory parameters');
77+
throw new MissingParameterException('Theses parameters are mandatory: `name`');
7578
}
7679

7780
$xml = $this->buildXML($params);
@@ -86,11 +89,11 @@ public function create(array $params = [])
8689
*
8790
* @param int $id
8891
*
89-
* @throws \Exception Not implemented
92+
* @throws Exception Not implemented
9093
*/
9194
public function update($id, array $params = [])
9295
{
93-
throw new \Exception('Not implemented');
96+
throw new Exception('Not implemented');
9497
}
9598

9699
/**

src/Redmine/Api/IssueCategory.php

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
namespace Redmine\Api;
44

5+
use Redmine\Exception\MissingParameterException;
6+
57
/**
68
* Listing issue categories, creating, editing.
79
*
@@ -91,7 +93,7 @@ public function show($id)
9193
* @param string|int $project project id or literal identifier
9294
* @param array $params the new issue category data
9395
*
94-
* @throws \Exception Missing mandatory parameters
96+
* @throws MissingParameterException Missing mandatory parameters
9597
*
9698
* @return string|false
9799
*/
@@ -106,7 +108,7 @@ public function create($project, array $params = [])
106108
if (
107109
!isset($params['name'])
108110
) {
109-
throw new \Exception('Missing mandatory parameters');
111+
throw new MissingParameterException('Theses parameters are mandatory: `name`');
110112
}
111113

112114
$xml = new \SimpleXMLElement('<?xml version="1.0"?><issue_category></issue_category>');

src/Redmine/Api/Membership.php

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
namespace Redmine\Api;
44

5+
use Redmine\Exception\MissingParameterException;
6+
57
/**
68
* Handling project memberships.
79
*
@@ -38,7 +40,7 @@ public function all($project, array $params = [])
3840
* @param string|int $project project id or literal identifier
3941
* @param array $params the new membership data
4042
*
41-
* @throws \Exception Missing mandatory parameters
43+
* @throws MissingParameterException Missing mandatory parameters
4244
*
4345
* @return string|false
4446
*/
@@ -51,7 +53,7 @@ public function create($project, array $params = [])
5153
$params = $this->sanitizeParams($defaults, $params);
5254

5355
if (!isset($params['user_id']) || !isset($params['role_ids'])) {
54-
throw new \Exception('Missing mandatory parameters');
56+
throw new MissingParameterException('Theses parameters are mandatory: `user_id`, `role_ids`');
5557
}
5658

5759
$xml = $this->buildXML($params);
@@ -67,7 +69,7 @@ public function create($project, array $params = [])
6769
* @param int $id id of the membership
6870
* @param array $params the new membership data
6971
*
70-
* @throws \Exception Missing mandatory parameters
72+
* @throws MissingParameterException Missing mandatory parameters
7173
*
7274
* @return string|false
7375
*/
@@ -79,7 +81,7 @@ public function update($id, array $params = [])
7981
$params = $this->sanitizeParams($defaults, $params);
8082

8183
if (!isset($params['role_ids'])) {
82-
throw new \Exception('Missing mandatory parameters');
84+
throw new MissingParameterException('Missing mandatory parameters');
8385
}
8486

8587
$xml = $this->buildXML($params);

src/Redmine/Api/Project.php

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
namespace Redmine\Api;
44

5+
use Redmine\Exception\MissingParameterException;
6+
57
/**
68
* Listing projects, creating, editing.
79
*
@@ -98,7 +100,7 @@ public function show($id, array $params = [])
98100
*
99101
* @param array $params the new project data
100102
*
101-
* @throws \Exception
103+
* @throws MissingParameterException
102104
*
103105
* @return \SimpleXMLElement
104106
*/
@@ -115,7 +117,7 @@ public function create(array $params = [])
115117
!isset($params['name'])
116118
|| !isset($params['identifier'])
117119
) {
118-
throw new \Exception('Missing mandatory parameters');
120+
throw new MissingParameterException('Theses parameters are mandatory: `name`, `identifier`');
119121
}
120122

121123
$xml = $this->prepareParamsXml($params);

src/Redmine/Api/TimeEntry.php

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
namespace Redmine\Api;
44

5+
use Redmine\Exception\MissingParameterException;
6+
57
/**
68
* Listing time entries, creating, editing.
79
*
@@ -50,7 +52,7 @@ public function show($id)
5052
*
5153
* @param array $params the new time entry data
5254
*
53-
* @throws \Exception Missing mandatory parameters
55+
* @throws MissingParameterException Missing mandatory parameters
5456
*
5557
* @return string|false
5658
*/
@@ -70,7 +72,7 @@ public function create(array $params = [])
7072
(!isset($params['issue_id']) && !isset($params['project_id']))
7173
|| !isset($params['hours'])
7274
) {
73-
throw new \Exception('Missing mandatory parameters');
75+
throw new MissingParameterException('Theses parameters are mandatory: `issue_id` or `project_id`, `hours`');
7476
}
7577

7678
$xml = new \SimpleXMLElement('<?xml version="1.0"?><time_entry></time_entry>');

src/Redmine/Api/User.php

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
namespace Redmine\Api;
44

5+
use Redmine\Exception\MissingParameterException;
6+
57
/**
68
* Listing users, creating, editing.
79
*
@@ -129,7 +131,7 @@ public function show($id, array $params = [])
129131
*
130132
* @param array $params the new user data
131133
*
132-
* @throws \Exception Missing mandatory parameters
134+
* @throws MissingParameterException Missing mandatory parameters
133135
*
134136
* @return string|false
135137
*/
@@ -150,7 +152,7 @@ public function create(array $params = [])
150152
|| !isset($params['firstname'])
151153
|| !isset($params['mail'])
152154
) {
153-
throw new \Exception('Missing mandatory parameters');
155+
throw new MissingParameterException('Theses parameters are mandatory: `login`, `lastname`, `firstname`, `mail`');
154156
}
155157
$xml = new \SimpleXMLElement('<?xml version="1.0"?><user></user>');
156158
foreach ($params as $k => $v) {

src/Redmine/Api/Version.php

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22

33
namespace Redmine\Api;
44

5+
use Redmine\Exception\InvalidParameterException;
6+
use Redmine\Exception\MissingParameterException;
7+
58
/**
69
* Listing versions, creating, editing.
710
*
@@ -94,7 +97,7 @@ public function show($id)
9497
* @param string|int $project project id or literal identifier
9598
* @param array $params the new issue category data
9699
*
97-
* @throws \Exception Missing mandatory parameters
100+
* @throws MissingParameterException Missing mandatory parameters
98101
*
99102
* @return string|false
100103
*/
@@ -112,7 +115,7 @@ public function create($project, array $params = [])
112115
if (
113116
!isset($params['name'])
114117
) {
115-
throw new \Exception('Missing mandatory parameters');
118+
throw new MissingParameterException('Theses parameters are mandatory: `name`');
116119
}
117120
$this->validateStatus($params);
118121
$this->validateSharing($params);
@@ -171,7 +174,7 @@ private function validateStatus(array $params = [])
171174
'closed',
172175
];
173176
if (isset($params['status']) && !in_array($params['status'], $arrStatus)) {
174-
throw new \Exception('Possible values for status : '.implode(', ', $arrStatus));
177+
throw new InvalidParameterException('Possible values for status : '.implode(', ', $arrStatus));
175178
}
176179
}
177180

@@ -185,7 +188,7 @@ private function validateSharing(array $params = [])
185188
'system' => 'With all projects',
186189
];
187190
if (isset($params['sharing']) && !isset($arrSharing[$params['sharing']])) {
188-
throw new \Exception('Possible values for sharing : '.implode(', ', array_keys($arrSharing)));
191+
throw new InvalidParameterException('Possible values for sharing : '.implode(', ', array_keys($arrSharing)));
189192
}
190193
}
191194

src/Redmine/Client/Client.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,16 @@
22

33
namespace Redmine\Client;
44

5-
use InvalidArgumentException;
65
use Redmine\Api;
6+
use Redmine\Exception\InvalidApiNameException;
77

88
/**
99
* client interface.
1010
*/
1111
interface Client
1212
{
1313
/**
14-
* @throws InvalidArgumentException if $name is not a valid api name
14+
* @throws InvalidApiNameException if $name is not a valid api name
1515
*/
1616
public function getApi(string $name): Api;
1717

src/Redmine/Client/ClientApiTrait.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@
22

33
namespace Redmine\Client;
44

5-
use InvalidArgumentException;
65
use Redmine\Api;
6+
use Redmine\Exception\InvalidApiNameException;
77

88
/**
99
* Provide API instantiation to clients.
@@ -38,12 +38,12 @@ trait ClientApiTrait
3838
];
3939

4040
/**
41-
* @throws InvalidArgumentException if $name is not a valid api name
41+
* @throws InvalidApiNameException if $name is not a valid api name
4242
*/
4343
public function getApi(string $name): Api
4444
{
4545
if (!isset($this->apiClassnames[$name])) {
46-
throw new InvalidArgumentException(sprintf('`%s` is not a valid api. Possible apis are `%s`', $name, implode('`, `', array_keys($this->apiClassnames))));
46+
throw new InvalidApiNameException(sprintf('`%s` is not a valid api. Possible apis are `%s`', $name, implode('`, `', array_keys($this->apiClassnames))));
4747
}
4848
if (isset($this->apiInstances[$name])) {
4949
return $this->apiInstances[$name];

src/Redmine/Client/NativeCurlClient.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@
44

55
namespace Redmine\Client;
66

7-
use Exception;
87
use Redmine\Api;
8+
use Redmine\Exception\ClientException;
99

1010
/**
1111
* Native cURL client.
@@ -210,7 +210,7 @@ private function unsetHttpHeader(string $name): void
210210
}
211211

212212
/**
213-
* @throws Exception If anything goes wrong on curl request
213+
* @throws ClientException If anything goes wrong on curl request
214214
*/
215215
private function request(string $method, string $path, string $body = ''): bool
216216
{
@@ -225,7 +225,7 @@ private function request(string $method, string $path, string $body = ''): bool
225225
$curlErrorNumber = curl_errno($curl);
226226

227227
if (CURLE_OK !== $curlErrorNumber) {
228-
$e = new Exception(curl_error($curl), $curlErrorNumber);
228+
$e = new ClientException(curl_error($curl), $curlErrorNumber);
229229
curl_close($curl);
230230
throw $e;
231231
}

0 commit comments

Comments
 (0)