Skip to content

Commit 8b30ba6

Browse files
jandomXWB
authored andcommitted
Handle Elastica Transport errors (#1465)
* Elastica/Client uses getTransportInfo and throws an exception if couldn't connect to ES host * Pass errored response body in transportInfo * Revert changes is composer.json - no longer point to a fork of ruflin/Elistica * Switch to ruflin/elastica 6.1 in composer.json * Factor out the forbidden HTTP codes Put in configuration instead of hard-coding in the Client class Unit test the configuration Defaults are 400, 403, 404 Refactor unit tests and mocks for the Client * Switch from Exception to Elastica\Exception\ClientException Update unit test * Refactor following discussion in ruflin/Elastica * Remove double carrige return * Handle responseData being a string or an array * Update composer.json
1 parent c2c5e06 commit 8b30ba6

File tree

5 files changed

+106
-11
lines changed

5 files changed

+106
-11
lines changed

composer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
"symfony/property-access": "^3.4|^4",
2020
"pagerfanta/pagerfanta": "^1.0.5|^2.0",
2121
"psr/log": "^1.0",
22-
"ruflin/elastica": "^5.3|^6.1"
22+
"ruflin/elastica": "^5.3.5|^6.1.1"
2323
},
2424
"require-dev": {
2525
"doctrine/orm": "^2.5",

src/DependencyInjection/Configuration.php

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -452,6 +452,15 @@ private function addClientsSection(ArrayNodeDefinition $rootNode)
452452
->scalarNode('host')->end()
453453
->scalarNode('port')->end()
454454
->scalarNode('proxy')->end()
455+
->arrayNode('http_error_codes')
456+
->beforeNormalization()
457+
->ifTrue(function ($v) { return !is_array($v); })
458+
->then(function ($v) { return array($v); })
459+
->end()
460+
->cannotBeEmpty()
461+
->defaultValue([400, 403, 404])
462+
->prototype('scalar')->end()
463+
->end()
455464
->scalarNode('aws_access_key_id')->end()
456465
->scalarNode('aws_secret_access_key')->end()
457466
->scalarNode('aws_region')->end()

src/Elastica/Client.php

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
namespace FOS\ElasticaBundle\Elastica;
1313

1414
use Elastica\Client as BaseClient;
15+
use Elastica\Exception\ClientException;
1516
use Elastica\Request;
1617
use FOS\ElasticaBundle\Logger\ElasticaLogger;
1718
use Symfony\Component\Stopwatch\Stopwatch;
@@ -50,6 +51,16 @@ public function request($path, $method = Request::GET, $data = [], array $query
5051
$response = parent::request($path, $method, $data, $query, $contentType);
5152
$responseData = $response->getData();
5253

54+
$transportInfo = $response->getTransferInfo();
55+
$connection = $this->getLastRequest()->getConnection();
56+
$forbiddenHttpCodes = $connection->hasConfig('http_error_codes') ? $connection->getConfig('http_error_codes') : [];
57+
58+
if (isset($transportInfo['http_code']) && in_array($transportInfo['http_code'], $forbiddenHttpCodes, true)) {
59+
$body = is_array($responseData) ? json_encode($responseData) : $responseData;
60+
$message = sprintf('Error in transportInfo: response code is %s, response body is %s', $transportInfo['http_code'], $body);
61+
throw new ClientException($message);
62+
}
63+
5364
if (isset($responseData['took']) && isset($responseData['hits'])) {
5465
$this->logQuery($path, $method, $data, $query, $response->getQueryTime(), $response->getEngineTime(), $responseData['hits']['total']);
5566
} else {

tests/Unit/DependencyInjection/ConfigurationTest.php

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -310,6 +310,30 @@ public function testAWSConfig()
310310
$this->assertTrue($connection['ssl']);
311311
}
312312

313+
public function testHttpErrorCodesConfig()
314+
{
315+
// test defaults
316+
$configuration = $this->getConfigs([
317+
'clients' => [
318+
'default' => [
319+
],
320+
],
321+
]);
322+
$connection = $configuration['clients']['default']['connections'][0];
323+
$this->assertSame([400, 403, 404], $connection['http_error_codes']);
324+
325+
// test custom
326+
$configuration = $this->getConfigs([
327+
'clients' => [
328+
'default' => [
329+
'http_error_codes' => ['HTTP_ERROR_CODE']
330+
],
331+
],
332+
]);
333+
$connection = $configuration['clients']['default']['connections'][0];
334+
$this->assertSame(['HTTP_ERROR_CODE'], $connection['http_error_codes']);
335+
}
336+
313337
private function getConfigs(array $configArray)
314338
{
315339
$configuration = new Configuration(true);

tests/Unit/Elastica/ClientTest.php

Lines changed: 61 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -11,24 +11,49 @@
1111

1212
namespace FOS\ElasticaBundle\Tests\Unit\Client;
1313

14+
use Elastica\Client as BaseClient;
1415
use Elastica\Connection;
16+
use Elastica\JSON;
1517
use Elastica\Request;
1618
use Elastica\Response;
1719
use Elastica\Transport\NullTransport;
1820
use FOS\ElasticaBundle\Elastica\Client;
1921
use FOS\ElasticaBundle\Logger\ElasticaLogger;
2022
use PHPUnit\Framework\TestCase;
23+
use Elastica\Exception\ClientException;
2124

2225
class ClientTest extends TestCase
2326
{
24-
public function testRequestsAreLogged()
27+
private function getConnectionMock()
28+
{
29+
$connection = $this->createMock(Connection::class);
30+
$connection->expects($this->any())->method('toArray')->will($this->returnValue([]));
31+
return $connection;
32+
}
33+
34+
private function getClientMock(Response $response = null, $connection = null)
2535
{
2636
$transport = new NullTransport();
37+
if ($response) {
38+
$transport->setResponse($response);
39+
}
2740

28-
$connection = $this->createMock(Connection::class);
41+
if (!$connection) {
42+
$connection = $this->getConnectionMock();
43+
}
2944
$connection->expects($this->any())->method('getTransportObject')->will($this->returnValue($transport));
30-
$connection->expects($this->any())->method('toArray')->will($this->returnValue([]));
3145

46+
$client = $this->getMockBuilder(Client::class)
47+
->setMethods(['getConnection'])
48+
->getMock();
49+
50+
$client->expects($this->any())->method('getConnection')->will($this->returnValue($connection));
51+
return $client;
52+
}
53+
54+
public function testRequestsAreLogged()
55+
{
56+
$client = $this->getClientMock();
3257
$logger = $this->createMock(ElasticaLogger::class);
3358
$logger
3459
->expects($this->once())
@@ -44,17 +69,43 @@ public function testRequestsAreLogged()
4469
$this->isType('array'),
4570
$this->isType('array')
4671
);
47-
48-
$client = $this->getMockBuilder(Client::class)
49-
->setMethods(['getConnection'])
50-
->getMock();
51-
52-
$client->expects($this->any())->method('getConnection')->will($this->returnValue($connection));
53-
5472
$client->setLogger($logger);
5573

5674
$response = $client->request('foo');
5775

5876
$this->assertInstanceOf(Response::class, $response);
5977
}
78+
79+
public function testRequestsWithTransportInfoErrorsRaiseExceptions()
80+
{
81+
$httpCode = 403;
82+
$responseString = JSON::stringify(['message' => 'some AWS error']);
83+
$transferInfo = [
84+
'request_header' => 'bar',
85+
'http_code' => $httpCode,
86+
'body' => $responseString,
87+
];
88+
$response = new Response($responseString);
89+
$response->setTransferInfo($transferInfo);
90+
91+
$connection = $this->getConnectionMock();
92+
$connection
93+
->expects($this->exactly(1))
94+
->method('hasConfig')
95+
->with('http_error_codes')
96+
->willReturn(true)
97+
;
98+
$connection
99+
->expects($this->exactly(1))
100+
->method('getConfig')
101+
->with('http_error_codes')
102+
->willReturn([400, 403, 404])
103+
;
104+
$client = $this->getClientMock($response, $connection);
105+
106+
$desiredMessage = sprintf('Error in transportInfo: response code is %d, response body is %s', $httpCode, $responseString);
107+
$this->expectException(ClientException::class);
108+
$this->expectExceptionMessage($desiredMessage);
109+
$response = $client->request('foo');
110+
}
60111
}

0 commit comments

Comments
 (0)