Skip to content

Commit 9579888

Browse files
authored
Merge pull request #100 from e-zannelli/feat-guzzle-failure-detector
Add guzzle failure detector
2 parents 68792cb + 889434d commit 9579888

File tree

5 files changed

+93
-4
lines changed

5 files changed

+93
-4
lines changed

README.md

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -418,6 +418,35 @@ $middleware = new GuzzleMiddleware(
418418
);
419419
```
420420

421+
### How does Guzzle Middleware determine the failure?
422+
423+
By default, if the next handler promise is fulfilled ganesha will consider it a success, and a failure if it is rejected.
424+
425+
You can implement your own rules on fulfilled response by passing an implementation of `FailureDetectorInterface` to the middleware.
426+
427+
```php
428+
use Ackintosh\Ganesha\GuzzleMiddleware\FailureDetectorInterface;
429+
use Psr\Http\Message\ResponseInterface;
430+
431+
class HttpStatusFailureDetector implements FailureDetectorInterface
432+
{
433+
public function isFailureResponse(ResponseInterface $response) : bool
434+
{
435+
return in_array($response->getStatusCode(), [503, 504], true);
436+
}
437+
}
438+
439+
// ---
440+
$ganesha = Builder::withRateStrategy()
441+
// ...
442+
->build();
443+
$middleware = new GuzzleMiddleware(
444+
$ganesha,
445+
// Pass the failure detector to the GuzzleMiddleware constructor.
446+
failureDetector: new HttpStatusFailureDetector()
447+
);
448+
```
449+
421450
## [Ganesha :heart: OpenAPI Generator](#table-of-contents)
422451

423452
PHP client generated by [OpenAPI Generator](https://github.com/OpenAPITools/openapi-generator) is using Guzzle as HTTP client and as we mentioned as [Ganesha :heart: Guzzle](https://github.com/ackintosh/ganesha#ganesha-heart-guzzle), Guzzle Middleware powered by Ganesha is ready. So it is easily possible to integrate Ganesha and the PHP client generated by OpenAPI Generator in a smart way as below.

src/Ganesha/GuzzleMiddleware.php

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

44
use Ackintosh\Ganesha;
55
use Ackintosh\Ganesha\Exception\RejectedException;
6+
use Ackintosh\Ganesha\GuzzleMiddleware\AlwaysSuccessFailureDetector;
7+
use Ackintosh\Ganesha\GuzzleMiddleware\FailureDetectorInterface;
68
use Ackintosh\Ganesha\GuzzleMiddleware\ServiceNameExtractor;
79
use Ackintosh\Ganesha\GuzzleMiddleware\ServiceNameExtractorInterface;
810
use Psr\Http\Message\RequestInterface;
@@ -19,12 +21,19 @@ class GuzzleMiddleware
1921
*/
2022
private $serviceNameExtractor;
2123

24+
/**
25+
* @var FailureDetectorInterface
26+
*/
27+
private $failureDetector;
28+
2229
public function __construct(
2330
Ganesha $ganesha,
24-
ServiceNameExtractorInterface $serviceNameExtractor = null
31+
ServiceNameExtractorInterface $serviceNameExtractor = null,
32+
FailureDetectorInterface $failureDetector = null
2533
) {
2634
$this->ganesha = $ganesha;
2735
$this->serviceNameExtractor = $serviceNameExtractor ?: new ServiceNameExtractor();
36+
$this->failureDetector = $failureDetector ?: new AlwaysSuccessFailureDetector();
2837
}
2938

3039
/**
@@ -48,7 +57,11 @@ public function __invoke(callable $handler): \Closure
4857

4958
return $promise->then(
5059
function ($value) use ($serviceName) {
51-
$this->ganesha->success($serviceName);
60+
if ($this->failureDetector->isFailureResponse($value)) {
61+
$this->ganesha->failure($serviceName);
62+
} else {
63+
$this->ganesha->success($serviceName);
64+
}
5265
return \GuzzleHttp\Promise\promise_for($value);
5366
},
5467
function ($reason) use ($serviceName) {
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<?php
2+
3+
namespace Ackintosh\Ganesha\GuzzleMiddleware;
4+
5+
use Psr\Http\Message\ResponseInterface;
6+
7+
class AlwaysSuccessFailureDetector implements FailureDetectorInterface
8+
{
9+
public function isFailureResponse(ResponseInterface $response): bool
10+
{
11+
return false;
12+
}
13+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<?php
2+
3+
namespace Ackintosh\Ganesha\GuzzleMiddleware;
4+
5+
use Psr\Http\Message\ResponseInterface;
6+
7+
interface FailureDetectorInterface
8+
{
9+
public function isFailureResponse(ResponseInterface $response): bool;
10+
}

tests/Ackintosh/Ganesha/GuzzleMiddlewareTest.php

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
namespace Ackintosh\Ganesha;
33

44
use Ackintosh\Ganesha\Exception\RejectedException;
5+
use Ackintosh\Ganesha\GuzzleMiddleware\FailureDetectorInterface;
56
use Ackintosh\Ganesha\Storage\Adapter\Memcached;
67
use Ackintosh\Ganesha\Storage\Adapter\Redis;
78
use GuzzleHttp\Client;
@@ -94,6 +95,28 @@ public function recordsSuccessOn500()
9495
);
9596
}
9697

98+
/**
99+
* @test
100+
* @vcr guzzle_responses.yml
101+
*/
102+
public function failureDetectorControlsIfResponseIsFailure()
103+
{
104+
$failureDetector = $this->createMock(FailureDetectorInterface::class);
105+
$failureDetector->expects($this->once())->method('isFailureResponse')->willReturn(true);
106+
$client = $this->buildClient($failureDetector);
107+
$response = $client->get('http://api.example.com/awesome_resource/200');
108+
109+
$this->assertSame(200, $response->getStatusCode());
110+
$this->assertSame(
111+
1,
112+
$this->adapter->load(Storage\StorageKeys::KEY_PREFIX . 'api.example.com' . Storage\StorageKeys::KEY_SUFFIX_FAILURE)
113+
);
114+
$this->assertSame(
115+
0,
116+
$this->adapter->load(Storage\StorageKeys::KEY_PREFIX . 'api.example.com' . Storage\StorageKeys::KEY_SUFFIX_SUCCESS)
117+
);
118+
}
119+
97120
/**
98121
* @test
99122
*/
@@ -162,7 +185,7 @@ public function reject()
162185
/**
163186
* @return Client
164187
*/
165-
private function buildClient()
188+
private function buildClient(?FailureDetectorInterface $failureDetector = null)
166189
{
167190
$ganesha = Builder::withRateStrategy()
168191
->timeWindow(30)
@@ -172,7 +195,8 @@ private function buildClient()
172195
->adapter($this->adapter)
173196
->build();
174197

175-
$middleware = new GuzzleMiddleware($ganesha);
198+
199+
$middleware = new GuzzleMiddleware($ganesha, null, $failureDetector);
176200
$handlers = HandlerStack::create();
177201
$handlers->push($middleware);
178202

0 commit comments

Comments
 (0)