1
0
Fork 0
mirror of synced 2025-03-30 11:40:10 +03:00
mg-bot-api-client-php/src/Bot/HttpClient.php
2019-06-24 11:07:34 +03:00

293 lines
8.1 KiB
PHP

<?php
/**
* PHP version 7.0
*
* HttpClient
*
* @package RetailCrm\Mg\Bot
* @author retailCRM <integration@retailcrm.ru>
* @license https://opensource.org/licenses/MIT MIT License
* @link http://help.retailcrm.pro/docs/Developers
*/
namespace RetailCrm\Mg\Bot;
use RetailCrm\Common\Exception\InvalidJsonException;
use RetailCrm\Common\Exception\LimitException;
use InvalidArgumentException;
use RetailCrm\Common\Serializer;
use RetailCrm\Common\Url;
use RetailCrm\Mg\Bot\Model\Response\UploadFileResponse;
use Symfony\Component\Validator\Validation;
use GuzzleHttp\Client;
use GuzzleHttp\Psr7\Request;
use GuzzleHttp\Exception\GuzzleException;
use Psr\Http\Message\ResponseInterface;
use function GuzzleHttp\Psr7\stream_for;
/**
* PHP version 7.0
*
* HttpClient class
*
* @package RetailCrm\Mg\Bot
* @author retailCRM <integration@retailcrm.ru>
* @license https://opensource.org/licenses/MIT MIT License
* @link http://help.retailcrm.pro/docs/Developers
*/
class HttpClient
{
/**
* GET HTTP Method constant
*/
const METHOD_GET = 'GET';
/**
* POST HTTP Method constant
*/
const METHOD_POST = 'POST';
/**
* PUT HTTP Method constant
*/
const METHOD_PUT = 'PUT';
/**
* PATCH HTTP Method constant
*/
const METHOD_PATCH = 'PATCH';
/**
* DELETE HTTP Method constant
*/
const METHOD_DELETE = 'DELETE';
protected $basePath;
protected $token;
private $allowedMethods;
private $stdout;
private $client;
/**
* HTTP Client constructor.
* Provide $stdout (for example: STDOUT) to log all requests.
*
* @param string $url MG API URL
* @param string $token MG API Key
* @param null|resource $stdout Output to log all requests (optional, default: null)
* @param \GuzzleHttp\HandlerStack $handler GuzzleHttp::HandlerStack instance (default: null)
*/
public function __construct($url, $token, $stdout = null, $handler = null)
{
if (false === stripos($url, 'https://')) {
throw new InvalidArgumentException('API schema requires HTTPS protocol');
}
$this->basePath = parse_url($url, PHP_URL_PATH);
$this->token = $token;
$this->stdout = $stdout;
$this->client = new Client(array_filter([
'base_uri' => Url::normalizeUrl($url),
'timeout' => 60,
'handler' => $handler
]));
$this->allowedMethods = [
self::METHOD_GET,
self::METHOD_POST,
self::METHOD_PUT,
self::METHOD_PATCH,
self::METHOD_DELETE
];
}
/**
* Make HTTP request
*
* @param string $path Request URL
* @param string $method Request method (default: 'GET')
* @param mixed $request Request model (default: null)
*
* @return ResponseInterface
* @throws \Exception
*/
public function makeRequest($path, $method, $request = null)
{
$getParameters = '';
$this->validateMethod($method);
if (!is_null($request)) {
$this->validateRequest($request);
}
if ($method == self::METHOD_GET && !is_null($request)) {
$getParameters = Url::buildGetParameters(Serializer::serialize($request, Serializer::S_ARRAY));
}
$requestBody = is_null($request) ? null : Serializer::serialize($request, Serializer::S_JSON);
$request = new Request(
$method,
\sprintf("%s%s%s", $this->basePath, $path, $getParameters),
[
'Content-Type' => 'application/json',
'X-Bot-Token' => $this->token
]
);
if (in_array($method, [self::METHOD_POST, self::METHOD_PUT, self::METHOD_PATCH, self::METHOD_DELETE])) {
$request = $request->withBody(stream_for($requestBody));
}
$responseObject = null;
try {
$responseObject = $this->client->send(
$request,
[
'debug' => $this->stdout,
'allow_redirects' => true,
'http_errors' => false,
'verify' => false
]
);
} catch (GuzzleException $exception) {
throw new \Exception($exception->getMessage(), $exception->getCode(), $exception);
}
$statusCode = $responseObject->getStatusCode();
$response = self::parseJSON((string) $responseObject->getBody());
$errorMessage = !empty($response['errorMsg']) ? $response['errorMsg'] : '';
$errorMessage = !empty($response['errors']) ? $this->getErrors($response['errors']) : $errorMessage;
/**
* responses with 400 & 460 http codes contains extended error data
* therefore they are not handled as exceptions
*/
if (in_array($statusCode, [403, 404, 500])) {
throw new \InvalidArgumentException($errorMessage);
}
if ($statusCode == 503) {
throw new LimitException($errorMessage);
}
return $responseObject;
}
/**
* @param string $filename
* @return UploadFileResponse
*
* @throws \Exception
*/
public function postFile(string $filename): UploadFileResponse
{
if (!file_exists($filename)) {
throw new \InvalidArgumentException("File doesn't exist");
}
try {
$responseData = $this->client->request(
self::METHOD_POST,
\sprintf("%s/files/upload", $this->basePath),
[
'headers' => [
'X-Bot-Token' => $this->token
],
'body' => fopen($filename, 'r')
]
);
} catch (GuzzleException $exception) {
throw new \Exception($exception->getMessage(), $exception->getCode(), $exception);
}
$obj = Serializer::deserialize(
(string) $responseData->getBody(),
'RetailCrm\Mg\Bot\Model\Response\UploadFileResponse'
);
return $obj instanceof UploadFileResponse ? $obj : null;
}
/**
* Validate HTTP method
*
* @param string $method
*/
private function validateMethod($method)
{
if (!in_array($method, $this->allowedMethods, false)) {
throw new InvalidArgumentException(
sprintf(
'Method "%s" is not valid. Allowed methods are %s',
$method,
implode(', ', $this->allowedMethods)
)
);
}
}
/**
* Validate given class
*
* @param string $class
*
* @return void
*/
private function validateRequest($class)
{
if (!is_string($class) && method_exists($class, 'validate')) {
$errors = $class->validate();
} else {
$validator = Validation::createValidatorBuilder()
->enableAnnotationMapping()
->getValidator();
$errors = $validator->validate($class);
}
if ((is_object($errors) && call_user_func([$errors, 'count']) > 0) || is_string($errors)) {
$message = (string) $errors;
throw new InvalidArgumentException($message);
}
}
/**
* @param array $errors
*
* @return string
*/
private function getErrors(array $errors)
{
$errorString = '';
foreach ($errors as $error) {
$errorString .= $error . " ";
}
return $errorString;
}
/**
* @param string $responseBody
*
* @return array
*/
public static function parseJSON($responseBody): array
{
$result = [];
if (!empty($responseBody)) {
$response = json_decode($responseBody, true);
if (!$response && JSON_ERROR_NONE !== ($error = json_last_error())) {
throw new InvalidJsonException("Invalid JSON in the API response body. Error code #$error", $error);
}
$result = $response;
}
return $result;
}
}