Skip to content

Commit

Permalink
feat: add TypesenseSourceReader class with pagination support and cor…
Browse files Browse the repository at this point in the history
…responding tests
  • Loading branch information
thorbrink committed Feb 25, 2025
1 parent d0c8e6b commit e7a546d
Show file tree
Hide file tree
Showing 2 changed files with 128 additions and 0 deletions.
51 changes: 51 additions & 0 deletions library/ExternalContent/SourceReaders/TypesenseSourceReader.php
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;
}
}
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;
}
}

0 comments on commit e7a546d

Please sign in to comment.