diff --git a/CHANGELOG.md b/CHANGELOG.md index 14fd95e4..bca988bd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -129,6 +129,7 @@ * fix [typehint for Bitrix24 User entity with field ID](https://github.com/mesilov/bitrix24-php-sdk/issues/382) * fix [default arguments for Bitrix24 User get method](https://github.com/mesilov/bitrix24-php-sdk/issues/381) +* fix [limit argument not worked in batch list and read model](https://github.com/mesilov/bitrix24-php-sdk/issues/389) ## 2.0-beta.2 — 1.04.2024 @@ -214,10 +215,7 @@ * fix [typehint at ContactItemResult](https://github.com/mesilov/bitrix24-php-sdk/issues/320) * fix [return types in DealCategoryItemResult](https://github.com/mesilov/bitrix24-php-sdk/issues/322) * fix [add auth node in telephony voximplant events requests](https://github.com/mesilov/bitrix24-php-sdk/issues/331) -* - -fix [add helper metods isError for registerCallResult fortelephony](https://github.com/mesilov/bitrix24-php-sdk/issues/335) - +* fix [add helper metods isError for registerCallResult fortelephony](https://github.com/mesilov/bitrix24-php-sdk/issues/335) * fix [add return type for crm multifields phone, email, im](https://github.com/mesilov/bitrix24-php-sdk/issues/338) * fix errors in `ShowFieldsDescriptionCommand` metadata reader CLI command * fix errors for `ApplicationProfile` with empty scope diff --git a/Makefile b/Makefile index 5c55b787..cd389856 100644 --- a/Makefile +++ b/Makefile @@ -18,4 +18,6 @@ test-integration-scope-telephony: test-integration-scope-workflows: vendor/bin/phpunit --testsuite integration_tests_scope_workflows test-integration-scope-user: - vendor/bin/phpunit --testsuite integration_tests_scope_user \ No newline at end of file + vendor/bin/phpunit --testsuite integration_tests_scope_user +test-integration-core: + vendor/bin/phpunit --testsuite integration_tests_core \ No newline at end of file diff --git a/phpunit.xml.dist b/phpunit.xml.dist index d16b93db..e878d19c 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -10,6 +10,9 @@ ./tests/Integration + + ./tests/Integration/Core/ + ./tests/Integration/Services/Telephony/ diff --git a/rector.php b/rector.php index d4d262d1..87d3e575 100644 --- a/rector.php +++ b/rector.php @@ -9,6 +9,7 @@ return RectorConfig::configure() ->withPaths([ + __DIR__ . '/src/Core/', __DIR__ . '/src/Services/Telephony', __DIR__ . '/tests/Integration/Services/Telephony', __DIR__ . '/src/Services/User', diff --git a/src/Core/ApiClient.php b/src/Core/ApiClient.php index 4bddc374..289589fd 100644 --- a/src/Core/ApiClient.php +++ b/src/Core/ApiClient.php @@ -18,42 +18,31 @@ class ApiClient implements ApiClientInterface { - protected HttpClientInterface $client; - protected LoggerInterface $logger; - protected Credentials $credentials; - protected RequestIdGeneratorInterface $requestIdGenerator; /** * @const string */ protected const BITRIX24_OAUTH_SERVER_URL = 'https://oauth.bitrix.info'; + /** * @const string */ protected const SDK_VERSION = '2.0.0'; + protected const SDK_USER_AGENT = 'bitrix24-php-sdk'; /** * ApiClient constructor. - * - * @param Credentials $credentials - * @param HttpClientInterface $client - * @param RequestIdGeneratorInterface $requestIdGenerator - * @param LoggerInterface $logger */ public function __construct( - Credentials $credentials, - HttpClientInterface $client, - RequestIdGeneratorInterface $requestIdGenerator, - LoggerInterface $logger) + protected Credentials $credentials, + protected HttpClientInterface $client, + protected RequestIdGeneratorInterface $requestIdGenerator, + protected LoggerInterface $logger) { - $this->credentials = $credentials; - $this->client = $client; - $this->requestIdGenerator = $requestIdGenerator; - $this->logger = $logger; $this->logger->debug( 'ApiClient.init', [ - 'httpClientType' => get_class($client), + 'httpClientType' => $this->client::class, ] ); } @@ -72,16 +61,12 @@ protected function getDefaultHeaders(): array ]; } - /** - * @return Credentials - */ public function getCredentials(): Credentials { return $this->credentials; } /** - * @return RenewedAccessToken * @throws InvalidArgumentException * @throws TransportExceptionInterface * @throws TransportException @@ -92,10 +77,11 @@ public function getNewAccessToken(): RenewedAccessToken $this->logger->debug('getNewAccessToken.start', [ 'requestId' => $requestId ]); - if ($this->getCredentials()->getApplicationProfile() === null) { + if (!$this->getCredentials()->getApplicationProfile() instanceof \Bitrix24\SDK\Core\Credentials\ApplicationProfile) { throw new InvalidArgumentException('application profile not set'); } - if ($this->getCredentials()->getAccessToken() === null) { + + if (!$this->getCredentials()->getAccessToken() instanceof \Bitrix24\SDK\Core\Credentials\AccessToken) { throw new InvalidArgumentException('access token in credentials not set'); } @@ -132,20 +118,20 @@ public function getNewAccessToken(): RenewedAccessToken ]); return $newAccessToken; } + if ($response->getStatusCode() === StatusCodeInterface::STATUS_BAD_REQUEST) { $this->logger->warning('getNewAccessToken.badRequest',[ 'url'=> $url ]); throw new TransportException(sprintf('getting new access token failure: %s', $responseData['error'])); } + throw new TransportException('getting new access token failure with unknown http-status code %s', $response->getStatusCode()); } /** - * @param string $apiMethod * @param array $parameters * - * @return ResponseInterface * @throws TransportExceptionInterface * @throws InvalidArgumentException */ @@ -163,16 +149,18 @@ public function getResponse(string $apiMethod, array $parameters = []): Response ); $method = 'POST'; - if ($this->getCredentials()->getWebhookUrl() !== null) { + if ($this->getCredentials()->getWebhookUrl() instanceof \Bitrix24\SDK\Core\Credentials\WebhookUrl) { $url = sprintf('%s/%s/', $this->getCredentials()->getWebhookUrl()->getUrl(), $apiMethod); } else { $url = sprintf('%s/rest/%s', $this->getCredentials()->getDomainUrl(), $apiMethod); - if ($this->getCredentials()->getAccessToken() === null) { - throw new InvalidArgumentException(sprintf('access token in credentials not found ')); + if (!$this->getCredentials()->getAccessToken() instanceof \Bitrix24\SDK\Core\Credentials\AccessToken) { + throw new InvalidArgumentException('access token in credentials not found '); } + $parameters['auth'] = $this->getCredentials()->getAccessToken()->getAccessToken(); } + // duplicate request id in query string for current version of bitrix24 api // vendor don't use request id from headers =( $url .= '?' . $this->requestIdGenerator->getQueryStringParameterName() . '=' . $requestId; diff --git a/src/Core/ApiLevelErrorHandler.php b/src/Core/ApiLevelErrorHandler.php index 817c5f63..1ce16ca2 100644 --- a/src/Core/ApiLevelErrorHandler.php +++ b/src/Core/ApiLevelErrorHandler.php @@ -21,20 +21,19 @@ */ 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'; /** * ApiLevelErrorHandler constructor. - * - * @param LoggerInterface $logger */ - public function __construct(LoggerInterface $logger) + public function __construct(protected LoggerInterface $logger) { - $this->logger = $logger; } /** @@ -84,14 +83,17 @@ private function handleError(array $responseBody, ?string $batchCommandId = null if ($batchCommandId !== null) { $batchErrorPrefix = sprintf(' batch command id: %s', $batchCommandId); } + // todo send issues to bitrix24 // fix errors without error_code responses if ($errorCode === '' && strtolower($errorDescription) === strtolower('You can delete ONLY templates created by current application')) { $errorCode = 'bizproc_workflow_template_access_denied'; } + if ($errorCode === '' && strtolower($errorDescription) === strtolower('No fields to update.')) { $errorCode = 'bad_request_no_fields_to_update'; } + if ($errorCode === '' && strtolower($errorDescription) === strtolower('User is not found or is not active')) { $errorCode = 'user_not_found_or_is_not_active'; } @@ -119,6 +121,7 @@ private function handleError(array $responseBody, ?string $batchCommandId = null default: throw new BaseException(sprintf('%s - %s %s', $errorCode, $errorDescription, $batchErrorPrefix)); } + // switch (strtoupper(trim($apiResponse['error']))) { // case 'EXPIRED_TOKEN': // throw new Bitrix24TokenIsExpiredException($errorMsg); diff --git a/src/Core/Batch.php b/src/Core/Batch.php index 8bb1fa43..dbbd17d5 100644 --- a/src/Core/Batch.php +++ b/src/Core/Batch.php @@ -25,22 +25,17 @@ */ class Batch implements BatchOperationsInterface { - private CoreInterface $core; - private LoggerInterface $logger; protected const MAX_BATCH_PACKET_SIZE = 50; + protected const MAX_ELEMENTS_IN_PAGE = 50; + protected CommandCollection $commands; /** * Batch constructor. - * - * @param CoreInterface $core - * @param LoggerInterface $log */ - public function __construct(CoreInterface $core, LoggerInterface $log) + public function __construct(private readonly CoreInterface $core, private readonly LoggerInterface $logger) { - $this->core = $core; - $this->logger = $log; $this->commands = new CommandCollection(); } @@ -62,7 +57,6 @@ protected function clearCommands(): void /** * Add entity items with batch call * - * @param string $apiMethod * @param array $entityItems * * @return Generator|ResponseData[] @@ -87,16 +81,16 @@ public function addEntityItems(string $apiMethod, array $entityItems): Generator foreach ($this->getTraversable(true) as $cnt => $addedItemResult) { yield $cnt => $addedItemResult; } - } catch (\Throwable $exception) { - $errorMessage = sprintf('batch add entity items: %s', $exception->getMessage()); + } catch (\Throwable $throwable) { + $errorMessage = sprintf('batch add entity items: %s', $throwable->getMessage()); $this->logger->error( $errorMessage, [ - 'trace' => $exception->getTrace(), + 'trace' => $throwable->getTrace(), ] ); - throw new BaseException($errorMessage, $exception->getCode(), $exception); + throw new BaseException($errorMessage, $throwable->getCode(), $throwable); } $this->logger->debug('addEntityItems.finish'); @@ -105,7 +99,6 @@ public function addEntityItems(string $apiMethod, array $entityItems): Generator /** * Delete entity items with batch call * - * @param string $apiMethod * @param array $entityItemId * * @return Generator|ResponseData[] @@ -134,6 +127,7 @@ public function deleteEntityItems(string $apiMethod, array $entityItemId): Gener ) ); } + $this->registerCommand($apiMethod, ['ID' => $itemId]); } @@ -173,7 +167,6 @@ public function deleteEntityItems(string $apiMethod, array $entityItemId): Gener * 'params' => [] // optional fields * ] * - * @param string $apiMethod * @param array> $entityItems * * @return Generator|ResponseData[] @@ -201,6 +194,7 @@ public function updateEntityItems(string $apiMethod, array $entityItems): Genera ) ); } + if (!array_key_exists('fields', $entityItem)) { throw new InvalidArgumentException( sprintf('array key «fields» not found in entity item with id %s', $entityItemId) @@ -214,6 +208,7 @@ public function updateEntityItems(string $apiMethod, array $entityItems): Genera if (array_key_exists('params', $entityItem)) { $cmdArguments['params'] = $entityItem['params']; } + $this->registerCommand($apiMethod, $cmdArguments); } @@ -247,9 +242,7 @@ public function updateEntityItems(string $apiMethod, array $entityItems): Genera /** * Register api command to command collection for batch calls * - * @param string $apiMethod * @param array $parameters - * @param string|null $commandName * @param callable|null $callback not implemented * * @throws \Exception @@ -300,11 +293,8 @@ protected function getReverseOrder(array $order): array } else { $order = array_change_key_case($order, CASE_UPPER); $oldDirection = array_values($order)[0]; - if ($oldDirection === 'ASC') { - $newOrderDirection = 'DESC'; - } else { - $newOrderDirection = 'ASC'; - } + $newOrderDirection = $oldDirection === 'ASC' ? 'DESC' : 'ASC'; + $reverseOrder[array_key_first($order)] = $newOrderDirection; } @@ -321,11 +311,9 @@ protected function getReverseOrder(array $order): array /** * Get traversable list without count elements * - * @param string $apiMethod * @param array $order * @param array $filter * @param array $select - * @param int|null $limit * * @return \Generator * @throws \Bitrix24\SDK\Core\Exceptions\BaseException @@ -396,14 +384,12 @@ public function getTraversableList( if (array_key_exists('entityTypeId', $additionalParameters)) { $isCrmItemsInBatch = true; } + $params = array_merge($params, $additionalParameters); } - if ($isCrmItemsInBatch) { - $keyId = 'id'; - } else { - $keyId = 'ID'; - } + $keyId = $isCrmItemsInBatch ? 'id' : 'ID'; + $firstResultPage = $this->core->call($apiMethod, $params); $totalElementsCount = $firstResultPage->getResponseData()->getPagination()->getTotal(); $this->logger->debug('getTraversableList.totalElementsCount', [ @@ -412,13 +398,15 @@ public function getTraversableList( // filtered elements count less than or equal one result page(50 elements) $elementsCounter = 0; if ($totalElementsCount <= self::MAX_ELEMENTS_IN_PAGE) { - foreach ($firstResultPage->getResponseData()->getResult() as $cnt => $listElement) { - $elementsCounter++; + foreach ($firstResultPage->getResponseData()->getResult() as $listElement) { + ++$elementsCounter; if ($limit !== null && $elementsCounter > $limit) { return; } + yield $listElement; } + $this->logger->debug('getTraversableList.finish'); return; @@ -428,21 +416,23 @@ public function getTraversableList( // return first page $lastElementIdInFirstPage = null; if ($isCrmItemsInBatch) { - foreach ($firstResultPage->getResponseData()->getResult()['items'] as $cnt => $listElement) { - $elementsCounter++; + foreach ($firstResultPage->getResponseData()->getResult()['items'] as $listElement) { + ++$elementsCounter; $lastElementIdInFirstPage = (int)$listElement[$keyId]; if ($limit !== null && $elementsCounter > $limit) { return; } + yield $listElement; } } else { - foreach ($firstResultPage->getResponseData()->getResult() as $cnt => $listElement) { - $elementsCounter++; + foreach ($firstResultPage->getResponseData()->getResult() as $listElement) { + ++$elementsCounter; $lastElementIdInFirstPage = (int)$listElement[$keyId]; if ($limit !== null && $elementsCounter > $limit) { return; } + yield $listElement; } } @@ -451,6 +441,7 @@ public function getTraversableList( if (!in_array($keyId, $select, true)) { $select[] = $keyId; } + // getLastElementId in filtered result $params = [ 'order' => $this->getReverseOrder($order), @@ -461,18 +452,21 @@ public function getTraversableList( if ($additionalParameters !== null) { $params = array_merge($params, $additionalParameters); } + $lastResultPage = $this->core->call($apiMethod, $params); if ($isCrmItemsInBatch) { $lastElementId = (int)$lastResultPage->getResponseData()->getResult()['items'][0][$keyId]; } else { $lastElementId = (int)$lastResultPage->getResponseData()->getResult()[0][$keyId]; } + // reverse order if you need if ($lastElementIdInFirstPage > $lastElementId) { $tmp = $lastElementIdInFirstPage; $lastElementIdInFirstPage = $lastElementId; $lastElementId = $tmp; } + $this->logger->debug('getTraversableList.lastElementsId', [ 'lastElementIdInFirstPage' => $lastElementIdInFirstPage, 'lastElementId' => $lastElementId, @@ -480,7 +474,7 @@ public function getTraversableList( // register commands with updated filter //more than one page in results - register list commands - $lastElementIdInFirstPage++; + ++$lastElementIdInFirstPage; for ($startId = $lastElementIdInFirstPage; $startId <= $lastElementId; $startId += self::MAX_ELEMENTS_IN_PAGE) { $this->logger->debug('registerCommand.item', [ 'startId' => $startId, @@ -501,7 +495,7 @@ public function getTraversableList( } $params = [ - 'order' => [], + 'order' => $order, 'filter' => $this->updateFilterForBatch($keyId, $startId, $lastElementIdInPage, $isLastPage, $filter), 'select' => $select, 'start' => -1, @@ -512,6 +506,7 @@ public function getTraversableList( $this->registerCommand($apiMethod, $params); } + $this->logger->debug( 'getTraversableList.commandsRegistered', [ @@ -520,7 +515,6 @@ public function getTraversableList( ); // iterate batch queries, max: 50 results per 50 elements in each result - $elementsCounter = 0; foreach ($this->getTraversable(true) as $queryCnt => $queryResultData) { /** * @var $queryResultData ResponseData @@ -536,32 +530,31 @@ public function getTraversableList( // iterate items in batch query result if ($isCrmItemsInBatch) { - foreach ($queryResultData->getResult()['items'] as $cnt => $listElement) { - $elementsCounter++; + foreach ($queryResultData->getResult()['items'] as $listElement) { + ++$elementsCounter; if ($limit !== null && $elementsCounter > $limit) { return; } + yield $listElement; } } else { - foreach ($queryResultData->getResult() as $cnt => $listElement) { - $elementsCounter++; + foreach ($queryResultData->getResult() as $listElement) { + ++$elementsCounter; if ($limit !== null && $elementsCounter > $limit) { return; } + yield $listElement; } } } + $this->logger->debug('getTraversableList.finish'); } /** - * @param string $keyId - * @param int $startElementId - * @param int $lastElementId - * @param bool $isLastPage * @param array $oldFilter * * @return array @@ -595,11 +588,9 @@ protected function updateFilterForBatch(string $keyId, int $startElementId, int * * work with start item position and elements count * - * @param string $apiMethod * @param array $order * @param array $filter * @param array $select - * @param int|null $limit * * @return Generator * @throws BaseException @@ -631,7 +622,7 @@ public function getTraversableListWithCount( $this->clearCommands(); // get total elements count - $firstResult = $this->core->call( + $response = $this->core->call( $apiMethod, [ 'order' => $order, @@ -641,8 +632,8 @@ public function getTraversableListWithCount( ] ); - $nextItem = $firstResult->getResponseData()->getPagination()->getNextItem(); - $total = $firstResult->getResponseData()->getPagination()->getTotal(); + $nextItem = $response->getResponseData()->getPagination()->getNextItem(); + $total = $response->getResponseData()->getPagination()->getTotal(); $this->logger->debug( 'getTraversableListWithCount.calculateCommandsRange', @@ -704,11 +695,12 @@ public function getTraversableListWithCount( ] ); // iterate items in batch query result - foreach ($queryResultData->getResult() as $cnt => $listElement) { - $elementsCounter++; + foreach ($queryResultData->getResult() as $listElement) { + ++$elementsCounter; if ($limit !== null && $elementsCounter > $limit) { return; } + yield $listElement; } } @@ -717,7 +709,6 @@ public function getTraversableListWithCount( } /** - * @param bool $isHaltOnError * * @return Generator * @throws BaseException @@ -737,7 +728,7 @@ protected function getTraversable(bool $isHaltOnError): Generator ] ); - foreach ($this->getTraversableBatchResults($isHaltOnError) as $batchItem => $batchResult) { + foreach ($this->getTraversableBatchResults($isHaltOnError) as $batchItem => $traversableBatchResult) { /** * @var $batchResult Response */ @@ -745,12 +736,12 @@ protected function getTraversable(bool $isHaltOnError): Generator 'getTraversable.batchResultItem.processStart', [ 'batchItemNumber' => $batchItem, - 'batchApiCommand' => $batchResult->getApiCommand()->getApiMethod(), - 'batchApiCommandUuid' => $batchResult->getApiCommand()->getUuid()->toString(), + 'batchApiCommand' => $traversableBatchResult->getApiCommand()->getApiMethod(), + 'batchApiCommandUuid' => $traversableBatchResult->getApiCommand()->getUuid()->toString(), ] ); // todo try to multiplex requests - $response = $batchResult->getResponseData(); + $response = $traversableBatchResult->getResponseData(); // single queries // todo handle error field @@ -765,6 +756,7 @@ protected function getTraversable(bool $isHaltOnError): Generator if (!is_array($singleQueryResult)) { $singleQueryResult = [$singleQueryResult]; } + if (!array_key_exists($singleQueryKey, $resultQueryTimeItems)) { throw new BaseException(sprintf('query time with key %s not found', $singleQueryKey)); } @@ -785,13 +777,14 @@ protected function getTraversable(bool $isHaltOnError): Generator new Pagination($nextItem, $total) ); } + $this->logger->debug('getTraversable.batchResult.processFinish'); } + $this->logger->debug('getTraversable.finish'); } /** - * @param bool $isHaltOnError * * @return Generator * @throws BaseException @@ -824,9 +817,10 @@ private function getTraversableBatchResults(bool $isHaltOnError): Generator $batchResult = $this->core->call('batch', ['halt' => $isHaltOnError, 'cmd' => $batchQuery]); // todo analyze batch result and halt on error - $batchQueryCounter++; + ++$batchQueryCounter; yield $batchResult; } + $this->logger->debug('getTraversableBatchResults.finish'); } @@ -836,14 +830,11 @@ private function getTraversableBatchResults(bool $isHaltOnError): Generator private function convertToApiCommands(): array { $apiCommands = []; - foreach ($this->commands as $itemCommand) { - /** - * @var Command $itemCommand - */ - $apiCommands[$itemCommand->getName() ?? $itemCommand->getUuid()->toString()] = sprintf( + foreach ($this->commands as $command) { + $apiCommands[$command->getName() ?? $command->getUuid()->toString()] = sprintf( '%s?%s', - $itemCommand->getApiMethod(), - http_build_query($itemCommand->getParameters()) + $command->getApiMethod(), + http_build_query($command->getParameters()) ); } diff --git a/src/Core/BulkItemsReader/BulkItemsReader.php b/src/Core/BulkItemsReader/BulkItemsReader.php index 50a2809b..fa6f7d7b 100644 --- a/src/Core/BulkItemsReader/BulkItemsReader.php +++ b/src/Core/BulkItemsReader/BulkItemsReader.php @@ -10,27 +10,12 @@ class BulkItemsReader implements BulkItemsReaderInterface { - protected BulkItemsReaderInterface $readStrategy; - protected LoggerInterface $logger; - - /** - * @param \Bitrix24\SDK\Core\Contracts\BulkItemsReaderInterface $readStrategy - * @param \Psr\Log\LoggerInterface $logger - */ - public function __construct(BulkItemsReaderInterface $readStrategy, LoggerInterface $logger) + public function __construct(protected BulkItemsReaderInterface $readStrategy, protected LoggerInterface $logger) { - $this->readStrategy = $readStrategy; - $this->logger = $logger; } /** - * @param string $apiMethod - * @param array $order - * @param array $filter - * @param array $select - * @param int|null $limit * - * @return \Generator * @throws \Bitrix24\SDK\Core\Exceptions\BaseException */ public function getTraversableList(string $apiMethod, array $order, array $filter, array $select, ?int $limit = null): Generator diff --git a/src/Core/BulkItemsReader/BulkItemsReaderBuilder.php b/src/Core/BulkItemsReader/BulkItemsReaderBuilder.php index 0c0fa8ea..84da8de9 100644 --- a/src/Core/BulkItemsReader/BulkItemsReaderBuilder.php +++ b/src/Core/BulkItemsReader/BulkItemsReaderBuilder.php @@ -12,49 +12,29 @@ class BulkItemsReaderBuilder { - protected CoreInterface $core; - protected BatchOperationsInterface $batch; - protected LoggerInterface $logger; protected BulkItemsReaderInterface $readStrategy; - /** - * @param \Bitrix24\SDK\Core\Contracts\CoreInterface $core - * @param \Bitrix24\SDK\Core\Contracts\BatchOperationsInterface $batch - * @param \Psr\Log\LoggerInterface $logger - */ - public function __construct(CoreInterface $core, BatchOperationsInterface $batch, LoggerInterface $logger) + public function __construct(protected CoreInterface $core, protected BatchOperationsInterface $batch, protected LoggerInterface $logger) { - $this->core = $core; - $this->batch = $batch; - $this->logger = $logger; $this->readStrategy = $this->getOptimalReadStrategy(); } - /** - * @param \Bitrix24\SDK\Core\Contracts\BulkItemsReaderInterface $readStrategy - * - * @return BulkItemsReaderBuilder - */ - public function withReadStrategy(BulkItemsReaderInterface $readStrategy): BulkItemsReaderBuilder + + public function withReadStrategy(BulkItemsReaderInterface $bulkItemsReader): BulkItemsReaderBuilder { - $this->readStrategy = $readStrategy; + $this->readStrategy = $bulkItemsReader; return $this; } /** * Get optimal read strategy based on integration tests with time and performance benchmarks - * - * @return \Bitrix24\SDK\Core\Contracts\BulkItemsReaderInterface */ protected function getOptimalReadStrategy(): BulkItemsReaderInterface { return new FilterWithBatchWithoutCountOrder($this->batch, $this->logger); } - /** - * @return \Bitrix24\SDK\Core\Contracts\BulkItemsReaderInterface - */ public function build(): BulkItemsReaderInterface { return new BulkItemsReader($this->readStrategy, $this->logger); diff --git a/src/Core/BulkItemsReader/ReadStrategies/FilterWithBatchWithoutCountOrder.php b/src/Core/BulkItemsReader/ReadStrategies/FilterWithBatchWithoutCountOrder.php index b8eed12d..cfe7f243 100644 --- a/src/Core/BulkItemsReader/ReadStrategies/FilterWithBatchWithoutCountOrder.php +++ b/src/Core/BulkItemsReader/ReadStrategies/FilterWithBatchWithoutCountOrder.php @@ -11,27 +11,12 @@ class FilterWithBatchWithoutCountOrder implements BulkItemsReaderInterface { - private BatchOperationsInterface $batch; - private LoggerInterface $log; - - /** - * @param \Bitrix24\SDK\Core\Contracts\BatchOperationsInterface $batch - * @param \Psr\Log\LoggerInterface $log - */ - public function __construct(BatchOperationsInterface $batch, LoggerInterface $log) + public function __construct(private readonly BatchOperationsInterface $batch, private readonly LoggerInterface $log) { - $this->batch = $batch; - $this->log = $log; } /** - * @param string $apiMethod - * @param array $order - * @param array $filter - * @param array $select - * @param int|null $limit * - * @return \Generator * @throws \Bitrix24\SDK\Core\Exceptions\BaseException * @throws \Bitrix24\SDK\Core\Exceptions\TransportException */ diff --git a/src/Core/BulkItemsReader/ReadStrategies/FilterWithoutBatchWithoutCountOrder.php b/src/Core/BulkItemsReader/ReadStrategies/FilterWithoutBatchWithoutCountOrder.php index e1d6c8c8..cdcf43d0 100644 --- a/src/Core/BulkItemsReader/ReadStrategies/FilterWithoutBatchWithoutCountOrder.php +++ b/src/Core/BulkItemsReader/ReadStrategies/FilterWithoutBatchWithoutCountOrder.php @@ -11,27 +11,12 @@ class FilterWithoutBatchWithoutCountOrder implements BulkItemsReaderInterface { - private CoreInterface $core; - private LoggerInterface $log; - - /** - * @param \Bitrix24\SDK\Core\Contracts\CoreInterface $core - * @param \Psr\Log\LoggerInterface $log - */ - public function __construct(CoreInterface $core, LoggerInterface $log) + public function __construct(private readonly CoreInterface $core, private readonly LoggerInterface $log) { - $this->core = $core; - $this->log = $log; } /** - * @param string $apiMethod - * @param array $order - * @param array $filter - * @param array $select - * @param int|null $limit * - * @return \Generator * @throws \Bitrix24\SDK\Core\Exceptions\BaseException * @throws \Bitrix24\SDK\Core\Exceptions\TransportException */ @@ -87,6 +72,7 @@ public function getTraversableList(string $apiMethod, array $order, array $filte return; } + $lastElementId = $this->getLastElementId($apiMethod, $filter, $select); if ($lastElementId === null) { $this->log->debug('FilterWithoutBatchWithoutCountOrder.getTraversableList.emptySelect'); @@ -140,11 +126,7 @@ public function getTraversableList(string $apiMethod, array $order, array $filte /** * Get first element id in filtered result ordered by id asc * - * @param string $apiMethod - * @param array $filter - * @param array $select * - * @return int|null * @throws \Bitrix24\SDK\Core\Exceptions\BaseException * @throws \Bitrix24\SDK\Core\Exceptions\TransportException * @todo Кандидат на вынос @@ -157,7 +139,7 @@ private function getFirstElementId(string $apiMethod, array $filter, array $sele 'select' => $select, ]); - $firstResultPage = $this->core->call( + $response = $this->core->call( $apiMethod, [ 'order' => ['ID' => 'ASC'], @@ -167,7 +149,7 @@ private function getFirstElementId(string $apiMethod, array $filter, array $sele ] ); - $elementId = $firstResultPage->getResponseData()->getResult()[0]['ID']; + $elementId = $response->getResponseData()->getResult()[0]['ID']; $this->log->debug('FilterWithoutBatchWithoutCountOrder.getFirstElementId.finish', [ 'elementId' => $elementId, @@ -179,11 +161,7 @@ private function getFirstElementId(string $apiMethod, array $filter, array $sele /** * Get first element id in filtered result ordered by id asc * - * @param string $apiMethod - * @param array $filter - * @param array $select * - * @return int|null * @throws \Bitrix24\SDK\Core\Exceptions\BaseException * @throws \Bitrix24\SDK\Core\Exceptions\TransportException * @todo Кандидат на вынос @@ -196,7 +174,7 @@ private function getLastElementId(string $apiMethod, array $filter, array $selec 'select' => $select, ]); - $lastResultPage = $this->core->call( + $response = $this->core->call( $apiMethod, [ 'order' => ['ID' => 'DESC'], @@ -206,7 +184,7 @@ private function getLastElementId(string $apiMethod, array $filter, array $selec ] ); - $elementId = $lastResultPage->getResponseData()->getResult()[0]['ID']; + $elementId = $response->getResponseData()->getResult()[0]['ID']; $this->log->debug('FilterWithoutBatchWithoutCountOrder.getLastElementId.finish', [ 'elementId' => $elementId, diff --git a/src/Core/Commands/Command.php b/src/Core/Commands/Command.php index 471bb9dd..a4a6e37e 100644 --- a/src/Core/Commands/Command.php +++ b/src/Core/Commands/Command.php @@ -14,67 +14,37 @@ */ class Command { - /** - * @var string - */ - private $apiMethod; - /** - * @var array - */ - private $parameters; - /** - * @var null|string - */ - private $name; - /** - * @var UuidInterface - */ - private $uuid; + private readonly string $name; + + private readonly \Ramsey\Uuid\UuidInterface $uuid; /** * BatchCommand constructor. * - * @param string $apiMethod - * @param array $parameters - * @param string|null $name * * @throws \Exception */ - public function __construct(string $apiMethod, array $parameters, ?string $name = null) + public function __construct(private readonly string $apiMethod, private readonly array $parameters, ?string $name = null) { $this->uuid = Uuid::uuid4(); - $this->apiMethod = $apiMethod; - $this->parameters = $parameters; $this->name = $name ?? $this->uuid->toString(); } - /** - * @return UuidInterface - */ public function getUuid(): UuidInterface { return $this->uuid; } - /** - * @return string - */ public function getApiMethod(): string { return $this->apiMethod; } - /** - * @return array - */ public function getParameters(): array { return $this->parameters; } - /** - * @return string|null - */ public function getName(): ?string { return $this->name; diff --git a/src/Core/Contracts/AddedItemIdResultInterface.php b/src/Core/Contracts/AddedItemIdResultInterface.php index e9adcf78..465bd43c 100644 --- a/src/Core/Contracts/AddedItemIdResultInterface.php +++ b/src/Core/Contracts/AddedItemIdResultInterface.php @@ -8,8 +8,6 @@ interface AddedItemIdResultInterface { /** * added entity id - * - * @return int */ public function getId(): int; } \ No newline at end of file diff --git a/src/Core/Contracts/ApiClientInterface.php b/src/Core/Contracts/ApiClientInterface.php index f4782170..11903492 100644 --- a/src/Core/Contracts/ApiClientInterface.php +++ b/src/Core/Contracts/ApiClientInterface.php @@ -13,24 +13,16 @@ interface ApiClientInterface { /** - * @param string $apiMethod - * @param array $parameters - * - * @return ResponseInterface * @throws TransportExceptionInterface * @throws InvalidArgumentException */ public function getResponse(string $apiMethod, array $parameters = []): ResponseInterface; /** - * @return RenewedAccessToken * @throws InvalidArgumentException * @throws TransportExceptionInterface */ public function getNewAccessToken(): RenewedAccessToken; - /** - * @return Credentials - */ public function getCredentials(): Credentials; } \ No newline at end of file diff --git a/src/Core/Contracts/BatchOperationsInterface.php b/src/Core/Contracts/BatchOperationsInterface.php index f6e5b55c..e11c804b 100644 --- a/src/Core/Contracts/BatchOperationsInterface.php +++ b/src/Core/Contracts/BatchOperationsInterface.php @@ -18,12 +18,9 @@ interface BatchOperationsInterface /** * Batch wrapper for *.list methods without counting elements on every api-call * - * @param string $apiMethod * @param array $order * @param array $filter * @param array $select - * @param int|null $limit - * @param array|null $additionalParameters * @return Generator * @throws BaseException */ @@ -41,13 +38,7 @@ public function getTraversableList( * * ⚠️ Call this wrapper is more expensive than getTraversableList method, use this method carefully * - * @param string $apiMethod - * @param array $order - * @param array $filter - * @param array $select - * @param int|null $limit * - * @return Generator * @throws BaseException */ public function getTraversableListWithCount( @@ -61,7 +52,6 @@ public function getTraversableListWithCount( /** * Add entity items with batch call * - * @param string $apiMethod * @param array $entityItems * * @return Generator|ResponseData[] @@ -72,7 +62,6 @@ public function addEntityItems(string $apiMethod, array $entityItems): Generator /** * Delete entity items with batch call * - * @param string $apiMethod * @param array $entityItemId * * @return Generator|ResponseData[] @@ -83,7 +72,6 @@ public function deleteEntityItems(string $apiMethod, array $entityItemId): Gener /** * Update entity items with batch call * - * @param string $apiMethod * @param array $entityItems * * @return Generator|ResponseData[] diff --git a/src/Core/Contracts/BulkItemsReaderInterface.php b/src/Core/Contracts/BulkItemsReaderInterface.php index 515dcb55..9c3ee383 100644 --- a/src/Core/Contracts/BulkItemsReaderInterface.php +++ b/src/Core/Contracts/BulkItemsReaderInterface.php @@ -20,7 +20,6 @@ interface BulkItemsReaderInterface * @param array $select select element fields * @param int|null $limit limit elements or read all elements * - * @return Generator * @throws BaseException */ public function getTraversableList(string $apiMethod, array $order, array $filter, array $select, ?int $limit = null): Generator; diff --git a/src/Core/Contracts/CoreInterface.php b/src/Core/Contracts/CoreInterface.php index 85fa6201..d58ccf33 100644 --- a/src/Core/Contracts/CoreInterface.php +++ b/src/Core/Contracts/CoreInterface.php @@ -16,17 +16,10 @@ interface CoreInterface { /** - * @param string $apiMethod - * @param array $parameters - * - * @return Response * @throws BaseException * @throws TransportException */ public function call(string $apiMethod, array $parameters = []): Response; - /** - * @return \Bitrix24\SDK\Core\Contracts\ApiClientInterface - */ public function getApiClient(): ApiClientInterface; } \ No newline at end of file diff --git a/src/Core/Contracts/DeletedItemResultInterface.php b/src/Core/Contracts/DeletedItemResultInterface.php index 8241f02e..053bf8c7 100644 --- a/src/Core/Contracts/DeletedItemResultInterface.php +++ b/src/Core/Contracts/DeletedItemResultInterface.php @@ -8,8 +8,6 @@ interface DeletedItemResultInterface { /** * Success deletion flag - * - * @return bool */ public function isSuccess(): bool; } \ No newline at end of file diff --git a/src/Core/Contracts/UpdatedItemResultInterface.php b/src/Core/Contracts/UpdatedItemResultInterface.php index a1dda726..7e95eda2 100644 --- a/src/Core/Contracts/UpdatedItemResultInterface.php +++ b/src/Core/Contracts/UpdatedItemResultInterface.php @@ -8,8 +8,6 @@ interface UpdatedItemResultInterface { /** * Success update flag - * - * @return bool */ public function isSuccess(): bool; } \ No newline at end of file diff --git a/src/Core/Core.php b/src/Core/Core.php index a1a473f9..bbbe7ef6 100644 --- a/src/Core/Core.php +++ b/src/Core/Core.php @@ -16,6 +16,7 @@ use Bitrix24\SDK\Events\PortalDomainUrlChangedEvent; use Fig\Http\Message\StatusCodeInterface; use Psr\Log\LoggerInterface; +use Symfony\Component\HttpClient\Exception\JsonException; use Symfony\Contracts\EventDispatcher\EventDispatcherInterface; use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface; @@ -26,37 +27,14 @@ */ class Core implements CoreInterface { - protected ApiClientInterface $apiClient; - protected LoggerInterface $logger; - protected EventDispatcherInterface $eventDispatcher; - protected ApiLevelErrorHandler $apiLevelErrorHandler; - /** * Main constructor. - * - * @param ApiClientInterface $apiClient - * @param ApiLevelErrorHandler $apiLevelErrorHandler - * @param EventDispatcherInterface $eventDispatcher - * @param LoggerInterface $logger */ - public function __construct( - ApiClientInterface $apiClient, - ApiLevelErrorHandler $apiLevelErrorHandler, - EventDispatcherInterface $eventDispatcher, - LoggerInterface $logger - ) + public function __construct(protected ApiClientInterface $apiClient, protected ApiLevelErrorHandler $apiLevelErrorHandler, protected EventDispatcherInterface $eventDispatcher, protected LoggerInterface $logger) { - $this->apiClient = $apiClient; - $this->apiLevelErrorHandler = $apiLevelErrorHandler; - $this->eventDispatcher = $eventDispatcher; - $this->logger = $logger; } /** - * @param string $apiMethod - * @param array $parameters - * - * @return Response * @throws BaseException * @throws TransportException */ @@ -163,6 +141,7 @@ public function call(string $apiMethod, array $parameters = []): Response default: throw new BaseException('UNAUTHORIZED request error'); } + break; case StatusCodeInterface::STATUS_FORBIDDEN: $body = $apiCallResponse->toArray(false); @@ -198,7 +177,7 @@ public function call(string $apiMethod, array $parameters = []): Response $this->apiLevelErrorHandler->handle($body); break; } - } catch (TransportExceptionInterface $exception) { + } catch (TransportExceptionInterface|JsonException $exception) { // catch symfony http client transport exception $this->logger->error( 'call.transportException', @@ -207,7 +186,7 @@ public function call(string $apiMethod, array $parameters = []): Response 'message' => $exception->getMessage(), ] ); - throw new TransportException(sprintf('transport error - %s', $exception->getMessage()), $exception->getCode(), $exception); + throw new TransportException(sprintf('transport error - %s, type %s', $exception->getMessage(), $exception::class), $exception->getCode(), $exception); } catch (BaseException $exception) { // rethrow known bitrix24 php sdk exception throw $exception; @@ -216,19 +195,18 @@ public function call(string $apiMethod, array $parameters = []): Response 'call.unknownException', [ 'message' => $exception->getMessage(), + 'class' => $exception::class, 'trace' => $exception->getTrace(), ] ); throw new BaseException(sprintf('unknown error - %s', $exception->getMessage()), $exception->getCode(), $exception); } + $this->logger->debug('call.finish'); return $response; } - /** - * @return \Bitrix24\SDK\Core\Contracts\ApiClientInterface - */ public function getApiClient(): ApiClientInterface { return $this->apiClient; diff --git a/src/Core/CoreBuilder.php b/src/Core/CoreBuilder.php index 8f0d7960..96572cc1 100644 --- a/src/Core/CoreBuilder.php +++ b/src/Core/CoreBuilder.php @@ -25,12 +25,18 @@ */ class CoreBuilder { - private ?ApiClientInterface $apiClient; + private ?ApiClientInterface $apiClient = null; + private HttpClientInterface $httpClient; + private EventDispatcherInterface $eventDispatcher; + private LoggerInterface $logger; - private ?Credentials $credentials; - private ApiLevelErrorHandler $apiLevelErrorHandler; + + private ?Credentials $credentials = null; + + private readonly ApiLevelErrorHandler $apiLevelErrorHandler; + private RequestIdGeneratorInterface $requestIdGenerator; /** @@ -46,8 +52,6 @@ public function __construct() 'timeout' => 120, ] ); - $this->credentials = null; - $this->apiClient = null; $this->apiLevelErrorHandler = new ApiLevelErrorHandler($this->logger); $this->requestIdGenerator = new DefaultRequestIdGenerator(); } @@ -58,8 +62,6 @@ public function withRequestIdGenerator(RequestIdGeneratorInterface $requestIdGen } /** - * @param Credentials $credentials - * * @return $this */ public function withCredentials(Credentials $credentials): self @@ -102,11 +104,11 @@ public function withEventDispatcher(EventDispatcherInterface $eventDispatcher): */ public function build(): CoreInterface { - if ($this->credentials === null) { + if (!$this->credentials instanceof \Bitrix24\SDK\Core\Credentials\Credentials) { throw new InvalidArgumentException('you must set credentials before call method build'); } - if ($this->apiClient === null) { + if (!$this->apiClient instanceof \Bitrix24\SDK\Core\Contracts\ApiClientInterface) { $this->apiClient = new ApiClient( $this->credentials, $this->httpClient, diff --git a/src/Core/Credentials/AccessToken.php b/src/Core/Credentials/AccessToken.php index 54e26478..ebb02d3c 100644 --- a/src/Core/Credentials/AccessToken.php +++ b/src/Core/Credentials/AccessToken.php @@ -10,33 +10,17 @@ class AccessToken { - protected string $accessToken; - protected ?string $refreshToken; - protected int $expires; - protected ?int $expiresIn; - /** * AccessToken constructor. - * - * @param string $accessToken - * @param string|null $refreshToken - * @param int $expires - * @param int|null $expiresIn */ - public function __construct(string $accessToken, ?string $refreshToken, int $expires, ?int $expiresIn = null) + public function __construct(protected string $accessToken, protected ?string $refreshToken, protected int $expires, protected ?int $expiresIn = null) { - $this->accessToken = $accessToken; - $this->refreshToken = $refreshToken; - $this->expires = $expires; - $this->expiresIn = $expiresIn; } /** * Is this one-off token from event * * One-off tokens do not have refresh token field - * - * @return bool */ public function isOneOff(): bool { @@ -53,27 +37,17 @@ public function getRefreshToken(): ?string return $this->refreshToken; } - /** - * @return int - */ public function getExpires(): int { return $this->expires; } - /** - * @return bool - */ public function hasExpired(): bool { return $this->getExpires() <= time(); } - /** - * @param array $request - * - * @return self - */ + public static function initFromArray(array $request): self { return new self( @@ -109,9 +83,11 @@ public static function initFromPlacementRequest(HttpFoundation\Request $request) if (!array_key_exists('AUTH_ID', $requestFields)) { throw new InvalidArgumentException('field AUTH_ID not fount in request'); } + if (!array_key_exists('REFRESH_ID', $requestFields)) { throw new InvalidArgumentException('field REFRESH_ID not fount in request'); } + if (!array_key_exists('AUTH_EXPIRES', $requestFields)) { throw new InvalidArgumentException('field AUTH_EXPIRES not fount in request'); } diff --git a/src/Core/Credentials/ApplicationProfile.php b/src/Core/Credentials/ApplicationProfile.php index 9b8816da..dbad55ab 100644 --- a/src/Core/Credentials/ApplicationProfile.php +++ b/src/Core/Credentials/ApplicationProfile.php @@ -14,45 +14,28 @@ class ApplicationProfile { private const BITRIX24_PHP_SDK_APPLICATION_CLIENT_ID = 'BITRIX24_PHP_SDK_APPLICATION_CLIENT_ID'; + private const BITRIX24_PHP_SDK_APPLICATION_CLIENT_SECRET = 'BITRIX24_PHP_SDK_APPLICATION_CLIENT_SECRET'; + private const BITRIX24_PHP_SDK_APPLICATION_SCOPE = 'BITRIX24_PHP_SDK_APPLICATION_SCOPE'; - private string $clientId; - private string $clientSecret; - private Scope $scope; /** * ApplicationProfile constructor. - * - * @param string $clientId - * @param string $clientSecret - * @param Scope $scope */ - public function __construct(string $clientId, string $clientSecret, Scope $scope) + public function __construct(private readonly string $clientId, private readonly string $clientSecret, private readonly Scope $scope) { - $this->clientId = $clientId; - $this->clientSecret = $clientSecret; - $this->scope = $scope; } - /** - * @return string - */ public function getClientId(): string { return $this->clientId; } - /** - * @return string - */ public function getClientSecret(): string { return $this->clientSecret; } - /** - * @return Scope - */ public function getScope(): Scope { return $this->scope; @@ -66,9 +49,11 @@ public static function initFromArray(array $appProfile): self if (!array_key_exists(self::BITRIX24_PHP_SDK_APPLICATION_CLIENT_ID, $appProfile)) { throw new InvalidArgumentException(sprintf('in array key %s not found', self::BITRIX24_PHP_SDK_APPLICATION_CLIENT_ID)); } + if (!array_key_exists(self::BITRIX24_PHP_SDK_APPLICATION_CLIENT_SECRET, $appProfile)) { throw new InvalidArgumentException(sprintf('in array key %s not found', self::BITRIX24_PHP_SDK_APPLICATION_CLIENT_SECRET)); } + if (!array_key_exists(self::BITRIX24_PHP_SDK_APPLICATION_SCOPE, $appProfile)) { throw new InvalidArgumentException(sprintf('in array key %s not found', self::BITRIX24_PHP_SDK_APPLICATION_SCOPE)); } @@ -76,7 +61,7 @@ public static function initFromArray(array $appProfile): self return new self( $appProfile[self::BITRIX24_PHP_SDK_APPLICATION_CLIENT_ID], $appProfile[self::BITRIX24_PHP_SDK_APPLICATION_CLIENT_SECRET], - new Scope(str_replace(' ', '', explode(',', $appProfile[self::BITRIX24_PHP_SDK_APPLICATION_SCOPE]))), + new Scope(str_replace(' ', '', explode(',', (string) $appProfile[self::BITRIX24_PHP_SDK_APPLICATION_SCOPE]))), ); } } \ No newline at end of file diff --git a/src/Core/Credentials/Credentials.php b/src/Core/Credentials/Credentials.php index 476bfc38..9adae014 100644 --- a/src/Core/Credentials/Credentials.php +++ b/src/Core/Credentials/Credentials.php @@ -9,47 +9,31 @@ class Credentials { - protected ?WebhookUrl $webhookUrl; - protected ?AccessToken $accessToken; - protected ?ApplicationProfile $applicationProfile; - protected ?string $domainUrl; + protected ?string $domainUrl = null; /** - * Credentials constructor. - * - * @param WebhookUrl|null $webhookUrl - * @param AccessToken|null $accessToken - * @param ApplicationProfile|null $applicationProfile - * @param string|null $domainUrl - * * @throws InvalidArgumentException */ public function __construct( - ?WebhookUrl $webhookUrl, - ?AccessToken $accessToken, - ?ApplicationProfile $applicationProfile, - ?string $domainUrl + protected ?WebhookUrl $webhookUrl, + protected ?AccessToken $accessToken, + protected ?ApplicationProfile $applicationProfile, + ?string $domainUrl ) { - $this->webhookUrl = $webhookUrl; - $this->accessToken = $accessToken; - $this->applicationProfile = $applicationProfile; - if ($domainUrl !== null) { $this->setDomainUrl($domainUrl); } - if ($this->accessToken === null && $this->webhookUrl === null) { - throw new \LogicException('you must set on of auth type: webhook or OAuth 2.0'); + if (!$this->accessToken instanceof AccessToken && !$this->webhookUrl instanceof WebhookUrl) { + throw new InvalidArgumentException('you must set on of auth type: webhook or OAuth 2.0'); } - if ($this->accessToken !== null && $this->domainUrl === null) { - throw new \LogicException('for oauth type you must set domain url'); + + if ($this->accessToken instanceof AccessToken && $this->domainUrl === null) { + throw new InvalidArgumentException('for oauth type you must set domain url'); } } - /** - * @param AccessToken $accessToken - */ public function setAccessToken(AccessToken $accessToken): void { $this->accessToken = $accessToken; @@ -58,9 +42,7 @@ public function setAccessToken(AccessToken $accessToken): void /** * Set domain url * - * @param string $domainUrl * - * @return void * @throws InvalidArgumentException */ public function setDomainUrl(string $domainUrl): void @@ -73,56 +55,38 @@ public function setDomainUrl(string $domainUrl): void if (filter_var($domainUrl, FILTER_VALIDATE_URL) === false) { throw new InvalidArgumentException(sprintf('domain URL %s is invalid', $domainUrl)); } + $this->domainUrl = $domainUrl; } public function isWebhookContext(): bool { - return $this->webhookUrl !== null && $this->accessToken === null; + return $this->webhookUrl instanceof WebhookUrl && !$this->accessToken instanceof AccessToken; } - /** - * @return ApplicationProfile|null - */ public function getApplicationProfile(): ?ApplicationProfile { return $this->applicationProfile; } - /** - * @return string - */ public function getDomainUrl(): string { - if ($this->getWebhookUrl() !== null) { - $arUrl = parse_url($this->getWebhookUrl()->getUrl()); - } else { - $arUrl = parse_url($this->domainUrl); - } + $arUrl = $this->getWebhookUrl() instanceof WebhookUrl ? parse_url($this->getWebhookUrl()->getUrl()) : parse_url((string)$this->domainUrl); return sprintf('%s://%s', $arUrl['scheme'], $arUrl['host']); } - /** - * @return WebhookUrl|null - */ public function getWebhookUrl(): ?WebhookUrl { return $this->webhookUrl; } - /** - * @return AccessToken|null - */ public function getAccessToken(): ?AccessToken { return $this->accessToken; } /** - * @param WebhookUrl $webhookUrl - * - * @return self * @throws InvalidArgumentException */ public static function createFromWebhook(WebhookUrl $webhookUrl): self @@ -136,11 +100,7 @@ public static function createFromWebhook(WebhookUrl $webhookUrl): self } /** - * @param AccessToken $accessToken - * @param ApplicationProfile $applicationProfile - * @param string $domainUrl * - * @return self * @throws InvalidArgumentException */ public static function createFromOAuth(AccessToken $accessToken, ApplicationProfile $applicationProfile, string $domainUrl): self @@ -154,10 +114,7 @@ public static function createFromOAuth(AccessToken $accessToken, ApplicationProf } /** - * @param \Bitrix24\SDK\Application\Requests\Placement\PlacementRequest $placementRequest - * @param \Bitrix24\SDK\Core\Credentials\ApplicationProfile $applicationProfile * - * @return self * @throws InvalidArgumentException */ public static function createFromPlacementRequest(PlacementRequest $placementRequest, ApplicationProfile $applicationProfile): self diff --git a/src/Core/Credentials/Endpoints.php b/src/Core/Credentials/Endpoints.php index 9c826eb7..6429ed02 100644 --- a/src/Core/Credentials/Endpoints.php +++ b/src/Core/Credentials/Endpoints.php @@ -23,6 +23,7 @@ public function __construct( if (filter_var($authServerUrl, FILTER_VALIDATE_URL) === false) { throw new InvalidArgumentException(sprintf('authServer endpoint URL «%s» is invalid', $authServerUrl)); } + if (filter_var($clientUrl, FILTER_VALIDATE_URL) === false) { throw new InvalidArgumentException(sprintf('client endpoint URL «%s» is invalid', $clientUrl)); } diff --git a/src/Core/Credentials/Scope.php b/src/Core/Credentials/Scope.php index b353d598..dae5cbb4 100644 --- a/src/Core/Credentials/Scope.php +++ b/src/Core/Credentials/Scope.php @@ -73,15 +73,11 @@ class Scope 'userfieldconfig', ]; - /** - * @var array - */ protected array $currentScope = []; /** * Scope constructor. * - * @param array $scope * * @throws UnknownScopeCodeException */ @@ -102,9 +98,6 @@ public function __construct(array $scope = []) $this->currentScope = $scope; } - /** - * @return array - */ public function getScopeCodes(): array { return $this->currentScope; diff --git a/src/Core/Credentials/WebhookUrl.php b/src/Core/Credentials/WebhookUrl.php index e33cabd0..8d7a8769 100644 --- a/src/Core/Credentials/WebhookUrl.php +++ b/src/Core/Credentials/WebhookUrl.php @@ -16,8 +16,6 @@ class WebhookUrl protected string $url; /** - * @param string $webhookUrl - * * @throws \Bitrix24\SDK\Core\Exceptions\InvalidArgumentException */ public function __construct(string $webhookUrl) @@ -25,12 +23,10 @@ public function __construct(string $webhookUrl) if (filter_var($webhookUrl, FILTER_VALIDATE_URL) === false) { throw new InvalidArgumentException(sprintf('webhook URL %s is invalid', $webhookUrl)); } + $this->url = $webhookUrl; } - /** - * @return string - */ public function getUrl(): string { return $this->url; diff --git a/src/Core/Exceptions/MethodConfirmWaitingException.php b/src/Core/Exceptions/MethodConfirmWaitingException.php index 2b282151..94141e00 100644 --- a/src/Core/Exceptions/MethodConfirmWaitingException.php +++ b/src/Core/Exceptions/MethodConfirmWaitingException.php @@ -8,11 +8,8 @@ class MethodConfirmWaitingException extends BaseException { - public readonly string $methodName; - - public function __construct(string $methodName, string $message, int $code = 0, ?Throwable $previous = null) + public function __construct(public readonly string $methodName, string $message, int $code = 0, ?Throwable $throwable = null) { - parent::__construct($message, $code, $previous); - $this->methodName = $methodName; + parent::__construct($message, $code, $throwable); } } \ No newline at end of file diff --git a/src/Core/Response/DTO/Pagination.php b/src/Core/Response/DTO/Pagination.php index 3f1816ef..12a7d78e 100644 --- a/src/Core/Response/DTO/Pagination.php +++ b/src/Core/Response/DTO/Pagination.php @@ -4,39 +4,19 @@ namespace Bitrix24\SDK\Core\Response\DTO; -/** - * Class Pagination - * - * @package Bitrix24\SDK\Core\Response\DTO - */ -class Pagination +readonly class Pagination { - private ?int $nextItem; - private ?int $total; - - /** - * Pagination constructor. - * - * @param int|null $nextItem - * @param int|null $total - */ - public function __construct(int $nextItem = null, int $total = null) + public function __construct( + private ?int $nextItem = null, + private ?int $total = null) { - $this->nextItem = $nextItem; - $this->total = $total; } - /** - * @return int|null - */ public function getNextItem(): ?int { return $this->nextItem; } - /** - * @return int|null - */ public function getTotal(): ?int { return $this->total; diff --git a/src/Core/Response/DTO/RenewedAccessToken.php b/src/Core/Response/DTO/RenewedAccessToken.php index 3ed060da..9b1cbde3 100644 --- a/src/Core/Response/DTO/RenewedAccessToken.php +++ b/src/Core/Response/DTO/RenewedAccessToken.php @@ -7,99 +7,51 @@ use Bitrix24\SDK\Core\Credentials\AccessToken; use Bitrix24\SDK\Core\Credentials\Scope; -/** - * Class RenewedAccessToken - * - * @package Bitrix24\SDK\Core\Response\DTO - */ -class RenewedAccessToken +readonly class RenewedAccessToken { - private AccessToken $accessToken; - private string $memberId; - private string $clientEndpoint; - private string $serverEndpoint; - private string $applicationStatus; - private string $domain; - /** * RenewedAccessToken constructor. - * - * @param AccessToken $accessToken - * @param string $memberId - * @param string $clientEndpoint - * @param string $serverEndpoint - * @param string $applicationStatus - * @param string $domain */ public function __construct( - AccessToken $accessToken, - string $memberId, - string $clientEndpoint, - string $serverEndpoint, - string $applicationStatus, - string $domain - ) { - $this->accessToken = $accessToken; - $this->memberId = $memberId; - $this->clientEndpoint = $clientEndpoint; - $this->serverEndpoint = $serverEndpoint; - $this->applicationStatus = $applicationStatus; - $this->domain = $domain; + private AccessToken $accessToken, + private string $memberId, + private string $clientEndpoint, + private string $serverEndpoint, + private string $applicationStatus, + private string $domain) + { } - /** - * @return AccessToken - */ public function getAccessToken(): AccessToken { return $this->accessToken; } - /** - * @return string - */ public function getMemberId(): string { return $this->memberId; } - /** - * @return string - */ public function getClientEndpoint(): string { return $this->clientEndpoint; } - /** - * @return string - */ public function getServerEndpoint(): string { return $this->serverEndpoint; } - /** - * @return string - */ public function getApplicationStatus(): string { return $this->applicationStatus; } - /** - * @return string - */ public function getDomain(): string { return $this->domain; } - /** - * @param array $response - * - * @return self - */ public static function initFromArray(array $response): self { return new self( diff --git a/src/Core/Response/DTO/ResponseData.php b/src/Core/Response/DTO/ResponseData.php index f50484ff..481defa0 100644 --- a/src/Core/Response/DTO/ResponseData.php +++ b/src/Core/Response/DTO/ResponseData.php @@ -4,50 +4,28 @@ namespace Bitrix24\SDK\Core\Response\DTO; -/** - * Class ResponseData - * - * @package Bitrix24\SDK\Core\Response\DTO - */ class ResponseData { - protected array $result; - protected Time $time; - protected Pagination $pagination; - /** * ResponseData constructor. - * - * @param array $result - * @param Time $time - * @param Pagination $pagination */ - public function __construct(array $result, Time $time, Pagination $pagination) + public function __construct( + protected array $result, + readonly protected Time $time, + readonly protected Pagination $pagination) { - $this->result = $result; - $this->time = $time; - $this->pagination = $pagination; } - /** - * @return Pagination - */ public function getPagination(): Pagination { return $this->pagination; } - /** - * @return Time - */ public function getTime(): Time { return $this->time; } - /** - * @return array - */ public function getResult(): array { return $this->result; diff --git a/src/Core/Response/DTO/Time.php b/src/Core/Response/DTO/Time.php index 3e83f84b..cfaaca27 100644 --- a/src/Core/Response/DTO/Time.php +++ b/src/Core/Response/DTO/Time.php @@ -5,133 +5,71 @@ namespace Bitrix24\SDK\Core\Response\DTO; use DateTimeImmutable; +use Exception; -/** - * Class Time - * - * @package Bitrix24\SDK\Core\Response\DTO - */ -class Time +readonly class Time { - private float $start; - private float $finish; - private float $duration; - private float $processing; - /** - * @var float $operating sum of query execution time - * @see https://training.bitrix24.com/rest_help/rest_sum/operating.php - */ - private float $operating; // time in seconds - private DateTimeImmutable $dateStart; - private DateTimeImmutable $dateFinish; - /** - * @var int|null time to reset nearest limit part - * @see https://training.bitrix24.com/rest_help/rest_sum/operating.php - */ - private ?int $operatingResetAt; - - /** - * Time constructor. - * - * @param float $start - * @param float $finish - * @param float $duration - * @param float $processing - * @param float $operating - * @param \DateTimeImmutable $dateStart - * @param \DateTimeImmutable $dateFinish - * @param int|null $operatingResetAt - */ public function __construct( - float $start, - float $finish, - float $duration, - float $processing, - float $operating, - DateTimeImmutable $dateStart, - DateTimeImmutable $dateFinish, - ?int $operatingResetAt + private float $start, + private float $finish, + private float $duration, + private float $processing, + /** + * @see https://training.bitrix24.com/rest_help/rest_sum/operating.php + */ + private float $operating, + private DateTimeImmutable $dateStart, + private DateTimeImmutable $dateFinish, + /** + * @see https://training.bitrix24.com/rest_help/rest_sum/operating.php + */ + private ?int $operatingResetAt ) { - $this->start = $start; - $this->finish = $finish; - $this->duration = $duration; - $this->processing = $processing; - $this->operating = $operating; - $this->dateStart = $dateStart; - $this->dateFinish = $dateFinish; - $this->operatingResetAt = $operatingResetAt; } - /** - * @return float - */ public function getStart(): float { return $this->start; } - /** - * @return float - */ public function getFinish(): float { return $this->finish; } - /** - * @return float - */ public function getDuration(): float { return $this->duration; } - /** - * @return float - */ public function getProcessing(): float { return $this->processing; } - /** - * @return float - */ public function getOperating(): float { return $this->operating; } - /** - * @return int|null - */ public function getOperatingResetAt(): ?int { return $this->operatingResetAt; } - /** - * @return \DateTimeImmutable - */ public function getDateStart(): DateTimeImmutable { return $this->dateStart; } - /** - * @return \DateTimeImmutable - */ public function getDateFinish(): DateTimeImmutable { return $this->dateFinish; } /** - * @param array $response - * - * @return self - * @throws \Exception + * @throws Exception */ public static function initFromResponse(array $response): self { diff --git a/src/Core/Response/Response.php b/src/Core/Response/Response.php index 7129653c..13cb3ac1 100644 --- a/src/Core/Response/Response.php +++ b/src/Core/Response/Response.php @@ -14,63 +14,39 @@ use Symfony\Contracts\HttpClient\ResponseInterface; use Throwable; -/** - * Class Response - * - * @package Bitrix24\SDK\Core\Response - */ class Response { - protected ResponseInterface $httpResponse; - protected ?DTO\ResponseData $responseData; - protected Command $apiCommand; - protected ApiLevelErrorHandler $apiLevelErrorHandler; - protected LoggerInterface $logger; + protected ?DTO\ResponseData $responseData = null; /** * Response constructor. - * - * @param ResponseInterface $httpResponse - * @param Command $apiCommand - * @param ApiLevelErrorHandler $apiLevelErrorHandler - * @param LoggerInterface $logger */ - public function __construct(ResponseInterface $httpResponse, Command $apiCommand, - ApiLevelErrorHandler $apiLevelErrorHandler, - LoggerInterface $logger) + public function __construct( + protected ResponseInterface $httpResponse, + protected Command $apiCommand, + protected ApiLevelErrorHandler $apiLevelErrorHandler, + protected LoggerInterface $logger) { - $this->httpResponse = $httpResponse; - $this->apiCommand = $apiCommand; - $this->apiLevelErrorHandler = $apiLevelErrorHandler; - $this->logger = $logger; - $this->responseData = null; } - /** - * @return ResponseInterface - */ public function getHttpResponse(): ResponseInterface { return $this->httpResponse; } - /** - * @return Command - */ public function getApiCommand(): Command { return $this->apiCommand; } /** - * @return DTO\ResponseData * @throws BaseException */ public function getResponseData(): DTO\ResponseData { $this->logger->debug('getResponseData.start'); - if ($this->responseData === null) { + if (!$this->responseData instanceof \Bitrix24\SDK\Core\Response\DTO\ResponseData) { try { $this->logger->debug('getResponseData.parseResponse.start'); $responseResult = $this->httpResponse->toArray(true); @@ -90,6 +66,7 @@ public function getResponseData(): DTO\ResponseData if (array_key_exists('next', $responseResult)) { $nextItem = (int)$responseResult['next']; } + if (array_key_exists('total', $responseResult)) { $total = (int)$responseResult['total']; } @@ -110,21 +87,19 @@ public function getResponseData(): DTO\ResponseData throw new BaseException(sprintf('api request error: %s', $exception->getMessage()), $exception->getCode(), $exception); } } + $this->logger->debug('getResponseData.finish'); return $this->responseData; } - /** - * @return string|null - */ private function getHttpResponseContent(): ?string { $content = null; try { $content = $this->httpResponse->getContent(false); - } catch (Throwable $exception) { - $this->logger->error($exception->getMessage()); + } catch (Throwable $throwable) { + $this->logger->error($throwable->getMessage()); } return $content; diff --git a/src/Core/Result/AbstractItem.php b/src/Core/Result/AbstractItem.php index 5522a869..4d13d439 100644 --- a/src/Core/Result/AbstractItem.php +++ b/src/Core/Result/AbstractItem.php @@ -18,19 +18,15 @@ */ abstract class AbstractItem implements IteratorAggregate { - protected array $data; protected DecimalMoneyParser $decimalMoneyParser; - public function __construct(array $data) + public function __construct(protected array $data) { - $this->data = $data; $this->decimalMoneyParser = new DecimalMoneyParser(new ISOCurrencies()); } /** * @param int|string $offset - * - * @return bool */ public function __isset($offset): bool { @@ -49,13 +45,12 @@ public function __get($offset) /** * @param int|string $offset - * @param mixed $value * * @return void * @throws ImmutableResultViolationException * */ - public function __set($offset, $value) + public function __set($offset, mixed $value) { throw new ImmutableResultViolationException(sprintf('Result is immutable, violation at offset %s', $offset)); } @@ -78,11 +73,7 @@ public function getIterator(): Traversable return new ArrayIterator($this->data); } - /** - * @param string $key - * - * @return bool - */ + protected function isKeyExists(string $key): bool { return array_key_exists($key, $this->data); diff --git a/src/Core/Result/AbstractResult.php b/src/Core/Result/AbstractResult.php index 1eac6f05..b3fae786 100644 --- a/src/Core/Result/AbstractResult.php +++ b/src/Core/Result/AbstractResult.php @@ -13,21 +13,13 @@ */ abstract class AbstractResult { - protected Response $coreResponse; - /** * AbstractResult constructor. - * - * @param Response $coreResponse */ - public function __construct(Response $coreResponse) + public function __construct(protected Response $coreResponse) { - $this->coreResponse = $coreResponse; } - /** - * @return Response - */ public function getCoreResponse(): Response { return $this->coreResponse; diff --git a/src/Core/Result/AddedItemBatchResult.php b/src/Core/Result/AddedItemBatchResult.php index 14180bbe..eb765e6f 100644 --- a/src/Core/Result/AddedItemBatchResult.php +++ b/src/Core/Result/AddedItemBatchResult.php @@ -9,19 +9,10 @@ class AddedItemBatchResult implements AddedItemIdResultInterface { - private ResponseData $responseData; - - /** - * @param \Bitrix24\SDK\Core\Response\DTO\ResponseData $responseData - */ - public function __construct(ResponseData $responseData) + public function __construct(private readonly ResponseData $responseData) { - $this->responseData = $responseData; } - /** - * @return \Bitrix24\SDK\Core\Response\DTO\ResponseData - */ public function getResponseData(): ResponseData { return $this->responseData; diff --git a/src/Core/Result/AddedItemResult.php b/src/Core/Result/AddedItemResult.php index bb491edc..509d0e4d 100644 --- a/src/Core/Result/AddedItemResult.php +++ b/src/Core/Result/AddedItemResult.php @@ -15,7 +15,6 @@ class AddedItemResult extends AbstractResult implements AddedItemIdResultInterface { /** - * @return int * @throws BaseException */ public function getId(): int diff --git a/src/Core/Result/DeletedItemBatchResult.php b/src/Core/Result/DeletedItemBatchResult.php index d63e67ab..1d06b6f6 100644 --- a/src/Core/Result/DeletedItemBatchResult.php +++ b/src/Core/Result/DeletedItemBatchResult.php @@ -9,27 +9,15 @@ class DeletedItemBatchResult implements DeletedItemResultInterface { - private ResponseData $responseData; - - /** - * @param \Bitrix24\SDK\Core\Response\DTO\ResponseData $responseData - */ - public function __construct(ResponseData $responseData) + public function __construct(private readonly ResponseData $responseData) { - $this->responseData = $responseData; } - /** - * @return \Bitrix24\SDK\Core\Response\DTO\ResponseData - */ public function getResponseData(): ResponseData { return $this->responseData; } - /** - * @return bool - */ public function isSuccess(): bool { return (bool)$this->getResponseData()->getResult()[0]; diff --git a/src/Core/Result/DeletedItemResult.php b/src/Core/Result/DeletedItemResult.php index 4eb5b4c4..75f599a2 100644 --- a/src/Core/Result/DeletedItemResult.php +++ b/src/Core/Result/DeletedItemResult.php @@ -15,7 +15,6 @@ class DeletedItemResult extends AbstractResult implements DeletedItemResultInterface { /** - * @return bool * @throws BaseException */ public function isSuccess(): bool diff --git a/src/Core/Result/FieldsResult.php b/src/Core/Result/FieldsResult.php index b9d7b435..d1069c82 100644 --- a/src/Core/Result/FieldsResult.php +++ b/src/Core/Result/FieldsResult.php @@ -14,7 +14,6 @@ class FieldsResult extends AbstractResult { /** - * @return array * @throws BaseException */ public function getFieldsDescription(): array diff --git a/src/Core/Result/UpdatedItemBatchResult.php b/src/Core/Result/UpdatedItemBatchResult.php index e36de654..67250b73 100644 --- a/src/Core/Result/UpdatedItemBatchResult.php +++ b/src/Core/Result/UpdatedItemBatchResult.php @@ -9,27 +9,15 @@ class UpdatedItemBatchResult implements UpdatedItemResultInterface { - private ResponseData $responseData; - - /** - * @param \Bitrix24\SDK\Core\Response\DTO\ResponseData $responseData - */ - public function __construct(ResponseData $responseData) + public function __construct(private readonly ResponseData $responseData) { - $this->responseData = $responseData; } - /** - * @return \Bitrix24\SDK\Core\Response\DTO\ResponseData - */ public function getResponseData(): ResponseData { return $this->responseData; } - /** - * @return bool - */ public function isSuccess(): bool { return (bool)$this->getResponseData()->getResult()[0]; diff --git a/src/Core/Result/UpdatedItemResult.php b/src/Core/Result/UpdatedItemResult.php index 1995f034..0ebd4d07 100644 --- a/src/Core/Result/UpdatedItemResult.php +++ b/src/Core/Result/UpdatedItemResult.php @@ -14,7 +14,6 @@ class UpdatedItemResult extends AbstractResult { /** - * @return bool * @throws BaseException */ public function isSuccess(): bool diff --git a/src/Core/Result/UserInterfaceDialogCallResult.php b/src/Core/Result/UserInterfaceDialogCallResult.php index 1e12f581..35ca3f0d 100644 --- a/src/Core/Result/UserInterfaceDialogCallResult.php +++ b/src/Core/Result/UserInterfaceDialogCallResult.php @@ -9,7 +9,6 @@ class UserInterfaceDialogCallResult extends AbstractResult { /** - * @return bool * @throws BaseException */ public function isSuccess(): bool diff --git a/tests/Integration/Core/BatchGetTraversableTest.php b/tests/Integration/Core/BatchGetTraversableTest.php new file mode 100644 index 00000000..f21bdbf9 --- /dev/null +++ b/tests/Integration/Core/BatchGetTraversableTest.php @@ -0,0 +1,169 @@ +toRfc4122(); + // add contacts + $contacts = []; + for ($i = 0; $i < $greaterThanDefaultPageSize; $i++) { + $contacts[] = [ + 'fields' => [ + 'NAME' => 'name-' . $i, + 'ORIGINATOR_ID' => $originatorId + ] + ]; + } + $cnt = 0; + foreach ($this->batch->addEntityItems('crm.contact.add', $contacts) as $addedContactResult) { + $this->createdContactIds[] = $addedContactResult->getResult()[0]; + $cnt++; + } + $this->assertEquals(count($contacts), $cnt); + $this->assertEquals(count($contacts), $this->serviceBuilder->getCRMScope()->contact()->countByFilter([ + 'ORIGINATOR_ID' => $originatorId + ])); + + $readContactsId = []; + foreach ($this->batch->getTraversableList('crm.contact.list', + [], + [ + 'ORIGINATOR_ID' => $originatorId + ], + [ + 'ID', + 'NAME', + 'ORIGINATOR_ID' + ] + ) as $cnt => $itemContact) { + $readContactsId[] = $itemContact['ID']; + } + $this->assertEquals($this->createdContactIds, $readContactsId); + } + + #[TestDox('test get contacts in batch mode with more than one page but only one batch query and limit argument')] + public function testSingleBatchWithMoreThanOnePageAndLimit(): void + { + $greaterThanDefaultPageSize = 120; + $originatorId = Uuid::v7()->toRfc4122(); + // add contacts + $contacts = []; + for ($i = 0; $i < $greaterThanDefaultPageSize; $i++) { + $contacts[] = [ + 'fields' => [ + 'NAME' => 'name-' . $i, + 'ORIGINATOR_ID' => $originatorId + ] + ]; + } + $cnt = 0; + foreach ($this->batch->addEntityItems('crm.contact.add', $contacts) as $addedContactResult) { + $this->createdContactIds[] = $addedContactResult->getResult()[0]; + $cnt++; + } + $this->assertEquals(count($contacts), $cnt); + $this->assertEquals(count($contacts), $this->serviceBuilder->getCRMScope()->contact()->countByFilter([ + 'ORIGINATOR_ID' => $originatorId + ])); + + // test batch with limit + $readContactsId = []; + foreach ($this->batch->getTraversableList('crm.contact.list', + [], + [ + 'ORIGINATOR_ID' => $originatorId + ], + [ + 'ID', + 'NAME', + 'ORIGINATOR_ID' + ], + $greaterThanDefaultPageSize / 2 + ) as $cnt => $itemContact) { + $readContactsId[] = $itemContact['ID']; + } + $this->assertCount($greaterThanDefaultPageSize / 2, $readContactsId); + } + + #[TestDox('test get contacts in batch mode with less than one page but only one batch query and limit argument')] + public function testSingleBatchWithLessThanOnePageAndLimit(): void + { + $greaterThanDefaultPageSize = 40; + $originatorId = Uuid::v7()->toRfc4122(); + // add contacts + $contacts = []; + for ($i = 0; $i < $greaterThanDefaultPageSize; $i++) { + $contacts[] = [ + 'fields' => [ + 'NAME' => 'name-' . $i, + 'ORIGINATOR_ID' => $originatorId + ] + ]; + } + $cnt = 0; + foreach ($this->batch->addEntityItems('crm.contact.add', $contacts) as $addedContactResult) { + $this->createdContactIds[] = $addedContactResult->getResult()[0]; + $cnt++; + } + $this->assertEquals(count($contacts), $cnt); + $this->assertEquals(count($contacts), $this->serviceBuilder->getCRMScope()->contact()->countByFilter([ + 'ORIGINATOR_ID' => $originatorId + ])); + + // test batch with limit + $readContactsId = []; + foreach ($this->batch->getTraversableList('crm.contact.list', + [], + [ + 'ORIGINATOR_ID' => $originatorId + ], + [ + 'ID', + 'NAME', + 'ORIGINATOR_ID' + ], + $greaterThanDefaultPageSize / 2 + ) as $cnt => $itemContact) { + $readContactsId[] = $itemContact['ID']; + } + $this->assertCount($greaterThanDefaultPageSize / 2, $readContactsId); + } + + /** + * @throws InvalidArgumentException + */ + public function setUp(): void + { + $this->batch = Fabric::getBatchService(); + $this->serviceBuilder = Fabric::getServiceBuilder(); + } + + public function tearDown(): void + { + if ($this->createdContactIds !== null) { + foreach ($this->batch->deleteEntityItems('crm.contact.delete', $this->createdContactIds) as $result) { + } + } + } +} \ No newline at end of file diff --git a/tests/Integration/Core/CoreTest.php b/tests/Integration/Core/CoreTest.php index f6b6c004..becfb839 100644 --- a/tests/Integration/Core/CoreTest.php +++ b/tests/Integration/Core/CoreTest.php @@ -10,8 +10,8 @@ use Bitrix24\SDK\Core\Credentials\ApplicationProfile; use Bitrix24\SDK\Core\Credentials\Credentials; use Bitrix24\SDK\Core\Credentials\Scope; -use Bitrix24\SDK\Core\Exceptions\AuthForbiddenException; use Bitrix24\SDK\Core\Exceptions\MethodNotFoundException; +use Bitrix24\SDK\Core\Exceptions\TransportException; use Bitrix24\SDK\Tests\Integration\Fabric; use PHPUnit\Framework\TestCase; use Psr\Log\LoggerInterface; @@ -37,13 +37,14 @@ public function testCallExistingApiMethod(): void public function testConnectToNonExistsBitrix24PortalInCloud():void { $core = (new CoreBuilder()) + ->withLogger($this->log) ->withCredentials(Credentials::createFromOAuth( new AccessToken('non-exists-access-token','refresh-token', 3600), new ApplicationProfile('non-exists-client-id', 'non-exists-client-secret', new Scope([])), 'non-exists-domain.bitrix24.com' )) ->build(); - $this->expectException(AuthForbiddenException::class); + $this->expectException(TransportException::class); $core->call('app.info'); }