Skip to content

Commit 4c2a3bc

Browse files
authored
Merge pull request #1964 from pkly/master
Add events for requests
2 parents 872c5ff + c3591ad commit 4c2a3bc

File tree

7 files changed

+299
-0
lines changed

7 files changed

+299
-0
lines changed

composer.json

+1
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
"ruflin/elastica": "^7.1",
3737
"symfony/console": "^5.4 || ^6.4 || ^7.1",
3838
"symfony/dependency-injection": "^5.4 || ^6.4 || ^7.1",
39+
"symfony/event-dispatcher": "^5.4 || ^6.4 || ^7.1",
3940
"symfony/framework-bundle": "^5.4 || ^6.4 || ^7.1",
4041
"symfony/property-access": "^5.4 || ^6.4 || ^7.1",
4142
"symfony/stopwatch": "^5.4 || ^6.4 || ^7.1"

src/Elastica/Client.php

+25
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,11 @@
1717
use Elastica\Index as BaseIndex;
1818
use Elastica\Request;
1919
use Elastica\Response;
20+
use FOS\ElasticaBundle\Event\ElasticaRequestExceptionEvent;
21+
use FOS\ElasticaBundle\Event\PostElasticaRequestEvent;
22+
use FOS\ElasticaBundle\Event\PreElasticaRequestEvent;
2023
use FOS\ElasticaBundle\Logger\ElasticaLogger;
24+
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
2125
use Symfony\Component\Stopwatch\Stopwatch;
2226

2327
/**
@@ -49,6 +53,8 @@ class Client extends BaseClient
4953
*/
5054
private $stopwatch;
5155

56+
private ?EventDispatcherInterface $dispatcher = null;
57+
5258
/**
5359
* @param array<mixed> $data
5460
* @param array<mixed> $query
@@ -59,10 +65,24 @@ public function request(string $path, string $method = Request::GET, $data = [],
5965
$this->stopwatch->start('es_request', 'fos_elastica');
6066
}
6167

68+
// todo: 8.1 change into nullable call
69+
if ($this->dispatcher) {
70+
$this->dispatcher->dispatch(new PreElasticaRequestEvent($path, $method, $data, $query, $contentType));
71+
}
72+
6273
try {
6374
$response = parent::request($path, $method, $data, $query, $contentType);
75+
76+
if ($this->dispatcher) {
77+
$this->dispatcher->dispatch(new PostElasticaRequestEvent($this->getLastRequest(), $this->getLastResponse()));
78+
}
6479
} catch (ExceptionInterface $e) {
6580
$this->logQuery($path, $method, $data, $query, 0, 0, 0);
81+
82+
if ($this->dispatcher) {
83+
$this->dispatcher->dispatch(new ElasticaRequestExceptionEvent($this->getLastRequest(), $e));
84+
}
85+
6686
throw $e;
6787
}
6888

@@ -114,6 +134,11 @@ public function setStopwatch(?Stopwatch $stopwatch = null): void
114134
$this->stopwatch = $stopwatch;
115135
}
116136

137+
public function setEventDispatcher(?EventDispatcherInterface $dispatcher = null): void
138+
{
139+
$this->dispatcher = $dispatcher;
140+
}
141+
117142
/**
118143
* Log the query if we have an instance of ElasticaLogger.
119144
*
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the FOSElasticaBundle package.
5+
*
6+
* (c) FriendsOfSymfony <https://friendsofsymfony.github.com/>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace FOS\ElasticaBundle\Event;
13+
14+
use Elastica\Exception\ExceptionInterface;
15+
use Elastica\Request;
16+
17+
class ElasticaRequestExceptionEvent
18+
{
19+
private Request $request;
20+
private ExceptionInterface $exception;
21+
22+
public function __construct(
23+
Request $request,
24+
ExceptionInterface $exception
25+
) {
26+
$this->request = $request;
27+
$this->exception = $exception;
28+
}
29+
30+
public function getRequest(): Request
31+
{
32+
return $this->request;
33+
}
34+
35+
public function getException(): ExceptionInterface
36+
{
37+
return $this->exception;
38+
}
39+
}
+39
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the FOSElasticaBundle package.
5+
*
6+
* (c) FriendsOfSymfony <https://friendsofsymfony.github.com/>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace FOS\ElasticaBundle\Event;
13+
14+
use Elastica\Request;
15+
use Elastica\Response;
16+
17+
class PostElasticaRequestEvent
18+
{
19+
private Request $request;
20+
private Response $response;
21+
22+
public function __construct(
23+
Request $request,
24+
Response $response
25+
) {
26+
$this->request = $request;
27+
$this->response = $response;
28+
}
29+
30+
public function getRequest(): Request
31+
{
32+
return $this->request;
33+
}
34+
35+
public function getResponse(): Response
36+
{
37+
return $this->response;
38+
}
39+
}

src/Event/PreElasticaRequestEvent.php

+77
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the FOSElasticaBundle package.
5+
*
6+
* (c) FriendsOfSymfony <https://friendsofsymfony.github.com/>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace FOS\ElasticaBundle\Event;
13+
14+
use Elastica\Request;
15+
use Symfony\Contracts\EventDispatcher\Event;
16+
17+
class PreElasticaRequestEvent extends Event
18+
{
19+
private string $path;
20+
private string $method;
21+
22+
/**
23+
* @var array<string, mixed>|string
24+
*/
25+
private $data;
26+
27+
/**
28+
* @var array<string, mixed>
29+
*/
30+
private array $query;
31+
private string $contentType;
32+
33+
public function __construct(
34+
string $path,
35+
string $method,
36+
$data,
37+
array $query,
38+
string $contentType = Request::DEFAULT_CONTENT_TYPE
39+
) {
40+
$this->path = $path;
41+
$this->method = $method;
42+
$this->data = $data;
43+
$this->query = $query;
44+
$this->contentType = $contentType;
45+
}
46+
47+
public function getPath(): string
48+
{
49+
return $this->path;
50+
}
51+
52+
public function getMethod(): string
53+
{
54+
return $this->method;
55+
}
56+
57+
/**
58+
* @return array<string, mixed>|string
59+
*/
60+
public function getData()
61+
{
62+
return $this->data;
63+
}
64+
65+
/**
66+
* @return array<string, mixed>
67+
*/
68+
public function getQuery(): array
69+
{
70+
return $this->query;
71+
}
72+
73+
public function getContentType(): string
74+
{
75+
return $this->contentType;
76+
}
77+
}

src/Resources/config/config.xml

+3
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@
1717
<call method="setStopwatch">
1818
<argument type="service" id="debug.stopwatch" on-invalid="null" />
1919
</call>
20+
<call method="setEventDispatcher">
21+
<argument type="service" id="event_dispatcher" on-invalid="null" />
22+
</call>
2023
</service>
2124

2225
<service id="fos_elastica.config_manager" class="FOS\ElasticaBundle\Configuration\ConfigManager">

tests/Unit/Elastica/ClientTest.php

+115
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,12 @@
1818
use Elastica\Response;
1919
use Elastica\Transport\NullTransport;
2020
use FOS\ElasticaBundle\Elastica\Client;
21+
use FOS\ElasticaBundle\Event\ElasticaRequestExceptionEvent;
22+
use FOS\ElasticaBundle\Event\PostElasticaRequestEvent;
23+
use FOS\ElasticaBundle\Event\PreElasticaRequestEvent;
2124
use FOS\ElasticaBundle\Logger\ElasticaLogger;
2225
use PHPUnit\Framework\TestCase;
26+
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
2327

2428
/**
2529
* @internal
@@ -52,6 +56,117 @@ public function testRequestsAreLogged()
5256
$this->assertInstanceOf(Response::class, $response);
5357
}
5458

59+
public function testSendsNormalEvents(): void
60+
{
61+
$client = $this->getClientMock();
62+
$dispatcher = $this->createMock(EventDispatcherInterface::class);
63+
$dispatcher->expects($invoke = $this->exactly(2))
64+
->method('dispatch')
65+
->with($this->callback(function ($o) use ($invoke): bool {
66+
$counter = $invoke->getInvocationCount() - 1;
67+
68+
if ($counter > 1) {
69+
return false;
70+
}
71+
72+
if (0 === $counter) {
73+
if (!($o instanceof PreElasticaRequestEvent)) {
74+
return false;
75+
}
76+
77+
$this->assertEquals('event', $o->getPath());
78+
$this->assertEquals(Request::GET, $o->getMethod());
79+
$this->assertEquals(['some' => 'data'], $o->getData());
80+
$this->assertEquals(['query' => 'data'], $o->getQuery());
81+
$this->assertEquals(Request::DEFAULT_CONTENT_TYPE, $o->getContentType());
82+
} elseif (1 === $counter) {
83+
if (!($o instanceof PostElasticaRequestEvent)) {
84+
return false;
85+
}
86+
87+
$request = $o->getRequest();
88+
89+
$this->assertEquals('event', $request->getPath());
90+
$this->assertEquals(Request::GET, $request->getMethod());
91+
$this->assertEquals(['some' => 'data'], $request->getData());
92+
$this->assertEquals(['query' => 'data'], $request->getQuery());
93+
$this->assertEquals(Request::DEFAULT_CONTENT_TYPE, $request->getContentType());
94+
95+
$this->assertInstanceOf(Response::class, $o->getResponse());
96+
}
97+
98+
return true;
99+
}))
100+
;
101+
102+
$client->setEventDispatcher($dispatcher);
103+
$client->request('event', Request::GET, ['some' => 'data'], ['query' => 'data']);
104+
}
105+
106+
public function testSendsExceptionEvents(): void
107+
{
108+
$httpCode = 403;
109+
$responseString = JSON::stringify(['message' => 'some AWS error']);
110+
$transferInfo = [
111+
'request_header' => 'bar',
112+
'http_code' => $httpCode,
113+
'body' => $responseString,
114+
];
115+
$response = new Response($responseString);
116+
$response->setTransferInfo($transferInfo);
117+
118+
$connection = $this->getConnectionMock();
119+
$connection->method('getTransportObject')
120+
->willThrowException(new ClientException())
121+
;
122+
123+
$client = $this->getClientMock($response, $connection);
124+
125+
$dispatcher = $this->createMock(EventDispatcherInterface::class);
126+
$dispatcher->expects($invoke = $this->exactly(2))
127+
->method('dispatch')
128+
->with($this->callback(function ($o) use ($invoke): bool {
129+
$counter = $invoke->getInvocationCount() - 1;
130+
131+
if ($counter > 1) {
132+
return false;
133+
}
134+
135+
if (0 === $counter) {
136+
if (!($o instanceof PreElasticaRequestEvent)) {
137+
return false;
138+
}
139+
140+
$this->assertEquals('event', $o->getPath());
141+
$this->assertEquals(Request::GET, $o->getMethod());
142+
$this->assertEquals(['some' => 'data'], $o->getData());
143+
$this->assertEquals(['query' => 'data'], $o->getQuery());
144+
$this->assertEquals(Request::DEFAULT_CONTENT_TYPE, $o->getContentType());
145+
} elseif (1 === $counter) {
146+
if (!($o instanceof ElasticaRequestExceptionEvent)) {
147+
return false;
148+
}
149+
150+
$request = $o->getRequest();
151+
152+
$this->assertEquals('event', $request->getPath());
153+
$this->assertEquals(Request::GET, $request->getMethod());
154+
$this->assertEquals(['some' => 'data'], $request->getData());
155+
$this->assertEquals(['query' => 'data'], $request->getQuery());
156+
$this->assertEquals(Request::DEFAULT_CONTENT_TYPE, $request->getContentType());
157+
158+
$this->assertInstanceOf(ClientException::class, $o->getException());
159+
}
160+
161+
return true;
162+
}))
163+
;
164+
165+
$client->setEventDispatcher($dispatcher);
166+
$this->expectException(ClientException::class);
167+
$client->request('event', Request::GET, ['some' => 'data'], ['query' => 'data']);
168+
}
169+
55170
public function testRequestsWithTransportInfoErrorsRaiseExceptions()
56171
{
57172
$httpCode = 403;

0 commit comments

Comments
 (0)