From 7c9ee60103dee70b37104af3d1fa8ed6901ba411 Mon Sep 17 00:00:00 2001 From: Alexey Skobkin Date: Sun, 26 Mar 2023 17:08:41 +0300 Subject: [PATCH] Ported Service\Api\*. Now using symfony/http-client instead of Guzzle. AbstractApi refactored a bit. --- .../Service/Api/AbstractApi.php | 202 ------------------ .../PointToolsBundle/Service/Api/PostApi.php | 28 --- src/Service/Api/AbstractApi.php | 136 ++++++++++++ src/Service/Api/PostApi.php | 22 ++ .../Service/Api/UserApi.php | 108 ++++------ 5 files changed, 200 insertions(+), 296 deletions(-) delete mode 100644 old/src/PointToolsBundle/Service/Api/AbstractApi.php delete mode 100644 old/src/PointToolsBundle/Service/Api/PostApi.php create mode 100644 src/Service/Api/AbstractApi.php create mode 100644 src/Service/Api/PostApi.php rename {old/src/PointToolsBundle => src}/Service/Api/UserApi.php (52%) diff --git a/old/src/PointToolsBundle/Service/Api/AbstractApi.php b/old/src/PointToolsBundle/Service/Api/AbstractApi.php deleted file mode 100644 index 706a516..0000000 --- a/old/src/PointToolsBundle/Service/Api/AbstractApi.php +++ /dev/null @@ -1,202 +0,0 @@ -client = $httpClient; - $this->serializer = $serializer; - $this->logger = $logger; - } - - /** - * Make GET request and return DTO objects - * - * @return array|object - */ - public function getGetJsonData(string $path, array $parameters = [], string $type, DeserializationContext $context = null) - { - return $this->serializer->deserialize( - $this->getGetResponseBody($path, $parameters), - $type, - 'json', - $context - ); - } - - /** - * Make POST request and return DTO objects - * - * @return array|object - */ - public function getPostJsonData(string $path, array $parameters = [], string $type, DeserializationContext $context = null) - { - return $this->serializer->deserialize( - $this->getPostResponseBody($path, $parameters), - $type, - 'json', - $context - ); - } - - /** - * Make GET request and return response body - */ - public function getGetResponseBody($path, array $parameters = []): StreamInterface - { - return $this->sendGetRequest($path, $parameters)->getBody(); - } - - /** - * Make POST request and return response body - */ - public function getPostResponseBody(string $path, array $parameters = []): StreamInterface - { - return $this->sendPostRequest($path, $parameters)->getBody(); - } - - /** - * @param string $path Request path - * @param array $parameters Key => Value array of query parameters - * - * @return ResponseInterface - * - * @throws \src\PointToolsBundle\Exception\Api\NetworkException - */ - private function sendGetRequest(string $path, array $parameters = []): ResponseInterface - { - $this->logger->debug('Sending GET request', ['path' => $path, 'parameters' => $parameters]); - - return $this->sendRequest('GET', $path, ['query' => $parameters]); - } - - /** - * @param string $path Request path - * @param array $parameters Key => Value array of request data - * - * @return ResponseInterface - * - * @throws \src\PointToolsBundle\Exception\Api\NetworkException - */ - private function sendPostRequest(string $path, array $parameters = []): ResponseInterface - { - $this->logger->debug('Sending POST request', ['path' => $path, 'parameters' => $parameters]); - - return $this->sendRequest('POST', $path, ['form_params' => $parameters]); - } - - private function sendRequest(string $method, string $path, array $parameters): ResponseInterface - { - try { - $response = $this->client->request($method, $path, $parameters); - - $this->checkResponse($response); - - return $response; - } catch (TransferException $e) { - $this->processTransferException($e); - - throw new \src\PointToolsBundle\Exception\Api\NetworkException('Request error', $e->getCode(), $e); - } - } - - /** - * @param \Exception $e - * - * @throws \src\PointToolsBundle\Exception\Api\ForbiddenException - * @throws \src\PointToolsBundle\Exception\Api\NotFoundException - * @throws \src\PointToolsBundle\Exception\Api\ServerProblemException - * @throws \src\PointToolsBundle\Exception\Api\UnauthorizedException - * @todo refactor with $this->checkResponse() - * - */ - private function processTransferException(\Exception $e): void - { - switch ($e->getCode()) { - case SymfonyResponse::HTTP_UNAUTHORIZED: - throw new \src\PointToolsBundle\Exception\Api\UnauthorizedException('Unauthorized', SymfonyResponse::HTTP_UNAUTHORIZED, $e); - case SymfonyResponse::HTTP_NOT_FOUND: - throw new \src\PointToolsBundle\Exception\Api\NotFoundException('Resource not found', SymfonyResponse::HTTP_NOT_FOUND, $e); - case SymfonyResponse::HTTP_FORBIDDEN: - throw new \src\PointToolsBundle\Exception\Api\ForbiddenException('Forbidden', SymfonyResponse::HTTP_FORBIDDEN, $e); - case SymfonyResponse::HTTP_INTERNAL_SERVER_ERROR: - case SymfonyResponse::HTTP_NOT_IMPLEMENTED: - case SymfonyResponse::HTTP_BAD_GATEWAY: - case SymfonyResponse::HTTP_SERVICE_UNAVAILABLE: - case SymfonyResponse::HTTP_GATEWAY_TIMEOUT: - throw new \src\PointToolsBundle\Exception\Api\ServerProblemException('Server error', SymfonyResponse::HTTP_INTERNAL_SERVER_ERROR, $e); - } - } - - /** - * @throws \src\PointToolsBundle\Exception\Api\ForbiddenException - * @throws \src\PointToolsBundle\Exception\Api\NotFoundException - * @throws \src\PointToolsBundle\Exception\Api\ServerProblemException - * @throws \src\PointToolsBundle\Exception\Api\UnauthorizedException - */ - private function checkResponse(ResponseInterface $response): void - { - $code = $response->getStatusCode(); - $reason = $response->getReasonPhrase(); - - // @todo remove after fix - // Temporary fix until @arts fixes this bug - if ('{"error": "UserNotFound"}' === (string) $response->getBody()) { - throw new \src\PointToolsBundle\Exception\Api\NotFoundException('Not found', SymfonyResponse::HTTP_NOT_FOUND); - } elseif ('{"message": "Forbidden", "code": 403, "error": "Forbidden"}' === (string) $response->getBody()) { - throw new \src\PointToolsBundle\Exception\Api\ForbiddenException('Forbidden', SymfonyResponse::HTTP_FORBIDDEN); - } - - switch ($code) { - case SymfonyResponse::HTTP_UNAUTHORIZED: - throw new \src\PointToolsBundle\Exception\Api\UnauthorizedException($reason, $code); - case SymfonyResponse::HTTP_FORBIDDEN: - throw new \src\PointToolsBundle\Exception\Api\ForbiddenException($reason, $code); - case SymfonyResponse::HTTP_NOT_FOUND: - throw new \src\PointToolsBundle\Exception\Api\NotFoundException($reason, $code); - case SymfonyResponse::HTTP_INTERNAL_SERVER_ERROR: - case SymfonyResponse::HTTP_NOT_IMPLEMENTED: - case SymfonyResponse::HTTP_BAD_GATEWAY: - case SymfonyResponse::HTTP_SERVICE_UNAVAILABLE: - case SymfonyResponse::HTTP_GATEWAY_TIMEOUT: - throw new \src\PointToolsBundle\Exception\Api\ServerProblemException($reason, $code); - } - } -} diff --git a/old/src/PointToolsBundle/Service/Api/PostApi.php b/old/src/PointToolsBundle/Service/Api/PostApi.php deleted file mode 100644 index c6f3aaa..0000000 --- a/old/src/PointToolsBundle/Service/Api/PostApi.php +++ /dev/null @@ -1,28 +0,0 @@ -postFactory = $postFactory; - } -} diff --git a/src/Service/Api/AbstractApi.php b/src/Service/Api/AbstractApi.php new file mode 100644 index 0000000..e45d165 --- /dev/null +++ b/src/Service/Api/AbstractApi.php @@ -0,0 +1,136 @@ +client = $pointApiClient; + } + + /** Make GET request and return DTO objects */ + public function getGetJsonData(string $path, array $parameters = [], string $type, DeserializationContext $context = null): array|object|null + { + return $this->serializer->deserialize( + $this->getGetResponseBody($path, $parameters), + $type, + 'json', + $context + ); + } + + /** Make POST request and return DTO objects */ + public function getPostJsonData(string $path, array $parameters = [], string $type, DeserializationContext $context = null): array|object|null + { + return $this->serializer->deserialize( + $this->getPostResponseBody($path, $parameters), + $type, + 'json', + $context + ); + } + + /** Make GET request and return response body */ + public function getGetResponseBody($path, array $parameters = []): string + { + return $this->sendGetRequest($path, $parameters)->getContent(); + } + + /** Make POST request and return response body */ + public function getPostResponseBody(string $path, array $parameters = []): string + { + return $this->sendPostRequest($path, $parameters)->getContent(); + } + + private function sendGetRequest(string $path, array $parameters = []): ResponseInterface + { + $this->logger->debug('Sending GET request', ['path' => $path, 'parameters' => $parameters]); + + return $this->sendRequest('GET', $path, ['query' => $parameters]); + } + + private function sendPostRequest(string $path, array $parameters = []): ResponseInterface + { + $this->logger->debug('Sending POST request', ['path' => $path, 'parameters' => $parameters]); + + return $this->sendRequest('POST', $path, ['form_params' => $parameters]); + } + + private function sendRequest(string $method, string $path, array $parameters): ResponseInterface + { + try { + $response = $this->client->request($method, $path, $parameters); + + $this->checkResponse($response); + + return $response; + } catch (TransportExceptionInterface $e) { + $this->throwIfCodeMatches($e->getCode(), $e->getPrevious()); + + throw new NetworkException('Request error', $e->getCode(), $e); + } + } + + /** @throws ApiException */ + private function checkResponse(ResponseInterface $response): void + { + $code = $response->getStatusCode(); + + // @todo remove after fix + // Temporary fix until @arts fixes this bug + if ('{"error": "UserNotFound"}' === $response->getContent()) { + throw new NotFoundException('Not found', SymfonyResponse::HTTP_NOT_FOUND); + } elseif ('{"message": "Forbidden", "code": 403, "error": "Forbidden"}' === (string) $response->getBody()) { + throw new ForbiddenException('Forbidden', SymfonyResponse::HTTP_FORBIDDEN); + } + + $this->throwIfCodeMatches($code); + } + + private function throwIfCodeMatches(int $code, ?\Throwable $previous = null): void + { + $e = $this->matchException($code, $previous); + + if ($e) { + throw $e; + } + } + + private function matchException(int $code, ?\Throwable $previous = null): ?ApiException + { + return match ($code) { + SymfonyResponse::HTTP_UNAUTHORIZED => new UnauthorizedException(previous: $previous), + SymfonyResponse::HTTP_NOT_FOUND => new NotFoundException(previous: $previous), + SymfonyResponse::HTTP_FORBIDDEN => new ForbiddenException(previous: $previous), + SymfonyResponse::HTTP_INTERNAL_SERVER_ERROR, + SymfonyResponse::HTTP_NOT_IMPLEMENTED, + SymfonyResponse::HTTP_BAD_GATEWAY, + SymfonyResponse::HTTP_SERVICE_UNAVAILABLE, + SymfonyResponse::HTTP_GATEWAY_TIMEOUT => new ServerProblemException(previous: $previous), + default => null, + }; + } +} diff --git a/src/Service/Api/PostApi.php b/src/Service/Api/PostApi.php new file mode 100644 index 0000000..69fcd0a --- /dev/null +++ b/src/Service/Api/PostApi.php @@ -0,0 +1,22 @@ +userFactory = $userFactory; + public function __construct( + HttpClientInterface $pointApiClient, + SerializerInterface $serializer, + LoggerInterface $logger, + private readonly UserFactory $userFactory, + ) { + parent::__construct($pointApiClient, $logger, $serializer); } public function isLoginAndPasswordValid(string $login, string $password): bool @@ -50,7 +46,7 @@ class UserApi extends AbstractApi return false; } - public function authenticate(string $login, string $password): \src\PointToolsBundle\DTO\Api\Auth + public function authenticate(string $login, string $password): AuthDTO { $this->logger->debug('Trying to authenticate user via Point.im API', ['login' => $login]); @@ -61,17 +57,15 @@ class UserApi extends AbstractApi 'login' => $login, 'password' => $password, ], - \src\PointToolsBundle\DTO\Api\Auth::class + AuthDTO::class ); - } catch (\src\PointToolsBundle\Exception\Api\NotFoundException $e) { - throw new \src\PointToolsBundle\Exception\Api\InvalidResponseException('API method not found', 0, $e); + } catch (NotFoundException $e) { + throw new InvalidResponseException('API method not found', 0, $e); } } - /** - * @throws \src\PointToolsBundle\Exception\Api\InvalidResponseException - */ - public function logout(\src\PointToolsBundle\DTO\Api\Auth $auth): bool + /** @throws InvalidResponseException */ + public function logout(AuthDTO $auth): bool { $this->logger->debug('Trying to log user out via Point.im API'); @@ -79,20 +73,14 @@ class UserApi extends AbstractApi $this->getPostResponseBody('/api/logout', ['csrf_token' => $auth->getCsRfToken()]); return true; - } catch (\src\PointToolsBundle\Exception\Api\NotFoundException $e) { - throw new \src\PointToolsBundle\Exception\Api\InvalidResponseException('API method not found', 0, $e); - } catch (\src\PointToolsBundle\Exception\Api\ForbiddenException $e) { + } catch (NotFoundException $e) { + throw new InvalidResponseException('API method not found', 0, $e); + } catch (ForbiddenException $e) { return true; } } - /** - * Get user subscribers by user login - * - * @return User[] - * - * @throws \src\PointToolsBundle\Exception\Api\UserNotFoundException - */ + /** @return User[] */ public function getUserSubscribersByLogin(string $login): array { $this->logger->debug('Trying to get user subscribers by login', ['login' => $login]); @@ -102,22 +90,16 @@ class UserApi extends AbstractApi self::PREFIX.urlencode($login).'/subscribers', [], 'array<'.UserDTO::class.'>', - DeserializationContext::create()->setGroups(['user_short']) + DeserializationContext::create()->setGroups(['user_short']), ); - } catch (\src\PointToolsBundle\Exception\Api\NotFoundException $e) { - throw new \src\PointToolsBundle\Exception\Api\UserNotFoundException('User not found', 0, $e, null, $login); + } catch (NotFoundException $e) { + throw new UserNotFoundException('User not found', 0, $e, null, $login); } return $this->userFactory->findOrCreateFromDTOArray($usersList); } - /** - * Get user subscribers by user id - * - * @return User[] - * - * @throws \src\PointToolsBundle\Exception\Api\UserNotFoundException - */ + /** @return User[] */ public function getUserSubscribersById(int $id): array { $this->logger->debug('Trying to get user subscribers by id', ['id' => $id]); @@ -127,18 +109,15 @@ class UserApi extends AbstractApi self::PREFIX.'id/'.$id.'/subscribers', [], 'array<'.UserDTO::class.'>', - DeserializationContext::create()->setGroups(['user_short']) + DeserializationContext::create()->setGroups(['user_short']), ); - } catch (\src\PointToolsBundle\Exception\Api\NotFoundException $e) { - throw new \src\PointToolsBundle\Exception\Api\UserNotFoundException('User not found', 0, $e, $id); + } catch (NotFoundException $e) { + throw new UserNotFoundException('User not found', 0, $e, $id); } return $this->userFactory->findOrCreateFromDTOArray($usersList); } - /** - * Get full user info by login - */ public function getUserByLogin(string $login): User { $this->logger->debug('Trying to get user by login', ['login' => $login]); @@ -149,18 +128,15 @@ class UserApi extends AbstractApi self::PREFIX.'login/'.urlencode($login), [], UserDTO::class, - DeserializationContext::create()->setGroups(['user_full']) + DeserializationContext::create()->setGroups(['user_full']), ); - } catch (\src\PointToolsBundle\Exception\Api\NotFoundException $e) { - throw new \src\PointToolsBundle\Exception\Api\UserNotFoundException('User not found', 0, $e, null, $login); + } catch (NotFoundException $e) { + throw new UserNotFoundException('User not found', 0, $e, null, $login); } return $this->userFactory->findOrCreateFromDTO($userInfo); } - /** - * Get full user info by id - */ public function getUserById(int $id): User { $this->logger->debug('Trying to get user by id', ['id' => $id]); @@ -171,10 +147,10 @@ class UserApi extends AbstractApi self::PREFIX.'id/'.$id, [], UserDTO::class, - DeserializationContext::create()->setGroups(['user_full']) + DeserializationContext::create()->setGroups(['user_full']), ); - } catch (\src\PointToolsBundle\Exception\Api\NotFoundException $e) { - throw new \src\PointToolsBundle\Exception\Api\UserNotFoundException('User not found', 0, $e, $id); + } catch (NotFoundException $e) { + throw new UserNotFoundException('User not found', 0, $e, $id); } // Not catching ForbiddenException right now