Skip to content

Commit 1cd3f88

Browse files
authored
Merge pull request #32 from factorio-item-browser/feature/track-searches
feature/track-searches
2 parents b0aa67a + 0023777 commit 1cd3f88

File tree

11 files changed

+239
-162
lines changed

11 files changed

+239
-162
lines changed

composer.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
"ext-PDO": "*",
1919
"bluepsyduck/mapper-manager": "^1.0",
2020
"bluepsyduck/laminas-autowire-factory": "^1.0",
21-
"bluepsyduck/ga4-measurement-protocol": "^1.0",
21+
"bluepsyduck/ga4-measurement-protocol": "^2.0",
2222
"doctrine/cache": "^1.10",
2323
"factorio-item-browser/api-client": "^4.0",
2424
"factorio-item-browser/api-database": "^3.7",
@@ -33,7 +33,7 @@
3333
"mezzio/mezzio-fastroute": "^3.0",
3434
"mezzio/mezzio-helpers": "^5.0",
3535
"ramsey/uuid": "^4.0",
36-
"symfony/console": "^4.0 | ^5.0"
36+
"symfony/console": "^5.0"
3737
},
3838
"require-dev": {
3939
"bluepsyduck/test-helper": "^2.0",

composer.lock

Lines changed: 81 additions & 82 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

phpunit.xml

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
<?xml version="1.0" ?>
22
<phpunit colors="true">
3+
<coverage>
4+
<include>
5+
<directory suffix=".php">src</directory>
6+
</include>
7+
</coverage>
38
<testsuites>
49
<testsuite name="unit-test">
510
<directory>test/src</directory>
611
</testsuite>
712
</testsuites>
8-
<filter>
9-
<whitelist>
10-
<directory suffix=".php">src</directory>
11-
</whitelist>
12-
</filter>
1313
</phpunit>

src/Handler/Search/SearchQueryHandler.php

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,12 @@
66

77
use FactorioItemBrowser\Api\Client\Request\Search\SearchQueryRequest;
88
use FactorioItemBrowser\Api\Client\Response\Search\SearchQueryResponse;
9+
use FactorioItemBrowser\Api\Database\Entity\Combination;
910
use FactorioItemBrowser\Api\Search\SearchManagerInterface;
1011
use FactorioItemBrowser\Api\Server\Response\ClientResponse;
1112
use FactorioItemBrowser\Api\Server\Service\SearchDecoratorService;
13+
use FactorioItemBrowser\Api\Server\Service\TrackingService;
14+
use FactorioItemBrowser\Api\Server\Tracking\Event\SearchEvent;
1215
use Psr\Http\Message\ResponseInterface;
1316
use Psr\Http\Message\ServerRequestInterface;
1417
use Psr\Http\Server\RequestHandlerInterface;
@@ -24,19 +27,26 @@ class SearchQueryHandler implements RequestHandlerInterface
2427
{
2528
private SearchDecoratorService $searchDecoratorService;
2629
private SearchManagerInterface $searchManager;
30+
private TrackingService $trackingService;
2731

2832
public function __construct(
2933
SearchDecoratorService $searchDecoratorService,
30-
SearchManagerInterface $searchManager
34+
SearchManagerInterface $searchManager,
35+
TrackingService $trackingService,
3136
) {
3237
$this->searchDecoratorService = $searchDecoratorService;
3338
$this->searchManager = $searchManager;
39+
$this->trackingService = $trackingService;
3440
}
3541

3642
public function handle(ServerRequestInterface $request): ResponseInterface
3743
{
3844
/** @var SearchQueryRequest $clientRequest */
3945
$clientRequest = $request->getParsedBody();
46+
/** @var Combination $combination */
47+
$combination = $request->getAttribute(Combination::class);
48+
49+
$startTime = microtime(true);
4050

4151
$searchQuery = $this->searchManager->parseQuery(
4252
Uuid::fromString($clientRequest->combinationId),
@@ -56,6 +66,17 @@ public function handle(ServerRequestInterface $request): ResponseInterface
5666
$response = new SearchQueryResponse();
5767
$response->results = $decoratedSearchResults; // @phpstan-ignore-line
5868
$response->totalNumberOfResults = $searchResults->count();
69+
70+
$trackingEvent = new SearchEvent();
71+
$trackingEvent->combinationId = $clientRequest->combinationId;
72+
$trackingEvent->modCount = $combination->getMods()->count();
73+
$trackingEvent->locale = $clientRequest->locale;
74+
$trackingEvent->queryString = $clientRequest->query;
75+
$trackingEvent->resultCount = $response->totalNumberOfResults;
76+
$trackingEvent->runtime = microtime(true) - $startTime;
77+
$trackingEvent->cached = $searchResults->getIsCached();
78+
$this->trackingService->addEvent($trackingEvent);
79+
5980
return new ClientResponse($response);
6081
}
6182
}

src/Tracking/ClientFactory.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
use BluePsyduck\Ga4MeasurementProtocol\Client;
88
use BluePsyduck\Ga4MeasurementProtocol\ClientInterface;
99
use BluePsyduck\Ga4MeasurementProtocol\Config;
10+
use BluePsyduck\Ga4MeasurementProtocol\Serializer\Serializer;
1011
use FactorioItemBrowser\Api\Server\Constant\ConfigKey;
1112
use GuzzleHttp\Client as GuzzleClient;
1213
use GuzzleHttp\Psr7\HttpFactory;
@@ -27,11 +28,12 @@ public function __invoke(ContainerInterface $container, $requestedName, ?array $
2728

2829
$guzzleClient = new GuzzleClient();
2930
$httpFactory = new HttpFactory();
31+
$serializer = new Serializer();
3032

3133
$clientConfig = new Config();
3234
$clientConfig->measurementId = $config[ConfigKey::TRACKING_MEASUREMENT_ID];
3335
$clientConfig->apiSecret = $config[ConfigKey::TRACKING_API_SECRET];
3436

35-
return new Client($guzzleClient, $httpFactory, $httpFactory, $clientConfig);
37+
return new Client($guzzleClient, $httpFactory, $httpFactory, $serializer, $clientConfig);
3638
}
3739
}

src/Tracking/Event/RequestEvent.php

Lines changed: 12 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -4,73 +4,67 @@
44

55
namespace FactorioItemBrowser\Api\Server\Tracking\Event;
66

7+
use BluePsyduck\Ga4MeasurementProtocol\Attribute\Event;
8+
use BluePsyduck\Ga4MeasurementProtocol\Attribute\Parameter;
79
use BluePsyduck\Ga4MeasurementProtocol\Request\Event\EventInterface;
810

911
/**
1012
* The event representing a request to the API.
1113
*
1214
* @author BluePsyduck <bluepsyduck@gmx.com>
1315
* @license http://opensource.org/licenses/GPL-3.0 GPL v3
16+
*
17+
* @codeCoverageIgnore -- Should not need any coverage to begin with
1418
*/
19+
#[Event('request')]
1520
class RequestEvent implements EventInterface
1621
{
1722
/**
1823
* The name of the agent which initiated the request.
1924
* @var string|null
2025
*/
26+
#[Parameter('agent_name')]
2127
public ?string $agentName = null;
2228

2329
/**
2430
* The name of the matched route for the request.
2531
* @var string|null
2632
*/
33+
#[Parameter('route_name')]
2734
public ?string $routeName = null;
2835

2936
/**
3037
* The locale used for the request.
3138
* @var string|null
3239
*/
40+
#[Parameter('locale')]
3341
public ?string $locale = null;
3442

3543
/**
3644
* The runtime of the request, in milliseconds.
3745
* @var float|null
3846
*/
47+
#[Parameter('runtime')]
3948
public ?float $runtime = null;
4049

4150
/**
4251
* The status code the request resulted in.
4352
* @var int|null
4453
*/
54+
#[Parameter('status_code')]
4555
public ?int $statusCode = null;
4656

4757
/**
4858
* The id of the combination of the request.
4959
* @var string|null
5060
*/
61+
#[Parameter('combination_id')]
5162
public ?string $combinationId = null;
5263

5364
/**
5465
* The number of mods contained in the requested combination.
5566
* @var int|null
5667
*/
68+
#[Parameter('mod_count')]
5769
public ?int $modCount = null;
58-
59-
public function getName(): string
60-
{
61-
return 'request';
62-
}
63-
64-
public function getParams(): array
65-
{
66-
return array_filter([
67-
'agent_name' => $this->agentName,
68-
'route_name' => $this->routeName,
69-
'locale' => $this->locale,
70-
'runtime' => $this->runtime,
71-
'status_code' => $this->statusCode,
72-
'combination_id' => $this->combinationId,
73-
'mod_count' => $this->modCount,
74-
], fn($v) => !is_null($v));
75-
}
7670
}

src/Tracking/Event/SearchEvent.php

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace FactorioItemBrowser\Api\Server\Tracking\Event;
6+
7+
use BluePsyduck\Ga4MeasurementProtocol\Attribute\Event;
8+
use BluePsyduck\Ga4MeasurementProtocol\Attribute\Parameter;
9+
use BluePsyduck\Ga4MeasurementProtocol\Request\Event\EventInterface;
10+
11+
/**
12+
* The event representing a search.
13+
*
14+
* @author BluePsyduck <bluepsyduck@gmx.com>
15+
* @license http://opensource.org/licenses/GPL-3.0 GPL v3
16+
*
17+
* @codeCoverageIgnore -- Should not need any coverage to begin with
18+
*/
19+
#[Event('search')]
20+
class SearchEvent implements EventInterface
21+
{
22+
/**
23+
* The locale used for the search.
24+
* @var string|null
25+
*/
26+
#[Parameter('locale')]
27+
public ?string $locale = null;
28+
29+
/**
30+
* The id of the combination used for the search.
31+
* @var string|null
32+
*/
33+
#[Parameter('combination_id')]
34+
public ?string $combinationId = null;
35+
36+
/**
37+
* The number of mods contained in the combination.
38+
* @var int|null
39+
*/
40+
#[Parameter('mod_count')]
41+
public ?int $modCount = null;
42+
43+
/**
44+
* The query string used for the search.
45+
* @var string|null
46+
*/
47+
#[Parameter('query_string')]
48+
public ?string $queryString = null;
49+
50+
/**
51+
* Whether the search results were already cached.
52+
* @var bool|null
53+
*/
54+
#[Parameter('cached')]
55+
public ?bool $cached = null;
56+
57+
/**
58+
* The number of results returned by the search.
59+
* @var int|null
60+
*/
61+
#[Parameter('result_count')]
62+
public ?int $resultCount = null;
63+
64+
/**
65+
* @var float|null The runtime of the search, in milliseconds.
66+
*/
67+
#[Parameter('runtime')]
68+
public ?float $runtime = null;
69+
}

test/src/Handler/Search/SearchQueryHandlerTest.php

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,17 @@
77
use FactorioItemBrowser\Api\Client\Transfer\GenericEntityWithRecipes;
88
use FactorioItemBrowser\Api\Client\Request\Search\SearchQueryRequest;
99
use FactorioItemBrowser\Api\Client\Response\Search\SearchQueryResponse;
10+
use FactorioItemBrowser\Api\Database\Entity\Combination;
11+
use FactorioItemBrowser\Api\Database\Entity\Mod;
1012
use FactorioItemBrowser\Api\Search\Collection\PaginatedResultCollection;
1113
use FactorioItemBrowser\Api\Search\Entity\Query;
1214
use FactorioItemBrowser\Api\Search\Entity\Result\ResultInterface;
1315
use FactorioItemBrowser\Api\Search\SearchManagerInterface;
1416
use FactorioItemBrowser\Api\Server\Handler\Search\SearchQueryHandler;
1517
use FactorioItemBrowser\Api\Server\Response\ClientResponse;
1618
use FactorioItemBrowser\Api\Server\Service\SearchDecoratorService;
19+
use FactorioItemBrowser\Api\Server\Service\TrackingService;
20+
use FactorioItemBrowser\Api\Server\Tracking\Event\SearchEvent;
1721
use PHPUnit\Framework\MockObject\MockObject;
1822
use PHPUnit\Framework\TestCase;
1923
use Psr\Http\Message\ServerRequestInterface;
@@ -32,11 +36,14 @@ class SearchQueryHandlerTest extends TestCase
3236
private SearchDecoratorService $searchDecoratorService;
3337
/** @var SearchManagerInterface&MockObject */
3438
private SearchManagerInterface $searchManager;
39+
/** @var TrackingService&MockObject */
40+
private TrackingService $trackingService;
3541

3642
protected function setUp(): void
3743
{
3844
$this->searchDecoratorService = $this->createMock(SearchDecoratorService::class);
3945
$this->searchManager = $this->createMock(SearchManagerInterface::class);
46+
$this->trackingService = $this->createMock(TrackingService::class);
4047
}
4148

4249
/**
@@ -51,6 +58,7 @@ private function createInstance(array $mockedMethods = []): SearchQueryHandler
5158
->setConstructorArgs([
5259
$this->searchDecoratorService,
5360
$this->searchManager,
61+
$this->trackingService,
5462
])
5563
->getMock();
5664
}
@@ -65,6 +73,9 @@ public function testHandle(): void
6573
$locale = 'def';
6674
$countResults = 7331;
6775

76+
$combination = new Combination();
77+
$combination->getMods()->add(new Mod());
78+
$combination->getMods()->add(new Mod());
6879
$searchQuery = $this->createMock(Query::class);
6980

7081
$currentSearchResults = [
@@ -96,11 +107,26 @@ public function testHandle(): void
96107
$searchResults->expects($this->once())
97108
->method('count')
98109
->willReturn($countResults);
110+
$searchResults->expects($this->any())
111+
->method('getIsCached')
112+
->willReturn(true);
113+
114+
$expectedEvent = new SearchEvent();
115+
$expectedEvent->combinationId = $combinationId;
116+
$expectedEvent->modCount = 2;
117+
$expectedEvent->locale = $locale;
118+
$expectedEvent->queryString = $query;
119+
$expectedEvent->resultCount = $countResults;
120+
$expectedEvent->cached = true;
99121

100122
$request = $this->createMock(ServerRequestInterface::class);
101123
$request->expects($this->once())
102124
->method('getParsedBody')
103125
->willReturn($clientRequest);
126+
$request->expects($this->once())
127+
->method('getAttribute')
128+
->with($this->identicalTo(Combination::class))
129+
->willReturn($combination);
104130

105131
$this->searchManager->expects($this->once())
106132
->method('parseQuery')
@@ -123,6 +149,16 @@ public function testHandle(): void
123149
)
124150
->willReturn($decoratedSearchResults);
125151

152+
$this->trackingService->expects($this->once())
153+
->method('addEvent')
154+
->with($this->callback(function (SearchEvent $event) use ($expectedEvent): bool {
155+
$this->assertGreaterThan(0, $event->runtime);
156+
$event->runtime = null;
157+
158+
$this->assertEquals($expectedEvent, $event);
159+
return true;
160+
}));
161+
126162
$instance = $this->createInstance();
127163
$result = $instance->handle($request);
128164

test/src/Middleware/AuthorizationMiddlewareTest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
*
2121
* @author BluePsyduck <bluepsyduck@gmx.com>
2222
* @license http://opensource.org/licenses/GPL-3.0 GPL v3
23-
* @coversDefaultClass \FactorioItemBrowser\Api\Server\Middleware\AuthorizationMiddleware
23+
* @covers \FactorioItemBrowser\Api\Server\Middleware\AuthorizationMiddleware
2424
*/
2525
class AuthorizationMiddlewareTest extends TestCase
2626
{

0 commit comments

Comments
 (0)