Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP: Initial tryout of PaginatedResponse #10

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 9 additions & 22 deletions src/Client.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
use Link0\Bunq\Middleware\ResponseSignatureMiddleware;
use Psr\Http\Message\ResponseInterface;

final class Client
class Client
{
/**
* @var GuzzleClient
Expand Down Expand Up @@ -58,7 +58,7 @@ public function __construct(Environment $environment, Keypair $keypair, PublicKe
* @param string $endpoint
* @return array
*/
public function get(string $endpoint, array $headers = []): array
public function get(string $endpoint, array $headers = [])
{
return $this->processResponse(
$this->guzzle->request('GET', $endpoint, [
Expand All @@ -73,7 +73,7 @@ public function get(string $endpoint, array $headers = []): array
* @param array $headers
* @return array
*/
public function post(string $endpoint, array $body, array $headers = []): array
public function post(string $endpoint, array $body, array $headers = [])
{
return $this->processResponse(
$this->guzzle->request('POST', $endpoint, [
Expand All @@ -89,7 +89,7 @@ public function post(string $endpoint, array $body, array $headers = []): array
* @param array $headers
* @return array
*/
public function put(string $endpoint, array $body, array $headers = []): array
public function put(string $endpoint, array $body, array $headers = [])
{
return $this->processResponse(
$this->guzzle->request('PUT', $endpoint, [
Expand All @@ -113,30 +113,17 @@ public function delete(string $endpoint, array $headers = [])

/**
* @param ResponseInterface $response
* @return array
* @return array|PaginatedResponse
*/
private function processResponse(ResponseInterface $response): array
private function processResponse(ResponseInterface $response)
{
$contents = (string) $response->getBody();
$json = json_decode($contents, true)['Response'];

// Return empty responses
if (count($json) === 0) {
return [];
}
$json = json_decode($contents, true);

foreach ($json as $key => $value) {
if (is_numeric($key)) {
// Often only a single associative entry here
foreach ($value as $type => $struct) {
$json[$key] = $this->mapResponse($type, $struct);
}
}
}
return $json;
return new PaginatedResponse($this, $json);
}

private function mapResponse(string $key, array $value)
public function mapResponse(string $key, array $value)
{
switch ($key) {
case 'DeviceServer':
Expand Down
188 changes: 188 additions & 0 deletions src/PaginatedResponse.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
<?php declare(strict_types = 1);

namespace Link0\Bunq;

use ArrayAccess;
use InvalidArgumentException;
use IteratorAggregate;
use Traversable;

/**
* Array (
* 'Response' => [
* 0 => [
* 'Foo' => [
* 'id' => 123,
* ],
* ],
* 1 => [
* 'Foo' => [
* 'id' => 456,
* ],
* ],
*
* ],
* 'Pagination' => [
* 'future_url' => '/v1/foo?newer_id=123',
* 'newer_url' => '/v1/foo?newer_id=456',
* 'older_url' => null,
* ]
* )
*/
final class PaginatedResponse implements IteratorAggregate, ArrayAccess
{
/**
* @var Client
*/
private $client;

/**
* @var array
*/
private $list;

/**
* @var array
*/
private $pagination;

/**
* @param Client $client
*/
public function __construct(Client $client, array $body)
{
$this->client = $client;

$this->guardAndSetResponseBody($body);
$this->guardAndSetPagination($body);
}

/**
* @param array $body
* @return void
*/
private function guardAndSetResponseBody(array $body)
{
if (!isset($body['Response'])) {
throw new InvalidArgumentException("Response body should contain key 'Response'");
}
$this->list = $body['Response'];
}

/**
* @param array $body
* @return void
*/
private function guardAndSetPagination(array $body)
{
$this->pagination = [
'future_url' => null,
'newer_url' => null,
'older_url' => null,
];

if (isset($body['Pagination'])) {
$pagination = $body['Pagination'];

if (!array_key_exists('future_url', $pagination)) {
throw new InvalidArgumentException("Pagination should contain future_url");
}
if (!array_key_exists('newer_url', $pagination)) {
throw new InvalidArgumentException("Pagination should contain newer_url");
}
if (!array_key_exists('older_url', $pagination)) {
throw new InvalidArgumentException("Pagination should contain older_url");
}
$this->pagination = $pagination;
}
}

/**
* Retrieve an external iterator
* @link http://php.net/manual/en/iteratoraggregate.getiterator.php
* @return Traversable An instance of an object implementing <b>Iterator</b> or
* <b>Traversable</b>
* @since 5.0.0
*/
public function getIterator()
{
foreach ($this->list as $key => $value) {
// mapResponse takes the struct and instantiates Value Objects
yield $key => $this->client->mapResponse($key, $value);
}

if ($this->pagination['newer_url'] !== null) {
/** @var PaginatedResponse $nextPagination */
$nextPagination = $this->client->get($this->pagination['newer_url']);

foreach ($nextPagination as $newKey => $newValue) {
$this->list[] = $newValue;
unset($nextPagination->list[$newKey]);
yield $newKey => $newValue;
}
}
}

/**
* Whether a offset exists
* @link http://php.net/manual/en/arrayaccess.offsetexists.php
* @param mixed $offset <p>
* An offset to check for.
* </p>
* @return boolean true on success or false on failure.
* </p>
* <p>
* The return value will be casted to boolean if non-boolean was returned.
* @since 5.0.0
*/
public function offsetExists($offset)
{
return array_key_exists($offset, $this->list);
}

/**
* Offset to retrieve
* @link http://php.net/manual/en/arrayaccess.offsetget.php
* @param mixed $offset <p>
* The offset to retrieve.
* </p>
* @return mixed Can return all value types.
* @since 5.0.0
*/
public function offsetGet($offset)
{

return $this->list[$offset];
}

/**
* Offset to set
* @link http://php.net/manual/en/arrayaccess.offsetset.php
* @param mixed $offset <p>
* The offset to assign the value to.
* </p>
* @param mixed $value <p>
* The value to set.
* </p>
* @return void
* @since 5.0.0
*/
public function offsetSet($offset, $value)
{
throw new \LogicException("Unable to set value on immutable object");
}

/**
* Offset to unset
* @link http://php.net/manual/en/arrayaccess.offsetunset.php
* @param mixed $offset <p>
* The offset to unset.
* </p>
* @return void
* @since 5.0.0
*/
public function offsetUnset($offset)
{
throw new \LogicException("Unable to unset value on immutable object");
}
}
2 changes: 1 addition & 1 deletion src/Service/UserService.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ public function __construct(Client $client)
/**
* @return User[]
*/
public function listUsers(): array
public function listUsers()
{
return $this->client->get('user');
}
Expand Down