diff --git a/src/Client.php b/src/Client.php index 9db12b4..1f432ee 100644 --- a/src/Client.php +++ b/src/Client.php @@ -21,7 +21,7 @@ use Link0\Bunq\Middleware\ResponseSignatureMiddleware; use Psr\Http\Message\ResponseInterface; -final class Client +class Client { /** * @var GuzzleClient @@ -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, [ @@ -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, [ @@ -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, [ @@ -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': diff --git a/src/PaginatedResponse.php b/src/PaginatedResponse.php new file mode 100644 index 0000000..2b42271 --- /dev/null +++ b/src/PaginatedResponse.php @@ -0,0 +1,188 @@ + [ + * 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 Iterator or + * Traversable + * @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
+ * An offset to check for. + *
+ * @return boolean true on success or false on failure. + * + *+ * 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
+ * The offset to retrieve. + *
+ * @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+ * The offset to assign the value to. + *
+ * @param mixed $value+ * The value to set. + *
+ * @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+ * The offset to unset. + *
+ * @return void + * @since 5.0.0 + */ + public function offsetUnset($offset) + { + throw new \LogicException("Unable to unset value on immutable object"); + } +} diff --git a/src/Service/UserService.php b/src/Service/UserService.php index f9bf179..0505f39 100644 --- a/src/Service/UserService.php +++ b/src/Service/UserService.php @@ -24,7 +24,7 @@ public function __construct(Client $client) /** * @return User[] */ - public function listUsers(): array + public function listUsers() { return $this->client->get('user'); }