Skip to content

Commit

Permalink
Merge pull request #346 from mesilov/340-add-support-for-operating-ti…
Browse files Browse the repository at this point in the history
…ming

340 add support for operating timing
  • Loading branch information
mesilov committed Aug 6, 2023
2 parents c2b966c + b4c54b5 commit e7217c9
Show file tree
Hide file tree
Showing 8 changed files with 345 additions and 83 deletions.
73 changes: 66 additions & 7 deletions src/Core/ApiLevelErrorHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

use Bitrix24\SDK\Core\Exceptions\BaseException;
use Bitrix24\SDK\Core\Exceptions\MethodNotFoundException;
use Bitrix24\SDK\Core\Exceptions\OperationTimeLimitExceededException;
use Bitrix24\SDK\Core\Exceptions\QueryLimitExceededException;
use Psr\Log\LoggerInterface;

Expand All @@ -20,6 +21,8 @@ class ApiLevelErrorHandler
{
protected LoggerInterface $logger;
protected const ERROR_KEY = 'error';
protected const RESULT_KEY = 'result';
protected const RESULT_ERROR_KEY = 'result_error';
protected const ERROR_DESCRIPTION_KEY = 'error_description';

/**
Expand All @@ -33,33 +36,89 @@ public function __construct(LoggerInterface $logger)
}

/**
* @param array<string,string> $responseBody
* @param array<string, mixed> $responseBody
*
* @throws QueryLimitExceededException
* @throws BaseException
*/
public function handle(array $responseBody): void
{
if (!array_key_exists(self::ERROR_KEY, $responseBody)) {
$this->logger->debug('handle.noError');
// single query error response
if (array_key_exists(self::ERROR_KEY, $responseBody) && array_key_exists(self::ERROR_DESCRIPTION_KEY, $responseBody)) {
$this->handleError($responseBody);
}

// error in batch response
if (!array_key_exists(self::RESULT_KEY, $responseBody) || (!is_array($responseBody[self::RESULT_KEY]))) {
return;
}

if (array_key_exists(self::RESULT_ERROR_KEY, $responseBody[self::RESULT_KEY])) {
foreach ($responseBody[self::RESULT_KEY][self::RESULT_ERROR_KEY] as $cmdId => $errorData) {
$this->handleError($errorData, $cmdId);
}
}
}

/**
* @throws MethodNotFoundException
* @throws QueryLimitExceededException
* @throws BaseException
*/
private function handleError(array $responseBody, ?string $batchCommandId = null): void
{
$errorCode = strtolower(trim((string)$responseBody[self::ERROR_KEY]));
$errorDescription = strtolower(trim((string)$responseBody[self::ERROR_DESCRIPTION_KEY]));

$this->logger->debug(
'handle.errorCode',
'handle.errorInformation',
[
'errorCode' => $errorCode,
'errorDescription' => $errorDescription,
]
);

$batchErrorPrefix = '';
if ($batchCommandId !== null) {
$batchErrorPrefix = sprintf(' batch command id: %s', $batchCommandId);
}

switch ($errorCode) {
case 'query_limit_exceeded':
throw new QueryLimitExceededException('query limit exceeded - too many requests');
throw new QueryLimitExceededException(sprintf('query limit exceeded - too many requests %s', $batchErrorPrefix));
case 'error_method_not_found':
throw new MethodNotFoundException('api method not found');
throw new MethodNotFoundException(sprintf('api method not found %s %s', $errorDescription, $batchErrorPrefix));
case 'operation_time_limit':
throw new OperationTimeLimitExceededException(sprintf('operation time limit exceeded %s %s', $errorDescription, $batchErrorPrefix));
default:
throw new BaseException(sprintf('%s - %s', $errorCode, $errorDescription));
throw new BaseException(sprintf('%s - %s %s', $errorCode, $errorDescription, $batchErrorPrefix));
}
// switch (strtoupper(trim($apiResponse['error']))) {
// case 'EXPIRED_TOKEN':
// throw new Bitrix24TokenIsExpiredException($errorMsg);
// case 'WRONG_CLIENT':
// case 'ERROR_OAUTH':
// $this->log->error($errorMsg, $this->getErrorContext());
// throw new Bitrix24WrongClientException($errorMsg);
// case 'ERROR_METHOD_NOT_FOUND':
// $this->log->error($errorMsg, $this->getErrorContext());
// throw new Bitrix24MethodNotFoundException($errorMsg);
// case 'INVALID_TOKEN':
// case 'INVALID_GRANT':
// $this->log->error($errorMsg, $this->getErrorContext());
// throw new Bitrix24TokenIsInvalidException($errorMsg);

// case 'PAYMENT_REQUIRED':
// $this->log->error($errorMsg, $this->getErrorContext());
// throw new Bitrix24PaymentRequiredException($errorMsg);
// case 'NO_AUTH_FOUND':
// $this->log->error($errorMsg, $this->getErrorContext());
// throw new Bitrix24PortalRenamedException($errorMsg);
// case 'INSUFFICIENT_SCOPE':
// $this->log->error($errorMsg, $this->getErrorContext());
// throw new Bitrix24InsufficientScope($errorMsg);
// default:
// $this->log->error($errorMsg, $this->getErrorContext());
// throw new Bitrix24ApiException($errorMsg);
}
}
39 changes: 20 additions & 19 deletions src/Core/Core.php
Original file line number Diff line number Diff line change
Expand Up @@ -32,17 +32,18 @@ class Core implements CoreInterface
/**
* Main constructor.
*
* @param ApiClientInterface $apiClient
* @param ApiLevelErrorHandler $apiLevelErrorHandler
* @param ApiClientInterface $apiClient
* @param ApiLevelErrorHandler $apiLevelErrorHandler
* @param EventDispatcherInterface $eventDispatcher
* @param LoggerInterface $logger
* @param LoggerInterface $logger
*/
public function __construct(
ApiClientInterface $apiClient,
ApiLevelErrorHandler $apiLevelErrorHandler,
ApiClientInterface $apiClient,
ApiLevelErrorHandler $apiLevelErrorHandler,
EventDispatcherInterface $eventDispatcher,
LoggerInterface $logger
) {
LoggerInterface $logger
)
{
$this->apiClient = $apiClient;
$this->apiLevelErrorHandler = $apiLevelErrorHandler;
$this->eventDispatcher = $eventDispatcher;
Expand All @@ -51,7 +52,7 @@ public function __construct(

/**
* @param string $apiMethod
* @param array $parameters
* @param array $parameters
*
* @return Response
* @throws BaseException
Expand All @@ -62,7 +63,7 @@ public function call(string $apiMethod, array $parameters = []): Response
$this->logger->debug(
'call.start',
[
'method' => $apiMethod,
'method' => $apiMethod,
'parameters' => $parameters,
]
);
Expand All @@ -80,7 +81,7 @@ public function call(string $apiMethod, array $parameters = []): Response
switch ($apiCallResponse->getStatusCode()) {
case StatusCodeInterface::STATUS_OK:
//todo check with empty response size from server
$response = new Response($apiCallResponse, new Command($apiMethod, $parameters), $this->logger);
$response = new Response($apiCallResponse, new Command($apiMethod, $parameters), $this->apiLevelErrorHandler, $this->logger);
break;
case StatusCodeInterface::STATUS_FOUND:
// change domain url
Expand All @@ -98,9 +99,9 @@ public function call(string $apiMethod, array $parameters = []): Response
$this->logger->debug(
'api call repeated to new domain url',
[
'domainUrl' => $portalNewDomainUrlHost,
'domainUrl' => $portalNewDomainUrlHost,
'repeatedApiMethod' => $apiMethod,
'httpStatusCode' => $response->getHttpResponse()->getStatusCode(),
'httpStatusCode' => $response->getHttpResponse()->getStatusCode(),
]
);
// dispatch event, application listeners update domain url host in accounts repository
Expand All @@ -121,10 +122,10 @@ public function call(string $apiMethod, array $parameters = []): Response
$this->logger->debug(
'access token renewed',
[
'newAccessToken' => $renewedToken->getAccessToken()->getAccessToken(),
'newAccessToken' => $renewedToken->getAccessToken()->getAccessToken(),
'newRefreshToken' => $renewedToken->getAccessToken()->getRefreshToken(),
'newExpires' => $renewedToken->getAccessToken()->getExpires(),
'appStatus' => $renewedToken->getApplicationStatus(),
'newExpires' => $renewedToken->getAccessToken()->getExpires(),
'appStatus' => $renewedToken->getApplicationStatus(),
]
);
$this->apiClient->getCredentials()->setAccessToken($renewedToken->getAccessToken());
Expand All @@ -135,7 +136,7 @@ public function call(string $apiMethod, array $parameters = []): Response
'api call repeated',
[
'repeatedApiMethod' => $apiMethod,
'httpStatusCode' => $response->getHttpResponse()->getStatusCode(),
'httpStatusCode' => $response->getHttpResponse()->getStatusCode(),
]
);

Expand All @@ -161,7 +162,7 @@ public function call(string $apiMethod, array $parameters = []): Response
'unhandled server status',
[
'httpStatus' => $apiCallResponse->getStatusCode(),
'body' => $body,
'body' => $body,
]
);
$this->apiLevelErrorHandler->handle($body);
Expand All @@ -172,7 +173,7 @@ public function call(string $apiMethod, array $parameters = []): Response
$this->logger->error(
'call.transportException',
[
'trace' => $exception->getTrace(),
'trace' => $exception->getTrace(),
'message' => $exception->getMessage(),
]
);
Expand All @@ -185,7 +186,7 @@ public function call(string $apiMethod, array $parameters = []): Response
'call.unknownException',
[
'message' => $exception->getMessage(),
'trace' => $exception->getTrace(),
'trace' => $exception->getTrace(),
]
);
throw new BaseException(sprintf('unknown error - %s', $exception->getMessage()), $exception->getCode(), $exception);
Expand Down
14 changes: 14 additions & 0 deletions src/Core/Exceptions/OperationTimeLimitExceededException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<?php

declare(strict_types=1);

namespace Bitrix24\SDK\Core\Exceptions;

/**
* Class OperationTimeLimitExceededException
*
* @package Bitrix24\SDK\Core\Exceptions
*/
class OperationTimeLimitExceededException extends BaseException
{
}
71 changes: 16 additions & 55 deletions src/Core/Response/Response.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

namespace Bitrix24\SDK\Core\Response;

use Bitrix24\SDK\Core\ApiLevelErrorHandler;
use Bitrix24\SDK\Core\Commands\Command;
use Bitrix24\SDK\Core\Exceptions\BaseException;
use Bitrix24\SDK\Core\Response\DTO;
Expand All @@ -23,19 +24,24 @@ class Response
protected ResponseInterface $httpResponse;
protected ?DTO\ResponseData $responseData;
protected Command $apiCommand;
protected ApiLevelErrorHandler $apiLevelErrorHandler;
protected LoggerInterface $logger;

/**
* Response constructor.
*
* @param ResponseInterface $httpResponse
* @param Command $apiCommand
* @param LoggerInterface $logger
* @param Command $apiCommand
* @param ApiLevelErrorHandler $apiLevelErrorHandler
* @param LoggerInterface $logger
*/
public function __construct(ResponseInterface $httpResponse, Command $apiCommand, LoggerInterface $logger)
public function __construct(ResponseInterface $httpResponse, Command $apiCommand,
ApiLevelErrorHandler $apiLevelErrorHandler,
LoggerInterface $logger)
{
$this->httpResponse = $httpResponse;
$this->apiCommand = $apiCommand;
$this->apiLevelErrorHandler = $apiLevelErrorHandler;
$this->logger = $logger;
$this->responseData = null;
}
Expand Down Expand Up @@ -64,16 +70,16 @@ public function __destruct()
$restTimings = null;
if ($this->responseData !== null) {
$restTimings = [
'rest_query_duration' => $this->responseData->getTime()->getDuration(),
'rest_query_duration' => $this->responseData->getTime()->getDuration(),
'rest_query_processing' => $this->responseData->getTime()->getProcessing(),
'rest_query_start' => $this->responseData->getTime()->getStart(),
'rest_query_finish' => $this->responseData->getTime()->getFinish(),
'rest_query_start' => $this->responseData->getTime()->getStart(),
'rest_query_finish' => $this->responseData->getTime()->getFinish(),
];
}
$this->logger->info('Response.TransportInfo', [
'restTimings' => $restTimings,
'restTimings' => $restTimings,
'networkTimings' => (new NetworkTimingsParser($this->httpResponse->getInfo()))->toArrayWithMicroseconds(),
'responseInfo' => (new ResponseInfoParser($this->httpResponse->getInfo()))->toArray(),
'responseInfo' => (new ResponseInfoParser($this->httpResponse->getInfo()))->toArray(),
]);
}

Expand All @@ -92,8 +98,9 @@ public function getResponseData(): DTO\ResponseData
$this->logger->info('getResponseData.responseBody', [
'responseBody' => $responseResult,
]);

// try to handle api-level errors
$this->handleApiLevelErrors($responseResult);
$this->apiLevelErrorHandler->handle($responseResult);

if (!is_array($responseResult['result'])) {
$responseResult['result'] = [$responseResult['result']];
Expand Down Expand Up @@ -143,50 +150,4 @@ private function getHttpResponseContent(): ?string

return $content;
}

/**
* @param array $apiResponse
*/
private function handleApiLevelErrors(array $apiResponse): void
{
$this->logger->debug('handleApiLevelErrors.start');

if (array_key_exists('error', $apiResponse)) {
$errorMsg = sprintf(
'%s - %s ',
$apiResponse['error'],
(array_key_exists('error_description', $apiResponse) ? $apiResponse['error_description'] : ''),
);
// todo check api-level error codes
//
// switch (strtoupper(trim($apiResponse['error']))) {
// case 'EXPIRED_TOKEN':
// throw new Bitrix24TokenIsExpiredException($errorMsg);
// case 'WRONG_CLIENT':
// case 'ERROR_OAUTH':
// $this->log->error($errorMsg, $this->getErrorContext());
// throw new Bitrix24WrongClientException($errorMsg);
// case 'ERROR_METHOD_NOT_FOUND':
// $this->log->error($errorMsg, $this->getErrorContext());
// throw new Bitrix24MethodNotFoundException($errorMsg);
// case 'INVALID_TOKEN':
// case 'INVALID_GRANT':
// $this->log->error($errorMsg, $this->getErrorContext());
// throw new Bitrix24TokenIsInvalidException($errorMsg);

// case 'PAYMENT_REQUIRED':
// $this->log->error($errorMsg, $this->getErrorContext());
// throw new Bitrix24PaymentRequiredException($errorMsg);
// case 'NO_AUTH_FOUND':
// $this->log->error($errorMsg, $this->getErrorContext());
// throw new Bitrix24PortalRenamedException($errorMsg);
// case 'INSUFFICIENT_SCOPE':
// $this->log->error($errorMsg, $this->getErrorContext());
// throw new Bitrix24InsufficientScope($errorMsg);
// default:
// $this->log->error($errorMsg, $this->getErrorContext());
// throw new Bitrix24ApiException($errorMsg);
}
$this->logger->debug('handleApiLevelErrors.finish');
}
}

0 comments on commit e7217c9

Please sign in to comment.