-
Notifications
You must be signed in to change notification settings - Fork 14
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: add TypesenseSourceReader class with pagination support and cor…
…responding tests
- Loading branch information
Showing
2 changed files
with
128 additions
and
0 deletions.
There are no files selected for viewing
51 changes: 51 additions & 0 deletions
51
library/ExternalContent/SourceReaders/TypesenseSourceReader.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
<?php | ||
|
||
namespace Municipio\ExternalContent\SourceReaders; | ||
|
||
use Municipio\ExternalContent\SourceReaders\HttpApi\ApiGET; | ||
use Municipio\ExternalContent\SourceReaders\HttpApi\ApiResponse; | ||
|
||
/** | ||
* Class TypesenseSourceReader | ||
*/ | ||
class TypesenseSourceReader implements SourceReaderInterface | ||
{ | ||
/** | ||
* Constructor. | ||
*/ | ||
public function __construct(private ApiGET $api, private string $endpoint) | ||
{ | ||
} | ||
|
||
/** | ||
* @inheritDoc | ||
*/ | ||
public function getSourceData(): array | ||
{ | ||
$data = []; | ||
$page = 1; | ||
|
||
while ($response = $this->fetchPageData($page)) { | ||
$data[] = $response; | ||
$page++; | ||
} | ||
|
||
return $data; | ||
} | ||
|
||
/** | ||
* Fetches data for a specific page from the API. | ||
* | ||
* @param int $page The page number to fetch data for. | ||
* @return ApiResponse|null The API response object or null if the request fails. | ||
*/ | ||
private function fetchPageData(int $page): ?ApiResponse | ||
{ | ||
$endpointContainsGetParams = strpos($this->endpoint, '?') !== false; | ||
$endpoint = $this->endpoint . ($endpointContainsGetParams ? '&' : '?') . 'page=' . $page; | ||
|
||
$apiResponse = $this->api->get($endpoint); | ||
|
||
return $apiResponse->getStatusCode() === 200 ? $apiResponse : null; | ||
} | ||
} |
77 changes: 77 additions & 0 deletions
77
library/ExternalContent/SourceReaders/TypesenseSourceReader.test.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
<?php | ||
|
||
namespace Municipio\ExternalContent\SourceReaders; | ||
|
||
use Municipio\ExternalContent\SourceReaders\HttpApi\ApiGET; | ||
use Municipio\ExternalContent\SourceReaders\HttpApi\ApiResponse; | ||
use PHPUnit\Framework\MockObject\MockObject; | ||
use PHPUnit\Framework\TestCase; | ||
|
||
class TypesenseSourceReaderTest extends TestCase | ||
{ | ||
/** | ||
* @testdox class can be instantiated | ||
*/ | ||
public function testCanBeInstantiated() | ||
{ | ||
$typesenseSourceReader = new TypesenseSourceReader($this->getApiMock(), 'end/point'); | ||
$this->assertInstanceOf(TypesenseSourceReader::class, $typesenseSourceReader); | ||
} | ||
|
||
/** | ||
* @testdox getSourceData calls api for data untill it gets a null response | ||
*/ | ||
public function testGetSourceDataCallsApiForDataUntillItGetsANullResponse() | ||
{ | ||
$api = $this->getApiMock([ $this->getApiResponseMock(), $this->getApiResponseMock([], 404) ]); | ||
$typesenseSourceReader = new TypesenseSourceReader($api, 'end/point'); | ||
|
||
$typesenseSourceReader->getSourceData(); | ||
|
||
$this->assertEquals('end/point?page=1', $api->calls[0]); | ||
$this->assertEquals('end/point?page=2', $api->calls[1]); | ||
} | ||
|
||
/** | ||
* @testdox page number is appended correctly to endpoint with already defined GET parameters | ||
*/ | ||
public function testPageNumberIsAppendedCorrectlyToEndpointWithAlreadyDefinedGetParameters() | ||
{ | ||
$api = $this->getApiMock([$this->getApiResponseMock(), $this->getApiResponseMock([], 404)]); | ||
$typesenseSourceReader = new TypesenseSourceReader($api, 'end/point?param=value'); | ||
|
||
$typesenseSourceReader->getSourceData(); | ||
|
||
$this->assertEquals('end/point?param=value&page=1', $api->calls[0]); | ||
$this->assertEquals('end/point?param=value&page=2', $api->calls[1]); | ||
} | ||
|
||
private function getApiMock(array $consecutiveReturns = []): ApiGET | ||
{ | ||
return new class ($consecutiveReturns) implements ApiGET | ||
{ | ||
public array $calls = []; | ||
private $nbrOfCalls = 0; | ||
|
||
public function __construct(private array $consecutiveReturns) | ||
{ | ||
} | ||
|
||
public function get(string $endpoint): ApiResponse | ||
{ | ||
$this->calls[] = $endpoint; | ||
return $this->consecutiveReturns[$this->nbrOfCalls++]; | ||
} | ||
}; | ||
} | ||
|
||
private function getApiResponseMock(array $body = [], int $statusCode = 200, array $headers = []): ApiResponse|MockObject | ||
{ | ||
$apiResponse = $this->createMock(ApiResponse::class); | ||
$apiResponse->method('getBody')->willReturn($body); | ||
$apiResponse->method('getStatusCode')->willReturn($statusCode); | ||
$apiResponse->method('getHeaders')->willReturn($headers); | ||
|
||
return $apiResponse; | ||
} | ||
} |