xabbuh/panda-client

View on GitHub
src/Api/HttplugClient.php

Summary

Maintainability
A
1 hr
Test Coverage
<?php

/*
 * This file is part of the XabbuhPandaClient package.
 *
 * (c) Christian Flothmann <christian.flothmann@xabbuh.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Xabbuh\PandaClient\Api;

use Http\Discovery\HttpClientDiscovery;
use Http\Discovery\MessageFactoryDiscovery;
use Http\Discovery\StreamFactoryDiscovery;
use Http\Message\RequestFactory;
use Http\Message\StreamFactory;
use Xabbuh\PandaClient\Exception\ApiException;
use Xabbuh\PandaClient\Exception\HttpException;
use Xabbuh\PandaClient\Signer\PandaSigner;

/**
 * Panda REST client implementation using the Httplug library.
 *
 * Sends signed requests (GET, POST, PUT or DELETE) to the Panda encoding
 * webservice.
 *
 * @author Christian Flothmann <christian.flothmann@xabbuh.de>
 * @author Christophe Coevoet <stof@notk.org>
 */
class HttplugClient implements HttpClientInterface
{
    /**
     * @var \Http\Client\HttpClient
     */
    private $httpClient;

    /**
     * @var RequestFactory
     */
    private $requestFactory;

    /**
     * @var StreamFactory
     */
    private $streamFactory;

    /**
     * Panda cloud id
     *
     * @var string
     */
    private $cloudId;

    /**
     * Panda Account
     *
     * @var Account
     */
    private $account;

    /**
     * HttplugClient constructor.
     *
     * @param \Http\Client\HttpClient|null $httpClient
     * @param RequestFactory|null          $requestFactory
     * @param StreamFactory|null           $streamFactory
     */
    public function __construct(\Http\Client\HttpClient $httpClient = null, RequestFactory $requestFactory = null, StreamFactory $streamFactory = null)
    {
        $this->httpClient = $httpClient ?: HttpClientDiscovery::find();
        $this->requestFactory = $requestFactory ?: MessageFactoryDiscovery::find();
        $this->streamFactory = $streamFactory ?: StreamFactoryDiscovery::find();
    }

    /**
     * {@inheritDoc}
     */
    public function setCloudId($cloudId)
    {
        $this->cloudId = $cloudId;
    }

    /**
     * {@inheritDoc}
     */
    public function getCloudId()
    {
        return $this->cloudId;
    }

    /**
     * {@inheritDoc}
     */
    public function setAccount(Account $account)
    {
        $this->account = $account;
    }

    /**
     * {@inheritDoc}
     */
    public function getAccount()
    {
        return $this->account;
    }

    /**
     * {@inheritDoc}
     */
    public function get($path, array $params = array())
    {
        return $this->request('GET', $path, $params);
    }

    /**
     * {@inheritDoc}
     */
    public function post($path, array $params = array())
    {
        return $this->request('POST', $path, $params);
    }

    /**
     * {@inheritDoc}
     */
    public function put($path, array $params = array())
    {
        return $this->request('PUT', $path, $params);
    }

    /**
     * {@inheritDoc}
     */
    public function delete($path, array $params = array())
    {
        return $this->request('DELETE', $path, $params);
    }

    /**
     * Send signed HTTP requests to the API server.
     *
     * @param string $method HTTP method (GET, POST, PUT or DELETE)
     * @param string $path   Request path
     * @param array  $params Additional request parameters
     *
     * @return string The API server's response
     *
     * @throws ApiException  if an API error occurs
     * @throws HttpException if the request fails
     */
    private function request($method, $path, array $params)
    {
        // sign the request parameters
        $signer = PandaSigner::getInstance($this->cloudId, $this->account);
        $params = $signer->signParams($method, $path, $params);

        // ensure to use relative paths
        if (0 === strpos($path, '/')) {
            $path = substr($path, 1);
        }

        // append request parameters to the URL
        if ('GET' === $method || 'DELETE' === $method) {
            $path .= '?'.http_build_query($params, '', '&');
        }

        $body = null;
        $headers = array();

        if ('PUT' === $method || 'POST' === $method) {
            $body = $this->streamFactory->createStream(http_build_query($params, '', '&'));
            $headers['Content-Type'] = 'application/x-www-form-urlencoded; charset=utf-8';
        }

        // prepare the request
        $uri = 'https://'.$this->account->getApiHost().'/v2/'.$path;
        $request = $this->requestFactory->createRequest($method, $uri, $headers, $body);

        // and execute it
        try {
            $response = $this->httpClient->sendRequest($request);
        } catch (\Exception $e) {
            // throw an exception if the http request failed
            throw new HttpException($e->getMessage(), $e->getCode());
        }

        $responseBody = (string) $response->getBody();

        // throw an API exception if the API response is not valid
        if ($response->getStatusCode() < 200 || $response->getStatusCode() > 207) {
            $decodedResponse = json_decode($responseBody);
            $message = $decodedResponse->error.': '.$decodedResponse->message;

            throw new ApiException($message, $response->getStatusCode());
        }

        return $responseBody;
    }
}