namespace seekat;
-use AsyncInterop\Promise;
use Countable;
use Generator;
-use http\{
- Client, Client\Request, Message\Body, QueryString, Url
-};
+use http\{Client, Client\Request, Message\Body, QueryString, Url};
+use Iterator;
use IteratorAggregate;
-use Psr\Log\{
- LoggerInterface, NullLogger
-};
-use seekat\API\{
- Call, Consumer, ContentType, Future, Iterator, Links
-};
+use Psr\Log\{LoggerInterface, NullLogger};
+use seekat\API\{Call, Consumer, ContentType, Future, Links};
use seekat\Exception\InvalidArgumentException;
class API implements IteratorAggregate, Countable {
+ /**
+ * API version
+ * @var int
+ */
+ private $version = 3;
+
/**
* The current API endpoint URL
* @var Url
/**
* Create a new API endpoint root
*
+ * @codeCoverageIgnore
+ *
* @param Future $future pretending to fulfill promises
* @param array $headers Standard request headers, defaults to ["Accept" => "application/vnd.github.v3+json"]
* @param Url $url The API's endpoint, defaults to https://api.github.com
$this->logger = $log ?? new NullLogger;
$this->url = $url ?? new Url("https://api.github.com");
$this->client = $client ?? new Client;
+ $this->type = new ContentType($this->version, "json");
$this->headers = (array) $headers + [
- "Accept" => "application/vnd.github.v3+json"
+ "Accept" => $this->type->getContentType()
];
}
(string) $this->url,
(string) $that->url
],
+ "data" => $that->data
]);
return $that;
*
* @param string $method The API's "path" element to ascend into
* @param array $args Array of arguments forwarded to \seekat\API::get()
- * @return Promise
+ * @return mixed promise
*/
- function __call(string $method, array $args) : Promise {
+ function __call(string $method, array $args) {
/* We cannot implement an explicit then() method,
* because the Promise implementation might think
* we're actually implementing Thenable,
* which might cause an infinite loop.
- * FIXXME: then/when
*/
- if ($method === "when") {
- $promise = $this->get();
- $promise->when(...$args);
- return $promise;
- }
-
+ if ($method === "then"
/*
* very short-hand version:
- * ->users->m6w6->gists->get()->when(...)
+ * ->users->m6w6->gists->get()->then(...)
* vs:
* ->users->m6w6->gists(...)
*/
- if (is_callable(current($args))) {
- $promise = $this->get();
- $promise->when(current($args));
- return $promise;
+ || is_callable(current($args))) {
+ return $this->future->handlePromise($this->get(), ...$args);
}
return (new Call($this, $method))($args);
* Run the send loop through a generator
*
* @param callable|Generator $cbg A \Generator or a factory of a \Generator yielding promises
- * @return Promise The promise of the generator's return value
+ * @return mixed The promise of the generator's return value
* @throws InvalidArgumentException
*/
- function __invoke($cbg) : Promise {
+ function __invoke($cbg) {
$this->logger->debug(__FUNCTION__);
$consumer = new Consumer($this->getFuture(), function() {
- $this->client->send();
+ $this->client->send();
});
invoke:
* @return string
*/
function __toString() : string {
- if (is_scalar($this->data)) {
- return (string) $this->data;
- }
-
- /* FIXME */
- return json_encode($this->data);
+ return (string) $this->type->encode($this->data);
}
/**
* @return Iterator
*/
function getIterator() : Iterator {
- return new Iterator($this);
+ foreach ($this->data as $key => $cur) {
+ if ($this->__get($key)->exists("url", $url)) {
+ $url = new Url($url);
+ $val = $this->withUrl($url)->withData($cur);
+ } else {
+ $val = $this->__get($key)->withData($cur);
+ }
+ yield $key => $val;
+ }
}
/**
* @return int
*/
function count() : int {
- return count($this->data);
+ if (is_array($this->data)) {
+ $count = count($this->data);
+ } else if ($this->data instanceof Countable) {
+ $count = count($this->data);
+ } else if (is_object($this->data)) {
+ $count = count((array) $this->data);
+ } else {
+ $count = 0;
+ }
+ $this->logger->debug("count()", [
+ "of type" => typeof($this->data),
+ "count" => $count
+ ]);
+ return $count;
}
/**
return $this->links;
}
+ /**
+ * @return int
+ */
+ function getVersion() : int {
+ return $this->version;
+ }
+
/**
* Export the endpoint's underlying data
*
* @return API clone
*/
function as(string $type, bool $keepdata = true) : API {
- $that = ContentType::apply($this, $type);
+ $ct = new ContentType($this->version, $type);
+
+ $that = $ct->apply($this);
+ $that->type = $ct;
+
if (!$keepdata) {
$that->data = null;
}
*
* @param mixed $args The HTTP query string parameters
* @param array $headers The request's additional HTTP headers
- * @return Promise
+ * @return mixed promise
*/
- function head($args = null, array $headers = null, $cache = null) : Promise {
+ function head($args = null, array $headers = null, $cache = null) {
return $this->request("HEAD", $args, null, $headers, $cache);
}
*
* @param mixed $args The HTTP query string parameters
* @param array $headers The request's additional HTTP headers
- * @return Promise
+ * @return mixed promise
*/
- function get($args = null, array $headers = null, $cache = null) : Promise {
+ function get($args = null, array $headers = null, $cache = null) {
return $this->request("GET", $args, null, $headers, $cache);
}
*
* @param mixed $args The HTTP query string parameters
* @param array $headers The request's additional HTTP headers
- * @return Promise
+ * @return mixed promise
*/
- function delete($args = null, array $headers = null) : Promise {
+ function delete($args = null, array $headers = null) {
return $this->request("DELETE", $args, null, $headers);
}
* @param mixed $body The HTTP message's body
* @param mixed $args The HTTP query string parameters
* @param array $headers The request's additional HTTP headers
- * @return Promise
+ * @return mixed promise
*/
- function post($body = null, $args = null, array $headers = null) : Promise {
+ function post($body = null, $args = null, array $headers = null) {
return $this->request("POST", $args, $body, $headers);
}
* @param mixed $body The HTTP message's body
* @param mixed $args The HTTP query string parameters
* @param array $headers The request's additional HTTP headers
- * @return Promise
+ * @return mixed promise
*/
- function put($body = null, $args = null, array $headers = null) : Promise {
+ function put($body = null, $args = null, array $headers = null) {
return $this->request("PUT", $args, $body, $headers);
}
* @param mixed $body The HTTP message's body
* @param mixed $args The HTTP query string parameters
* @param array $headers The request's additional HTTP headers
- * @return Promise
+ * @return mixed promise
*/
- function patch($body = null, $args = null, array $headers = null) : Promise {
+ function patch($body = null, $args = null, array $headers = null) {
return $this->request("PATCH", $args, $body, $headers);
}
$seg, typeof($this->data, false), $exists ? "true" : "false"
), [
"url" => (string) $this->url,
- "val" => $val,
+ "val" => typeof($val, false),
]);
return $exists;
* @param mixed $body Thee HTTP message's body
* @param array $headers The request's additional HTTP headers
* @param Call\Cache\Service $cache
- * @return Promise
+ * @return mixed promise
*/
- private function request(string $method, $args = null, $body = null, array $headers = null, Call\Cache\Service $cache = null) : Promise {
+ private function request(string $method, $args = null, $body = null, array $headers = null, Call\Cache\Service $cache = null) {
if (isset($this->data)) {
$this->logger->debug("request -> resolve", [
"method" => $method,
$url = $this->url->mod(["query" => new QueryString($args)]);
$request = new Request($method, $url, ((array) $headers) + $this->headers,
- $body = is_array($body) ? json_encode($body) : (
- is_resource($body) ? new Body($body) : (
- is_scalar($body) ? (new Body)->append($body) :
- $body)));
+ $body = $this->type->encode(is_resource($body) ? new Body($body) : $body));
$this->logger->info("request -> deferred", [
"method" => $method,