2017-02-07 13:11:42 -05:00
|
|
|
<?php declare(strict_types=1);
|
|
|
|
/**
|
2017-02-15 16:13:32 -05:00
|
|
|
* Hummingbird Anime List Client
|
2017-02-07 13:11:42 -05:00
|
|
|
*
|
|
|
|
* An API client for Kitsu and MyAnimeList to manage anime and manga watch lists
|
|
|
|
*
|
|
|
|
* PHP version 7
|
|
|
|
*
|
2017-02-15 16:13:32 -05:00
|
|
|
* @package HummingbirdAnimeClient
|
2017-02-07 13:11:42 -05:00
|
|
|
* @author Timothy J. Warren <tim@timshomepage.net>
|
|
|
|
* @copyright 2015 - 2017 Timothy J. Warren
|
|
|
|
* @license http://www.opensource.org/licenses/mit-license.html MIT License
|
|
|
|
* @version 4.0
|
2017-03-07 20:53:58 -05:00
|
|
|
* @link https://git.timshomepage.net/timw4mail/HummingBirdAnimeClient
|
2017-02-07 13:11:42 -05:00
|
|
|
*/
|
|
|
|
|
|
|
|
namespace Aviat\AnimeClient\API;
|
|
|
|
|
2017-12-08 22:32:00 -05:00
|
|
|
use function Amp\Promise\wait;
|
|
|
|
|
2017-02-08 15:48:20 -05:00
|
|
|
use Amp;
|
2017-03-20 19:08:33 -04:00
|
|
|
use Amp\Artax\{FormBody, Request};
|
2017-02-08 15:48:20 -05:00
|
|
|
use Aviat\Ion\Json;
|
2017-02-07 13:11:42 -05:00
|
|
|
use InvalidArgumentException;
|
|
|
|
use Psr\Log\LoggerAwareTrait;
|
|
|
|
|
|
|
|
/**
|
2017-12-08 22:32:00 -05:00
|
|
|
* Wrapper around Artax to make it easier to build API requests
|
2017-02-07 13:11:42 -05:00
|
|
|
*/
|
|
|
|
class APIRequestBuilder {
|
|
|
|
use LoggerAwareTrait;
|
2017-02-08 00:44:57 -05:00
|
|
|
|
2017-02-07 13:11:42 -05:00
|
|
|
/**
|
|
|
|
* Url prefix for making url requests
|
|
|
|
* @var string
|
|
|
|
*/
|
|
|
|
protected $baseUrl = '';
|
2017-02-08 00:44:57 -05:00
|
|
|
|
2017-02-07 13:11:42 -05:00
|
|
|
/**
|
|
|
|
* Url path of the request
|
|
|
|
* @var string
|
|
|
|
*/
|
|
|
|
protected $path = '';
|
2017-02-08 00:44:57 -05:00
|
|
|
|
2017-02-07 13:11:42 -05:00
|
|
|
/**
|
|
|
|
* Query string for the request
|
|
|
|
* @var string
|
|
|
|
*/
|
|
|
|
protected $query = '';
|
2017-02-08 00:44:57 -05:00
|
|
|
|
2017-02-07 13:11:42 -05:00
|
|
|
/**
|
|
|
|
* Default request headers
|
|
|
|
* @var array
|
|
|
|
*/
|
|
|
|
protected $defaultHeaders = [];
|
2017-02-08 00:44:57 -05:00
|
|
|
|
2017-02-07 13:11:42 -05:00
|
|
|
/**
|
|
|
|
* Valid HTTP request methos
|
|
|
|
* @var array
|
|
|
|
*/
|
|
|
|
protected $validMethods = ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS'];
|
2017-02-08 00:44:57 -05:00
|
|
|
|
2017-02-07 13:11:42 -05:00
|
|
|
/**
|
|
|
|
* The current request
|
2017-12-08 22:32:00 -05:00
|
|
|
* @var \Amp\Artax\Request
|
2017-02-07 13:11:42 -05:00
|
|
|
*/
|
|
|
|
protected $request;
|
2017-02-08 00:44:57 -05:00
|
|
|
|
2017-02-08 15:48:20 -05:00
|
|
|
/**
|
|
|
|
* Set an authorization header
|
|
|
|
*
|
|
|
|
* @param string $type The type of authorization, eg, basic, bearer, etc.
|
|
|
|
* @param string $value The authorization value
|
|
|
|
* @return self
|
|
|
|
*/
|
|
|
|
public function setAuth(string $type, string $value): self
|
|
|
|
{
|
|
|
|
$authString = ucfirst($type) . ' ' . $value;
|
|
|
|
$this->setHeader('Authorization', $authString);
|
|
|
|
|
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
2017-02-07 13:11:42 -05:00
|
|
|
/**
|
2017-02-08 00:44:57 -05:00
|
|
|
* Set a basic authentication header
|
|
|
|
*
|
|
|
|
* @param string $username
|
|
|
|
* @param string $password
|
2017-02-07 13:11:42 -05:00
|
|
|
* @return self
|
|
|
|
*/
|
2017-02-08 00:44:57 -05:00
|
|
|
public function setBasicAuth(string $username, string $password): self
|
2017-02-07 13:11:42 -05:00
|
|
|
{
|
2017-02-08 15:48:20 -05:00
|
|
|
$this->setAuth('basic', base64_encode($username . ':' . $password));
|
2017-02-07 13:11:42 -05:00
|
|
|
return $this;
|
|
|
|
}
|
2017-02-08 00:44:57 -05:00
|
|
|
|
2017-02-07 13:11:42 -05:00
|
|
|
/**
|
|
|
|
* Set the request body
|
|
|
|
*
|
|
|
|
* @param FormBody|string $body
|
2017-12-08 22:32:00 -05:00
|
|
|
* @throws \TypeError
|
2017-02-07 13:11:42 -05:00
|
|
|
* @return self
|
|
|
|
*/
|
|
|
|
public function setBody($body): self
|
|
|
|
{
|
2017-12-08 22:32:00 -05:00
|
|
|
$this->request = $this->request->withBody($body);
|
2017-02-07 13:11:42 -05:00
|
|
|
return $this;
|
|
|
|
}
|
2017-02-08 00:44:57 -05:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Set body as form fields
|
|
|
|
*
|
|
|
|
* @param array $fields Mapping of field names to values
|
2017-12-08 22:32:00 -05:00
|
|
|
* @throws \TypeError
|
2017-02-08 00:44:57 -05:00
|
|
|
* @return self
|
|
|
|
*/
|
|
|
|
public function setFormFields(array $fields): self
|
|
|
|
{
|
2017-12-08 22:32:00 -05:00
|
|
|
$body = new FormBody();
|
|
|
|
$body->addFields($fields);
|
|
|
|
|
|
|
|
return $this->setBody($body);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Unset a request header
|
|
|
|
*
|
|
|
|
* @param string $name
|
|
|
|
* @return self
|
|
|
|
*/
|
|
|
|
public function unsetHeader(string $name): self
|
|
|
|
{
|
|
|
|
$this->request = $this->request->withoutHeader($name);
|
2017-02-08 00:44:57 -05:00
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
2017-02-07 13:11:42 -05:00
|
|
|
/**
|
|
|
|
* Set a request header
|
|
|
|
*
|
|
|
|
* @param string $name
|
|
|
|
* @param string $value
|
|
|
|
* @return self
|
|
|
|
*/
|
2017-12-08 22:32:00 -05:00
|
|
|
public function setHeader(string $name, string $value = NULL): self
|
2017-02-07 13:11:42 -05:00
|
|
|
{
|
2017-12-08 22:32:00 -05:00
|
|
|
if (NULL === $value)
|
|
|
|
{
|
|
|
|
$this->unsetHeader($name);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
$this->request = $this->request->withHeader($name, $value);
|
|
|
|
}
|
|
|
|
|
2017-02-07 13:11:42 -05:00
|
|
|
return $this;
|
|
|
|
}
|
2017-02-08 00:44:57 -05:00
|
|
|
|
2017-02-07 13:11:42 -05:00
|
|
|
/**
|
|
|
|
* Set multiple request headers
|
2017-02-08 00:44:57 -05:00
|
|
|
*
|
2017-02-07 13:11:42 -05:00
|
|
|
* name => value
|
|
|
|
*
|
|
|
|
* @param array $headers
|
|
|
|
* @return self
|
|
|
|
*/
|
|
|
|
public function setHeaders(array $headers): self
|
|
|
|
{
|
|
|
|
foreach ($headers as $name => $value)
|
|
|
|
{
|
|
|
|
$this->setHeader($name, $value);
|
|
|
|
}
|
2017-02-08 00:44:57 -05:00
|
|
|
|
2017-02-07 13:11:42 -05:00
|
|
|
return $this;
|
|
|
|
}
|
2017-02-08 00:44:57 -05:00
|
|
|
|
2017-02-08 15:48:20 -05:00
|
|
|
/**
|
|
|
|
* Set the request body
|
|
|
|
*
|
2017-02-09 20:10:13 -05:00
|
|
|
* @param mixed $body
|
2017-12-08 22:32:00 -05:00
|
|
|
* @throws \TypeError
|
2017-02-08 15:48:20 -05:00
|
|
|
* @return self
|
|
|
|
*/
|
2017-02-09 20:10:13 -05:00
|
|
|
public function setJsonBody($body): self
|
2017-02-08 15:48:20 -05:00
|
|
|
{
|
2017-02-09 20:10:13 -05:00
|
|
|
$requestBody = ( ! is_scalar($body))
|
|
|
|
? Json::encode($body)
|
|
|
|
: $body;
|
2017-12-08 22:32:00 -05:00
|
|
|
|
2017-02-08 15:48:20 -05:00
|
|
|
return $this->setBody($requestBody);
|
|
|
|
}
|
|
|
|
|
2017-02-07 13:11:42 -05:00
|
|
|
/**
|
|
|
|
* Append a query string in array format
|
|
|
|
*
|
|
|
|
* @param array $params
|
|
|
|
* @return self
|
|
|
|
*/
|
|
|
|
public function setQuery(array $params): self
|
|
|
|
{
|
2017-02-08 00:44:57 -05:00
|
|
|
$this->query = http_build_query($params);
|
2017-02-07 13:11:42 -05:00
|
|
|
return $this;
|
|
|
|
}
|
2017-02-08 00:44:57 -05:00
|
|
|
|
2017-02-07 13:11:42 -05:00
|
|
|
/**
|
|
|
|
* Return the promise for the current request
|
|
|
|
*
|
2017-12-08 22:32:00 -05:00
|
|
|
* @throws \Throwable
|
|
|
|
* @return \Amp\Artax\Request
|
2017-02-07 13:11:42 -05:00
|
|
|
*/
|
2017-12-08 22:32:00 -05:00
|
|
|
public function getFullRequest(): Request
|
2017-02-07 13:11:42 -05:00
|
|
|
{
|
|
|
|
$this->buildUri();
|
2017-02-08 15:48:20 -05:00
|
|
|
|
2017-02-08 00:44:57 -05:00
|
|
|
if ($this->logger)
|
|
|
|
{
|
|
|
|
$this->logger->debug('API Request', [
|
|
|
|
'request_url' => $this->request->getUri(),
|
2017-12-08 22:32:00 -05:00
|
|
|
'request_headers' => $this->request->getHeaders(),
|
|
|
|
'request_body' => wait(
|
|
|
|
$this->request->getBody()
|
|
|
|
->createBodyStream()
|
|
|
|
->read()
|
|
|
|
)
|
2017-02-08 00:44:57 -05:00
|
|
|
]);
|
|
|
|
}
|
2017-02-08 15:48:20 -05:00
|
|
|
|
2017-02-07 13:11:42 -05:00
|
|
|
return $this->request;
|
|
|
|
}
|
2017-02-08 00:44:57 -05:00
|
|
|
|
2017-02-07 13:11:42 -05:00
|
|
|
/**
|
|
|
|
* Create a new http request
|
|
|
|
*
|
|
|
|
* @param string $type
|
|
|
|
* @param string $uri
|
2017-02-17 10:55:17 -05:00
|
|
|
* @throws InvalidArgumentException
|
2017-02-07 13:11:42 -05:00
|
|
|
* @return self
|
|
|
|
*/
|
|
|
|
public function newRequest(string $type, string $uri): self
|
|
|
|
{
|
2017-12-08 22:32:00 -05:00
|
|
|
if ( ! \in_array($type, $this->validMethods, TRUE))
|
2017-02-07 13:11:42 -05:00
|
|
|
{
|
2017-12-08 22:32:00 -05:00
|
|
|
throw new InvalidArgumentException('Invalid HTTP method');
|
2017-02-07 13:11:42 -05:00
|
|
|
}
|
2017-02-08 00:44:57 -05:00
|
|
|
|
2017-12-08 22:32:00 -05:00
|
|
|
$realUrl = (strpos($uri, '//') !== FALSE)
|
|
|
|
? $uri
|
|
|
|
: $this->baseUrl . $uri;
|
2017-02-08 15:48:20 -05:00
|
|
|
|
2017-12-08 22:32:00 -05:00
|
|
|
$this->resetState($realUrl, $type);
|
2017-02-08 00:44:57 -05:00
|
|
|
$this->path = $uri;
|
2017-02-08 15:48:20 -05:00
|
|
|
|
2017-12-08 22:32:00 -05:00
|
|
|
// Actually create the full url!
|
|
|
|
$this->buildUri();
|
|
|
|
|
2017-02-08 00:44:57 -05:00
|
|
|
if ( ! empty($this->defaultHeaders))
|
|
|
|
{
|
|
|
|
$this->setHeaders($this->defaultHeaders);
|
2017-02-08 15:48:20 -05:00
|
|
|
}
|
2017-02-08 00:44:57 -05:00
|
|
|
|
2017-02-07 13:11:42 -05:00
|
|
|
return $this;
|
|
|
|
}
|
2017-02-08 00:44:57 -05:00
|
|
|
|
2017-02-07 13:11:42 -05:00
|
|
|
/**
|
|
|
|
* Create the full request url
|
|
|
|
*
|
2017-12-08 22:32:00 -05:00
|
|
|
* @return Request
|
2017-02-07 13:11:42 -05:00
|
|
|
*/
|
2017-12-08 22:32:00 -05:00
|
|
|
private function buildUri(): Request
|
2017-02-07 13:11:42 -05:00
|
|
|
{
|
|
|
|
$url = (strpos($this->path, '//') !== FALSE)
|
|
|
|
? $this->path
|
2017-02-08 00:44:57 -05:00
|
|
|
: $this->baseUrl . $this->path;
|
2017-02-07 13:11:42 -05:00
|
|
|
|
|
|
|
if ( ! empty($this->query))
|
|
|
|
{
|
|
|
|
$url .= '?' . $this->query;
|
|
|
|
}
|
2017-02-08 00:44:57 -05:00
|
|
|
|
2017-12-08 22:32:00 -05:00
|
|
|
$this->request = $this->request->withUri($url);
|
|
|
|
|
|
|
|
return $this->request;
|
2017-02-07 13:11:42 -05:00
|
|
|
}
|
2017-02-08 00:44:57 -05:00
|
|
|
|
2017-02-07 13:11:42 -05:00
|
|
|
/**
|
|
|
|
* Reset the class state for a new request
|
|
|
|
*
|
2017-12-08 22:32:00 -05:00
|
|
|
* @param string $url
|
|
|
|
* @param string $type
|
2017-02-07 13:11:42 -05:00
|
|
|
* @return void
|
|
|
|
*/
|
2017-12-08 22:32:00 -05:00
|
|
|
private function resetState($url, $type = 'GET')
|
2017-02-07 13:11:42 -05:00
|
|
|
{
|
2017-12-08 22:32:00 -05:00
|
|
|
$requestUrl = $url ?: $this->baseUrl;
|
|
|
|
|
2017-02-07 13:11:42 -05:00
|
|
|
$this->path = '';
|
|
|
|
$this->query = '';
|
2017-12-08 22:32:00 -05:00
|
|
|
$this->request = (new Request($requestUrl))
|
|
|
|
->withMethod($type);
|
2017-02-07 13:11:42 -05:00
|
|
|
}
|
|
|
|
}
|