From af6a26548fb166e63e6404f6c932e410aca87ff9 Mon Sep 17 00:00:00 2001 From: Dmitriy Ignatenko Date: Thu, 7 May 2026 18:43:06 +0400 Subject: [PATCH 01/18] This is the first implementation for biconnector.connector methods --- CHANGELOG.md | 11 + Makefile | 8 + phpunit.xml.dist | 7 + .../Biconnector/BiconnectorServiceBuilder.php | 40 ++++ .../Result/AddedConnectorBatchResult.php | 38 +++ .../Connector/Result/AddedConnectorResult.php | 42 ++++ .../Connector/Result/ConnectorItemResult.php | 40 ++++ .../Connector/Result/ConnectorResult.php | 37 +++ .../Connector/Result/ConnectorsResult.php | 49 ++++ .../Result/DeletedConnectorBatchResult.php | 28 +++ .../Result/DeletedConnectorResult.php | 32 +++ .../Result/UpdatedConnectorBatchResult.php | 28 +++ .../Result/UpdatedConnectorResult.php | 32 +++ .../Biconnector/Connector/Service/Batch.php | 169 +++++++++++++ .../Connector/Service/Connector.php | 222 ++++++++++++++++++ src/Services/ServiceBuilder.php | 15 ++ .../ConnectorItemResultAnnotationsTest.php | 92 ++++++++ .../Connector/Service/BatchTest.php | 119 ++++++++++ .../Connector/Service/ConnectorTest.php | 183 +++++++++++++++ .../BiconnectorServiceBuilderTest.php | 44 ++++ .../Unit/Services/ServiceBuilderCacheTest.php | 4 + 21 files changed, 1240 insertions(+) create mode 100644 src/Services/Biconnector/BiconnectorServiceBuilder.php create mode 100644 src/Services/Biconnector/Connector/Result/AddedConnectorBatchResult.php create mode 100644 src/Services/Biconnector/Connector/Result/AddedConnectorResult.php create mode 100644 src/Services/Biconnector/Connector/Result/ConnectorItemResult.php create mode 100644 src/Services/Biconnector/Connector/Result/ConnectorResult.php create mode 100644 src/Services/Biconnector/Connector/Result/ConnectorsResult.php create mode 100644 src/Services/Biconnector/Connector/Result/DeletedConnectorBatchResult.php create mode 100644 src/Services/Biconnector/Connector/Result/DeletedConnectorResult.php create mode 100644 src/Services/Biconnector/Connector/Result/UpdatedConnectorBatchResult.php create mode 100644 src/Services/Biconnector/Connector/Result/UpdatedConnectorResult.php create mode 100644 src/Services/Biconnector/Connector/Service/Batch.php create mode 100644 src/Services/Biconnector/Connector/Service/Connector.php create mode 100644 tests/Integration/Services/Biconnector/Connector/Result/ConnectorItemResultAnnotationsTest.php create mode 100644 tests/Integration/Services/Biconnector/Connector/Service/BatchTest.php create mode 100644 tests/Integration/Services/Biconnector/Connector/Service/ConnectorTest.php create mode 100644 tests/Unit/Services/Biconnector/BiconnectorServiceBuilderTest.php diff --git a/CHANGELOG.md b/CHANGELOG.md index 9797dc88..600534e3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,17 @@ ### Added +- Added service `Services\Biconnector\Connector` with support methods, + see [biconnector.connector.* methods](https://github.com/bitrix24/b24phpsdk/issues/469): + - `add` adds a new connector, with batch calls support + - `update` updates an existing connector, with batch calls support + - `get` gets information about the connector by its identifier + - `list` gets the list of connectors, with batch calls support + - `delete` deletes a connector, with batch calls support + - `fields` returns the fields description + - `count` counts connectors +- Added `Bitrix24\SDK\Services\Biconnector\BiconnectorServiceBuilder` with `connector()` accessor ([#469](https://github.com/bitrix24/b24phpsdk/issues/469)) +- Added `Bitrix24\SDK\Services\ServiceBuilder::getBiconnectorScope()` method ([#469](https://github.com/bitrix24/b24phpsdk/issues/469)) - Added `Bitrix24\SDK\Services\IM\Recent\Service\Recent` service wrapping `im.recent.get`, `im.recent.list`, `im.recent.pin`, `im.recent.unread`, and `im.recent.hide`, with `RecentItemResult`/`RecentsResult` and `IMServiceBuilder::recent()` accessor ([#427](https://github.com/bitrix24/b24phpsdk/issues/427)) - Added `Bitrix24\SDK\Services\IM\Revision\Service\Revision` service wrapping `im.revision.get` for IM module API revision/compatibility checks, with `RevisionItemResult` (`rest`, `web`, `mobile`, `desktop`, `im_revision_mobile` fields) and `IMServiceBuilder::revision()` accessor ([#434](https://github.com/bitrix24/b24phpsdk/issues/434)) - Added `IM\Counters` service with `im.counters.get` support for retrieving unread message and notification counters ([#433](https://github.com/bitrix24/b24phpsdk/issues/433)) diff --git a/Makefile b/Makefile index 475de8b8..c0801cbc 100644 --- a/Makefile +++ b/Makefile @@ -607,6 +607,14 @@ integration_tests_sale_payment_item_basket: integration_tests_crm_documentgenerator_numerator: docker compose run --rm php-cli vendor/bin/phpunit --testsuite integration_tests_crm_documentgenerator_numerator +.PHONY: test-integration-scope-biconnector +test-integration-scope-biconnector: + docker compose run --rm php-cli vendor/bin/phpunit --testsuite integration_tests_scope_biconnector + +.PHONY: test-integration-biconnector-connector +test-integration-biconnector-connector: + docker compose run --rm php-cli vendor/bin/phpunit --testsuite integration_tests_biconnector_connector + .PHONY: integration_tests_crm_documentgenerator_document integration_tests_crm_documentgenerator_document: docker compose run --rm php-cli vendor/bin/phpunit --testsuite integration_tests_crm_documentgenerator_document diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 8e3837b9..4ab7033d 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -332,6 +332,13 @@ ./tests/Integration/Services/Rest/Service/ScopeTest.php ./tests/Integration/Services/Rest/Result/ScopeMethodItemResultTest.php + + ./tests/Integration/Services/Biconnector/ + + + ./tests/Integration/Services/Biconnector/Connector/Service/ + ./tests/Integration/Services/Biconnector/Connector/Result/ + diff --git a/src/Services/Biconnector/BiconnectorServiceBuilder.php b/src/Services/Biconnector/BiconnectorServiceBuilder.php new file mode 100644 index 00000000..20e9f53f --- /dev/null +++ b/src/Services/Biconnector/BiconnectorServiceBuilder.php @@ -0,0 +1,40 @@ + + * + * For the full copyright and license information, please view the MIT-LICENSE.txt + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Bitrix24\SDK\Services\Biconnector; + +use Bitrix24\SDK\Attributes\ApiServiceBuilderMetadata; +use Bitrix24\SDK\Core\Credentials\Scope; +use Bitrix24\SDK\Services\AbstractServiceBuilder; +use Bitrix24\SDK\Services\Biconnector\Connector\Service\Batch; +use Bitrix24\SDK\Services\Biconnector\Connector\Service\Connector; + +#[ApiServiceBuilderMetadata(new Scope(['biconnector']))] +class BiconnectorServiceBuilder extends AbstractServiceBuilder +{ + /** + * Get the Connector service + */ + public function connector(): Connector + { + if (!isset($this->serviceCache[__METHOD__])) { + $this->serviceCache[__METHOD__] = new Connector( + new Batch($this->batch, $this->log), + $this->core, + $this->log + ); + } + + return $this->serviceCache[__METHOD__]; + } +} diff --git a/src/Services/Biconnector/Connector/Result/AddedConnectorBatchResult.php b/src/Services/Biconnector/Connector/Result/AddedConnectorBatchResult.php new file mode 100644 index 00000000..9db7f5e6 --- /dev/null +++ b/src/Services/Biconnector/Connector/Result/AddedConnectorBatchResult.php @@ -0,0 +1,38 @@ + + * + * For the full copyright and license information, please view the MIT-LICENSE.txt + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Bitrix24\SDK\Services\Biconnector\Connector\Result; + +use Bitrix24\SDK\Core\Result\AddedItemBatchResult; + +/** + * Class AddedConnectorBatchResult + */ +class AddedConnectorBatchResult extends AddedItemBatchResult +{ + #[\Override] + public function getId(): int + { + $result = $this->getResponseData()->getResult(); + + if (!empty($result['connector']['id'])) { + return (int)$result['connector']['id']; + } + + if (!empty($result['id'])) { + return (int)$result['id']; + } + + return (int)$result; + } +} diff --git a/src/Services/Biconnector/Connector/Result/AddedConnectorResult.php b/src/Services/Biconnector/Connector/Result/AddedConnectorResult.php new file mode 100644 index 00000000..1c04a49c --- /dev/null +++ b/src/Services/Biconnector/Connector/Result/AddedConnectorResult.php @@ -0,0 +1,42 @@ + + * + * For the full copyright and license information, please view the MIT-LICENSE.txt + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Bitrix24\SDK\Services\Biconnector\Connector\Result; + +use Bitrix24\SDK\Core\Exceptions\BaseException; +use Bitrix24\SDK\Core\Result\AddedItemResult; + +/** + * Class AddedConnectorResult + */ +class AddedConnectorResult extends AddedItemResult +{ + /** + * @throws BaseException + */ + #[\Override] + public function getId(): int + { + $result = $this->getCoreResponse()->getResponseData()->getResult(); + + if (!empty($result['connector']['id'])) { + return (int)$result['connector']['id']; + } + + if (!empty($result['id'])) { + return (int)$result['id']; + } + + return (int)$result; + } +} diff --git a/src/Services/Biconnector/Connector/Result/ConnectorItemResult.php b/src/Services/Biconnector/Connector/Result/ConnectorItemResult.php new file mode 100644 index 00000000..7eef189e --- /dev/null +++ b/src/Services/Biconnector/Connector/Result/ConnectorItemResult.php @@ -0,0 +1,40 @@ + + * + * For the full copyright and license information, please view the MIT-LICENSE.txt + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Bitrix24\SDK\Services\Biconnector\Connector\Result; + +use Bitrix24\SDK\Core\Result\AbstractItem; + +/** + * Class ConnectorItemResult + * + * @property-read int $id + * @property-read string $name + * @property-read string $code + * @property-read string|null $description + * @property-read string|null $pictureUrl + * @property-read array|null $settings + * @property-read bool $isEnabled + */ +class ConnectorItemResult extends AbstractItem +{ + #[\Override] + public function __get($offset): mixed + { + return match ($offset) { + 'isEnabled' => isset($this->data[$offset]) ? (bool)$this->data[$offset] : null, + 'id' => isset($this->data[$offset]) ? (int)$this->data[$offset] : null, + default => $this->data[$offset] ?? null, + }; + } +} diff --git a/src/Services/Biconnector/Connector/Result/ConnectorResult.php b/src/Services/Biconnector/Connector/Result/ConnectorResult.php new file mode 100644 index 00000000..edf0edc1 --- /dev/null +++ b/src/Services/Biconnector/Connector/Result/ConnectorResult.php @@ -0,0 +1,37 @@ + + * + * For the full copyright and license information, please view the MIT-LICENSE.txt + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Bitrix24\SDK\Services\Biconnector\Connector\Result; + +use Bitrix24\SDK\Core\Exceptions\BaseException; +use Bitrix24\SDK\Core\Result\AbstractResult; + +/** + * Class ConnectorResult + */ +class ConnectorResult extends AbstractResult +{ + /** + * @throws BaseException + */ + public function connector(): ConnectorItemResult + { + $result = $this->getCoreResponse()->getResponseData()->getResult(); + + if (!empty($result['connector']) && is_array($result['connector'])) { + $result = $result['connector']; + } + + return new ConnectorItemResult($result); + } +} diff --git a/src/Services/Biconnector/Connector/Result/ConnectorsResult.php b/src/Services/Biconnector/Connector/Result/ConnectorsResult.php new file mode 100644 index 00000000..3fbfa144 --- /dev/null +++ b/src/Services/Biconnector/Connector/Result/ConnectorsResult.php @@ -0,0 +1,49 @@ + + * + * For the full copyright and license information, please view the MIT-LICENSE.txt + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Bitrix24\SDK\Services\Biconnector\Connector\Result; + +use Bitrix24\SDK\Core\Exceptions\BaseException; +use Bitrix24\SDK\Core\Result\AbstractResult; + +/** + * Class ConnectorsResult + */ +class ConnectorsResult extends AbstractResult +{ + /** + * @return ConnectorItemResult[] + * @throws BaseException + */ + public function getConnectors(): array + { + $items = []; + $source = []; + + $result = $this->getCoreResponse()->getResponseData()->getResult(); + + if (!empty($result['connectors']) && is_array($result['connectors'])) { + $source = $result['connectors']; + } elseif (!empty($result['items']) && is_array($result['items'])) { + $source = $result['items']; + } elseif (is_array($result) && array_is_list($result)) { + $source = $result; + } + + foreach ($source as $item) { + $items[] = new ConnectorItemResult($item); + } + + return $items; + } +} diff --git a/src/Services/Biconnector/Connector/Result/DeletedConnectorBatchResult.php b/src/Services/Biconnector/Connector/Result/DeletedConnectorBatchResult.php new file mode 100644 index 00000000..ae8f5d51 --- /dev/null +++ b/src/Services/Biconnector/Connector/Result/DeletedConnectorBatchResult.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the MIT-LICENSE.txt + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Bitrix24\SDK\Services\Biconnector\Connector\Result; + +use Bitrix24\SDK\Core\Result\DeletedItemBatchResult; + +/** + * Class DeletedConnectorBatchResult + */ +class DeletedConnectorBatchResult extends DeletedItemBatchResult +{ + #[\Override] + public function isSuccess(): bool + { + return (bool)$this->getResponseData()->getResult(); + } +} diff --git a/src/Services/Biconnector/Connector/Result/DeletedConnectorResult.php b/src/Services/Biconnector/Connector/Result/DeletedConnectorResult.php new file mode 100644 index 00000000..979bf396 --- /dev/null +++ b/src/Services/Biconnector/Connector/Result/DeletedConnectorResult.php @@ -0,0 +1,32 @@ + + * + * For the full copyright and license information, please view the MIT-LICENSE.txt + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Bitrix24\SDK\Services\Biconnector\Connector\Result; + +use Bitrix24\SDK\Core\Exceptions\BaseException; +use Bitrix24\SDK\Core\Result\DeletedItemResult; + +/** + * Class DeletedConnectorResult + */ +class DeletedConnectorResult extends DeletedItemResult +{ + /** + * @throws BaseException + */ + #[\Override] + public function isSuccess(): bool + { + return (bool)$this->getCoreResponse()->getResponseData()->getResult(); + } +} diff --git a/src/Services/Biconnector/Connector/Result/UpdatedConnectorBatchResult.php b/src/Services/Biconnector/Connector/Result/UpdatedConnectorBatchResult.php new file mode 100644 index 00000000..26b50085 --- /dev/null +++ b/src/Services/Biconnector/Connector/Result/UpdatedConnectorBatchResult.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the MIT-LICENSE.txt + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Bitrix24\SDK\Services\Biconnector\Connector\Result; + +use Bitrix24\SDK\Core\Result\UpdatedItemBatchResult; + +/** + * Class UpdatedConnectorBatchResult + */ +class UpdatedConnectorBatchResult extends UpdatedItemBatchResult +{ + #[\Override] + public function isSuccess(): bool + { + return (bool)$this->getResponseData()->getResult(); + } +} diff --git a/src/Services/Biconnector/Connector/Result/UpdatedConnectorResult.php b/src/Services/Biconnector/Connector/Result/UpdatedConnectorResult.php new file mode 100644 index 00000000..eec333d8 --- /dev/null +++ b/src/Services/Biconnector/Connector/Result/UpdatedConnectorResult.php @@ -0,0 +1,32 @@ + + * + * For the full copyright and license information, please view the MIT-LICENSE.txt + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Bitrix24\SDK\Services\Biconnector\Connector\Result; + +use Bitrix24\SDK\Core\Exceptions\BaseException; +use Bitrix24\SDK\Core\Result\UpdatedItemResult; + +/** + * Class UpdatedConnectorResult + */ +class UpdatedConnectorResult extends UpdatedItemResult +{ + /** + * @throws BaseException + */ + #[\Override] + public function isSuccess(): bool + { + return (bool)$this->getCoreResponse()->getResponseData()->getResult(); + } +} diff --git a/src/Services/Biconnector/Connector/Service/Batch.php b/src/Services/Biconnector/Connector/Service/Batch.php new file mode 100644 index 00000000..01c31a8d --- /dev/null +++ b/src/Services/Biconnector/Connector/Service/Batch.php @@ -0,0 +1,169 @@ + + * + * For the full copyright and license information, please view the MIT-LICENSE.txt + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Bitrix24\SDK\Services\Biconnector\Connector\Service; + +use Bitrix24\SDK\Attributes\ApiBatchMethodMetadata; +use Bitrix24\SDK\Attributes\ApiBatchServiceMetadata; +use Bitrix24\SDK\Core\Contracts\BatchOperationsInterface; +use Bitrix24\SDK\Core\Credentials\Scope; +use Bitrix24\SDK\Core\Exceptions\BaseException; +use Bitrix24\SDK\Services\Biconnector\Connector\Result\AddedConnectorBatchResult; +use Bitrix24\SDK\Services\Biconnector\Connector\Result\ConnectorItemResult; +use Bitrix24\SDK\Services\Biconnector\Connector\Result\DeletedConnectorBatchResult; +use Bitrix24\SDK\Services\Biconnector\Connector\Result\UpdatedConnectorBatchResult; +use Generator; +use Psr\Log\LoggerInterface; + +#[ApiBatchServiceMetadata(new Scope(['biconnector']))] +class Batch +{ + /** + * Batch constructor + */ + public function __construct(protected BatchOperationsInterface $batch, protected LoggerInterface $log) + { + } + + /** + * Batch list connectors + * + * @link https://apidocs.bitrix24.com/api-reference/biconnector/connector/biconnector-connector-list.html + * + * @return Generator + * @throws BaseException + */ + #[ApiBatchMethodMetadata( + 'biconnector.connector.list', + 'https://apidocs.bitrix24.com/api-reference/biconnector/connector/biconnector-connector-list.html', + 'Batch list connectors' + )] + public function list( + array $order = [], + array $filter = [], + array $select = [], + ?int $limit = null + ): Generator { + $this->log->debug( + 'batchList', + [ + 'order' => $order, + 'filter' => $filter, + 'select' => $select, + 'limit' => $limit, + ] + ); + + $connectorListGenerator = $this->batch->getTraversableListWithCount( + 'biconnector.connector.list', + $order, + $filter, + $select, + $limit + ); + foreach ($connectorListGenerator as $key => $value) { + yield $key => new ConnectorItemResult($value); + } + } + + /** + * Batch add connectors + * + * @link https://apidocs.bitrix24.com/api-reference/biconnector/connector/biconnector-connector-add.html + * + * @param array $connectors + * + * @return Generator + * @throws BaseException + */ + #[ApiBatchMethodMetadata( + 'biconnector.connector.add', + 'https://apidocs.bitrix24.com/api-reference/biconnector/connector/biconnector-connector-add.html', + 'Batch add connectors' + )] + public function add(array $connectors): Generator + { + $items = []; + foreach ($connectors as $item) { + $items[] = [ + 'fields' => $item, + ]; + } + + foreach ($this->batch->addEntityItems('biconnector.connector.add', $items) as $key => $item) { + yield $key => new AddedConnectorBatchResult($item); + } + } + + /** + * Batch update connectors + * + * Update elements in array with structure: + * id => [ // Connector id + * 'fields' => [] // Connector fields to update + * ] + * + * @param array $entityItems + * + * @return Generator + * @throws BaseException + */ + #[ApiBatchMethodMetadata( + 'biconnector.connector.update', + 'https://apidocs.bitrix24.com/api-reference/biconnector/connector/biconnector-connector-update.html', + 'Batch update connectors' + )] + public function update(array $entityItems): Generator + { + foreach ( + $this->batch->updateEntityItems( + 'biconnector.connector.update', + $entityItems + ) as $key => $item + ) { + yield $key => new UpdatedConnectorBatchResult($item); + } + } + + /** + * Batch delete connectors + * + * @param int[] $connectorIds + * + * @return Generator + * @throws BaseException + */ + #[ApiBatchMethodMetadata( + 'biconnector.connector.delete', + 'https://apidocs.bitrix24.com/api-reference/biconnector/connector/biconnector-connector-delete.html', + 'Batch delete connectors' + )] + public function delete(array $connectorIds): Generator + { + foreach ( + $this->batch->deleteEntityItems( + 'biconnector.connector.delete', + $connectorIds + ) as $key => $item + ) { + yield $key => new DeletedConnectorBatchResult($item); + } + } +} diff --git a/src/Services/Biconnector/Connector/Service/Connector.php b/src/Services/Biconnector/Connector/Service/Connector.php new file mode 100644 index 00000000..30448dd4 --- /dev/null +++ b/src/Services/Biconnector/Connector/Service/Connector.php @@ -0,0 +1,222 @@ + + * + * For the full copyright and license information, please view the MIT-LICENSE.txt + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Bitrix24\SDK\Services\Biconnector\Connector\Service; + +use Bitrix24\SDK\Attributes\ApiEndpointMetadata; +use Bitrix24\SDK\Attributes\ApiServiceMetadata; +use Bitrix24\SDK\Core\Contracts\CoreInterface; +use Bitrix24\SDK\Core\Credentials\Scope; +use Bitrix24\SDK\Core\Exceptions\BaseException; +use Bitrix24\SDK\Core\Exceptions\TransportException; +use Bitrix24\SDK\Core\Result\FieldsResult; +use Bitrix24\SDK\Services\AbstractService; +use Bitrix24\SDK\Services\Biconnector\Connector\Result\AddedConnectorResult; +use Bitrix24\SDK\Services\Biconnector\Connector\Result\ConnectorResult; +use Bitrix24\SDK\Services\Biconnector\Connector\Result\ConnectorsResult; +use Bitrix24\SDK\Services\Biconnector\Connector\Result\DeletedConnectorResult; +use Bitrix24\SDK\Services\Biconnector\Connector\Result\UpdatedConnectorResult; +use Psr\Log\LoggerInterface; + +#[ApiServiceMetadata(new Scope(['biconnector']))] +class Connector extends AbstractService +{ + /** + * Connector constructor + */ + public function __construct(public Batch $batch, CoreInterface $core, LoggerInterface $logger) + { + parent::__construct($core, $logger); + } + + /** + * Add a new connector + * + * @link https://apidocs.bitrix24.com/api-reference/biconnector/connector/biconnector-connector-add.html + * + * @param array{ + * name: string, + * code: string, + * description?: string, + * pictureUrl?: string, + * settings?: array, + * isEnabled?: bool + * } $fields + * + * @throws BaseException + * @throws TransportException + */ + #[ApiEndpointMetadata( + 'biconnector.connector.add', + 'https://apidocs.bitrix24.com/api-reference/biconnector/connector/biconnector-connector-add.html', + 'Add a new connector' + )] + public function add(array $fields): AddedConnectorResult + { + return new AddedConnectorResult( + $this->core->call( + 'biconnector.connector.add', + [ + 'fields' => $fields, + ] + ) + ); + } + + /** + * Update an existing connector + * + * @link https://apidocs.bitrix24.com/api-reference/biconnector/connector/biconnector-connector-update.html + * + * @param array{ + * name?: string, + * code?: string, + * description?: string, + * pictureUrl?: string, + * settings?: array, + * isEnabled?: bool + * } $fields + * + * @throws BaseException + * @throws TransportException + */ + #[ApiEndpointMetadata( + 'biconnector.connector.update', + 'https://apidocs.bitrix24.com/api-reference/biconnector/connector/biconnector-connector-update.html', + 'Update an existing connector' + )] + public function update(int $id, array $fields): UpdatedConnectorResult + { + return new UpdatedConnectorResult( + $this->core->call( + 'biconnector.connector.update', + [ + 'id' => $id, + 'fields' => $fields, + ] + ) + ); + } + + /** + * Get a connector by its ID + * + * @link https://apidocs.bitrix24.com/api-reference/biconnector/connector/biconnector-connector-get.html + * + * @throws BaseException + * @throws TransportException + */ + #[ApiEndpointMetadata( + 'biconnector.connector.get', + 'https://apidocs.bitrix24.com/api-reference/biconnector/connector/biconnector-connector-get.html', + 'Get a connector by its ID' + )] + public function get(int $id): ConnectorResult + { + return new ConnectorResult( + $this->core->call( + 'biconnector.connector.get', + [ + 'id' => $id, + ] + ) + ); + } + + /** + * Get a list of connectors + * + * @link https://apidocs.bitrix24.com/api-reference/biconnector/connector/biconnector-connector-list.html + * + * @param array $order - sort fields, e.g. ['id' => 'ASC'] + * @param array $filter - filter fields + * @param array $select - fields to include in the result + * @param int $start - offset for pagination + * + * @throws BaseException + * @throws TransportException + */ + #[ApiEndpointMetadata( + 'biconnector.connector.list', + 'https://apidocs.bitrix24.com/api-reference/biconnector/connector/biconnector-connector-list.html', + 'Get a list of connectors' + )] + public function list(array $order = [], array $filter = [], array $select = [], int $start = 0): ConnectorsResult + { + return new ConnectorsResult( + $this->core->call( + 'biconnector.connector.list', + [ + 'order' => $order, + 'filter' => $filter, + 'select' => $select, + 'start' => $start, + ] + ) + ); + } + + /** + * Delete a connector by its ID + * + * @link https://apidocs.bitrix24.com/api-reference/biconnector/connector/biconnector-connector-delete.html + * + * @throws BaseException + * @throws TransportException + */ + #[ApiEndpointMetadata( + 'biconnector.connector.delete', + 'https://apidocs.bitrix24.com/api-reference/biconnector/connector/biconnector-connector-delete.html', + 'Delete a connector by its ID' + )] + public function delete(int $id): DeletedConnectorResult + { + return new DeletedConnectorResult( + $this->core->call( + 'biconnector.connector.delete', + [ + 'id' => $id, + ] + ) + ); + } + + /** + * Get the fields description for connectors + * + * @link https://apidocs.bitrix24.com/api-reference/biconnector/connector/biconnector-connector-fields.html + * + * @throws BaseException + * @throws TransportException + */ + #[ApiEndpointMetadata( + 'biconnector.connector.fields', + 'https://apidocs.bitrix24.com/api-reference/biconnector/connector/biconnector-connector-fields.html', + 'Get the fields description for connectors' + )] + public function fields(): FieldsResult + { + return new FieldsResult($this->core->call('biconnector.connector.fields')); + } + + /** + * Count connectors + * + * @throws BaseException + * @throws TransportException + */ + public function count(): int + { + return $this->list()->getCoreResponse()->getResponseData()->getPagination()->getTotal(); + } +} diff --git a/src/Services/ServiceBuilder.php b/src/Services/ServiceBuilder.php index 417ca0d6..5e79458f 100644 --- a/src/Services/ServiceBuilder.php +++ b/src/Services/ServiceBuilder.php @@ -17,6 +17,7 @@ use Bitrix24\SDK\Core\Contracts\BulkItemsReaderInterface; use Bitrix24\SDK\Core\Contracts\CoreInterface; use Bitrix24\SDK\Services\AI\AIServiceBuilder; +use Bitrix24\SDK\Services\Biconnector\BiconnectorServiceBuilder; use Bitrix24\SDK\Services\Catalog\CatalogServiceBuilder; use Bitrix24\SDK\Services\CRM\CRMServiceBuilder; use Bitrix24\SDK\Services\Disk\DiskServiceBuilder; @@ -53,6 +54,20 @@ public function __construct( parent::__construct($core, $batch, $bulkItemsReader, $log); } + public function getBiconnectorScope(): BiconnectorServiceBuilder + { + if (!isset($this->serviceCache[__METHOD__])) { + $this->serviceCache[__METHOD__] = new BiconnectorServiceBuilder( + $this->core, + $this->batch, + $this->bulkItemsReader, + $this->log + ); + } + + return $this->serviceCache[__METHOD__]; + } + public function getSaleScope(): SaleServiceBuilder { if (!isset($this->serviceCache[__METHOD__])) { diff --git a/tests/Integration/Services/Biconnector/Connector/Result/ConnectorItemResultAnnotationsTest.php b/tests/Integration/Services/Biconnector/Connector/Result/ConnectorItemResultAnnotationsTest.php new file mode 100644 index 00000000..30d092f4 --- /dev/null +++ b/tests/Integration/Services/Biconnector/Connector/Result/ConnectorItemResultAnnotationsTest.php @@ -0,0 +1,92 @@ + + * + * For the full copyright and license information, please view the MIT-LICENSE.txt + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Bitrix24\SDK\Tests\Integration\Services\Biconnector\Connector\Result; + +use Bitrix24\SDK\Core\Exceptions\InvalidArgumentException; +use Bitrix24\SDK\Services\Biconnector\Connector\Result\ConnectorItemResult; +use Bitrix24\SDK\Services\Biconnector\Connector\Service\Connector; +use Bitrix24\SDK\Tests\CustomAssertions\CustomBitrix24Assertions; +use Bitrix24\SDK\Tests\Integration\Factory; +use Faker\Generator; +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\Test; +use PHPUnit\Framework\Attributes\TestDox; +use PHPUnit\Framework\TestCase; +use Faker; + +#[CoversClass(ConnectorItemResult::class)] +class ConnectorItemResultAnnotationsTest extends TestCase +{ + use CustomBitrix24Assertions; + + private Connector $connectorService; + + private Generator $faker; + + private int $createdConnectorId; + + /** + * @throws InvalidArgumentException + */ + #[\Override] + protected function setUp(): void + { + $this->connectorService = Factory::getServiceBuilder()->getBiconnectorScope()->connector(); + $this->faker = Faker\Factory::create(); + + // Create a connector to use in tests + $this->createdConnectorId = $this->connectorService->add([ + 'name' => 'test-annotations-' . $this->faker->uuid(), + 'code' => 'code_' . substr($this->faker->uuid(), 0, 8), + ])->getId(); + } + + #[\Override] + protected function tearDown(): void + { + $this->connectorService->delete($this->createdConnectorId); + } + + #[Test] + #[TestDox('all fields in ConnectorItemResult are annotated and match live API response')] + public function testAllSystemFieldsAnnotated(): void + { + $rawItem = $this->connectorService->get($this->createdConnectorId) + ->getCoreResponse() + ->getResponseData() + ->getResult(); + + // Handle nested envelope: result['connector'] or result directly + if (!empty($rawItem['connector']) && is_array($rawItem['connector'])) { + $rawItem = $rawItem['connector']; + } + + $this->assertBitrix24AllResultItemFieldsAnnotated( + array_keys($rawItem), + ConnectorItemResult::class + ); + } + + #[Test] + #[TestDox('all fields in ConnectorItemResult have valid type casting in magic getters')] + public function testAllSystemFieldsHasValidTypeAnnotation(): void + { + $connectorItemResult = $this->connectorService->get($this->createdConnectorId)->connector(); + + $this->assertBitrix24ResultItemFieldsTypeCastMatchAnnotations( + $connectorItemResult, + ConnectorItemResult::class + ); + } +} diff --git a/tests/Integration/Services/Biconnector/Connector/Service/BatchTest.php b/tests/Integration/Services/Biconnector/Connector/Service/BatchTest.php new file mode 100644 index 00000000..68fd41e7 --- /dev/null +++ b/tests/Integration/Services/Biconnector/Connector/Service/BatchTest.php @@ -0,0 +1,119 @@ + + * + * For the full copyright and license information, please view the MIT-LICENSE.txt + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Bitrix24\SDK\Tests\Integration\Services\Biconnector\Connector\Service; + +use Bitrix24\SDK\Core\Exceptions\BaseException; +use Bitrix24\SDK\Core\Exceptions\InvalidArgumentException; +use Bitrix24\SDK\Core\Exceptions\TransportException; +use Bitrix24\SDK\Services\Biconnector\Connector\Result\ConnectorItemResult; +use Bitrix24\SDK\Services\Biconnector\Connector\Service\Batch; +use Bitrix24\SDK\Services\Biconnector\Connector\Service\Connector; +use Bitrix24\SDK\Tests\Integration\Factory; +use Faker\Generator; +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\TestCase; +use Faker; + +#[CoversClass(Batch::class)] +class BatchTest extends TestCase +{ + private Connector $connectorService; + + private Generator $faker; + + /** + * @throws InvalidArgumentException + */ + #[\Override] + protected function setUp(): void + { + $this->connectorService = Factory::getServiceBuilder()->getBiconnectorScope()->connector(); + $this->faker = Faker\Factory::create(); + } + + /** + * @throws BaseException + * @throws TransportException + */ + public function testBatchList(): void + { + $name = 'connector-' . $this->faker->uuid(); + $code = 'code_' . substr($this->faker->uuid(), 0, 8); + $id = $this->connectorService->add([ + 'name' => $name, + 'code' => $code, + ])->getId(); + + $count = 0; + foreach ($this->connectorService->batch->list([], [], [], 10) as $item) { + self::assertInstanceOf(ConnectorItemResult::class, $item); + $count++; + } + + self::assertGreaterThanOrEqual(1, $count); + + // Cleanup + $this->connectorService->delete($id); + } + + /** + * @throws BaseException + * @throws TransportException + */ + public function testBatchAdd(): void + { + $connectors = []; + $codes = []; + for ($i = 0; $i < 3; $i++) { + $code = 'code_' . substr($this->faker->uuid(), 0, 8); + $codes[] = $code; + $connectors[] = [ + 'name' => 'connector-batch-' . $this->faker->uuid(), + 'code' => $code, + ]; + } + + $addedIds = []; + foreach ($this->connectorService->batch->add($connectors) as $result) { + $addedIds[] = $result->getId(); + self::assertGreaterThanOrEqual(1, $result->getId()); + } + + self::assertCount(3, $addedIds); + + // Cleanup + foreach ($this->connectorService->batch->delete($addedIds) as $deleteResult) { + self::assertTrue($deleteResult->isSuccess()); + } + } + + /** + * @throws BaseException + * @throws TransportException + */ + public function testBatchDelete(): void + { + $ids = []; + for ($i = 0; $i < 2; $i++) { + $ids[] = $this->connectorService->add([ + 'name' => 'connector-del-batch-' . $this->faker->uuid(), + 'code' => 'code_' . substr($this->faker->uuid(), 0, 8), + ])->getId(); + } + + foreach ($this->connectorService->batch->delete($ids) as $result) { + self::assertTrue($result->isSuccess()); + } + } +} diff --git a/tests/Integration/Services/Biconnector/Connector/Service/ConnectorTest.php b/tests/Integration/Services/Biconnector/Connector/Service/ConnectorTest.php new file mode 100644 index 00000000..bb7486c3 --- /dev/null +++ b/tests/Integration/Services/Biconnector/Connector/Service/ConnectorTest.php @@ -0,0 +1,183 @@ + + * + * For the full copyright and license information, please view the MIT-LICENSE.txt + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Bitrix24\SDK\Tests\Integration\Services\Biconnector\Connector\Service; + +use Bitrix24\SDK\Core\Exceptions\BaseException; +use Bitrix24\SDK\Core\Exceptions\InvalidArgumentException; +use Bitrix24\SDK\Core\Exceptions\TransportException; +use Bitrix24\SDK\Services\Biconnector\Connector\Result\ConnectorItemResult; +use Bitrix24\SDK\Services\Biconnector\Connector\Service\Connector; +use Bitrix24\SDK\Tests\CustomAssertions\CustomBitrix24Assertions; +use Bitrix24\SDK\Tests\Integration\Factory; +use Faker\Generator; +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\TestCase; +use Faker; + +#[CoversClass(Connector::class)] +class ConnectorTest extends TestCase +{ + use CustomBitrix24Assertions; + + private Connector $connectorService; + + private Generator $faker; + + /** + * @throws InvalidArgumentException + */ + #[\Override] + protected function setUp(): void + { + $this->connectorService = Factory::getServiceBuilder()->getBiconnectorScope()->connector(); + $this->faker = Faker\Factory::create(); + } + + /** + * @throws BaseException + * @throws TransportException + */ + public function testAdd(): void + { + $name = 'connector-' . $this->faker->uuid(); + $code = 'code_' . substr($this->faker->uuid(), 0, 8); + $id = $this->connectorService->add([ + 'name' => $name, + 'code' => $code, + ])->getId(); + + self::assertGreaterThanOrEqual(1, $id); + + // Cleanup + $this->connectorService->delete($id); + } + + /** + * @throws BaseException + * @throws TransportException + */ + public function testGet(): void + { + $name = 'connector-' . $this->faker->uuid(); + $code = 'code_' . substr($this->faker->uuid(), 0, 8); + $id = $this->connectorService->add([ + 'name' => $name, + 'code' => $code, + ])->getId(); + + $connectorItemResult = $this->connectorService->get($id)->connector(); + self::assertInstanceOf(ConnectorItemResult::class, $connectorItemResult); + self::assertEquals($id, $connectorItemResult->id); + self::assertEquals($name, $connectorItemResult->name); + + // Cleanup + $this->connectorService->delete($id); + } + + /** + * @throws BaseException + * @throws TransportException + */ + public function testList(): void + { + $name = 'connector-' . $this->faker->uuid(); + $code = 'code_' . substr($this->faker->uuid(), 0, 8); + $id = $this->connectorService->add([ + 'name' => $name, + 'code' => $code, + ])->getId(); + + $list = $this->connectorService->list()->getConnectors(); + self::assertIsArray($list); + self::assertGreaterThanOrEqual(1, count($list)); + + // Cleanup + $this->connectorService->delete($id); + } + + /** + * @throws BaseException + * @throws TransportException + */ + public function testUpdate(): void + { + $name = 'connector-' . $this->faker->uuid(); + $code = 'code_' . substr($this->faker->uuid(), 0, 8); + $id = $this->connectorService->add([ + 'name' => $name, + 'code' => $code, + ])->getId(); + + $newName = $name . '-updated'; + self::assertTrue( + $this->connectorService->update($id, [ + 'name' => $newName, + ])->isSuccess() + ); + + self::assertEquals($newName, $this->connectorService->get($id)->connector()->name); + + // Cleanup + $this->connectorService->delete($id); + } + + /** + * @throws BaseException + * @throws TransportException + */ + public function testDelete(): void + { + $name = 'connector-' . $this->faker->uuid(); + $code = 'code_' . substr($this->faker->uuid(), 0, 8); + $id = $this->connectorService->add([ + 'name' => $name, + 'code' => $code, + ])->getId(); + + self::assertTrue($this->connectorService->delete($id)->isSuccess()); + } + + /** + * @throws BaseException + * @throws TransportException + */ + public function testFields(): void + { + $fields = $this->connectorService->fields()->getFieldsDescription(); + self::assertIsArray($fields); + self::assertNotEmpty($fields); + } + + /** + * @throws BaseException + * @throws TransportException + */ + public function testCount(): void + { + $countBefore = $this->connectorService->count(); + + $name = 'connector-' . $this->faker->uuid(); + $code = 'code_' . substr($this->faker->uuid(), 0, 8); + $id = $this->connectorService->add([ + 'name' => $name, + 'code' => $code, + ])->getId(); + + $countAfter = $this->connectorService->count(); + self::assertEquals($countBefore + 1, $countAfter); + + // Cleanup + $this->connectorService->delete($id); + } +} diff --git a/tests/Unit/Services/Biconnector/BiconnectorServiceBuilderTest.php b/tests/Unit/Services/Biconnector/BiconnectorServiceBuilderTest.php new file mode 100644 index 00000000..b939cb53 --- /dev/null +++ b/tests/Unit/Services/Biconnector/BiconnectorServiceBuilderTest.php @@ -0,0 +1,44 @@ + + * + * For the full copyright and license information, please view the MIT-LICENSE.txt + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Bitrix24\SDK\Tests\Unit\Services\Biconnector; + +use Bitrix24\SDK\Services\Biconnector\BiconnectorServiceBuilder; +use Bitrix24\SDK\Tests\Unit\Stubs\NullBatch; +use Bitrix24\SDK\Tests\Unit\Stubs\NullBulkItemsReader; +use Bitrix24\SDK\Tests\Unit\Stubs\NullCore; +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\TestCase; +use Psr\Log\NullLogger; + +#[CoversClass(BiconnectorServiceBuilder::class)] +class BiconnectorServiceBuilderTest extends TestCase +{ + private BiconnectorServiceBuilder $serviceBuilder; + + #[\Override] + protected function setUp(): void + { + $this->serviceBuilder = new BiconnectorServiceBuilder( + new NullCore(), + new NullBatch(), + new NullBulkItemsReader(), + new NullLogger() + ); + } + + public function testConnectorServiceIsCached(): void + { + $this::assertSame($this->serviceBuilder->connector(), $this->serviceBuilder->connector()); + } +} diff --git a/tests/Unit/Services/ServiceBuilderCacheTest.php b/tests/Unit/Services/ServiceBuilderCacheTest.php index 6901fa2f..b8661d99 100644 --- a/tests/Unit/Services/ServiceBuilderCacheTest.php +++ b/tests/Unit/Services/ServiceBuilderCacheTest.php @@ -87,4 +87,8 @@ public function testGetTelephonyBuilder(): void $this::assertSame($this->serviceBuilder->getTelephonyScope(), $this->serviceBuilder->getTelephonyScope()); } + public function testGetBiconnectorScopeBuilder(): void + { + $this::assertSame($this->serviceBuilder->getBiconnectorScope(), $this->serviceBuilder->getBiconnectorScope()); + } } From 0f38abe7f55e973396ed268a2f8b773f2ac47ed3 Mon Sep 17 00:00:00 2001 From: Dmitriy Ignatenko Date: Fri, 8 May 2026 16:46:39 +0400 Subject: [PATCH 02/18] Fix list method parameters and uses lowercase id --- .../Biconnector/BiconnectorServiceBuilder.php | 12 +- src/Services/Biconnector/Connector/Batch.php | 266 ++++++++++++++++++ .../Connector/Service/Connector.php | 6 +- 3 files changed, 280 insertions(+), 4 deletions(-) create mode 100644 src/Services/Biconnector/Connector/Batch.php diff --git a/src/Services/Biconnector/BiconnectorServiceBuilder.php b/src/Services/Biconnector/BiconnectorServiceBuilder.php index 20e9f53f..cba02ece 100644 --- a/src/Services/Biconnector/BiconnectorServiceBuilder.php +++ b/src/Services/Biconnector/BiconnectorServiceBuilder.php @@ -16,6 +16,7 @@ use Bitrix24\SDK\Attributes\ApiServiceBuilderMetadata; use Bitrix24\SDK\Core\Credentials\Scope; use Bitrix24\SDK\Services\AbstractServiceBuilder; +use Bitrix24\SDK\Services\Biconnector\Connector\Batch as ConnectorBatch; use Bitrix24\SDK\Services\Biconnector\Connector\Service\Batch; use Bitrix24\SDK\Services\Biconnector\Connector\Service\Connector; @@ -24,12 +25,21 @@ class BiconnectorServiceBuilder extends AbstractServiceBuilder { /** * Get the Connector service + * + * Uses a specialized ConnectorBatch to handle biconnector.connector.* REST API differences: + * - list uses 'page' parameter (page number) instead of standard 'start' (offset) + * - delete uses lowercase 'id' instead of 'ID' */ public function connector(): Connector { if (!isset($this->serviceCache[__METHOD__])) { + // Use specialized Batch for Connector to ensure correct REST parameter mapping + $connectorBatch = new ConnectorBatch( + $this->core, + $this->log + ); $this->serviceCache[__METHOD__] = new Connector( - new Batch($this->batch, $this->log), + new Batch($connectorBatch, $this->log), $this->core, $this->log ); diff --git a/src/Services/Biconnector/Connector/Batch.php b/src/Services/Biconnector/Connector/Batch.php new file mode 100644 index 00000000..29c1d96c --- /dev/null +++ b/src/Services/Biconnector/Connector/Batch.php @@ -0,0 +1,266 @@ + + * + * For the full copyright and license information, please view the MIT-LICENSE.txt + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Bitrix24\SDK\Services\Biconnector\Connector; + +use Bitrix24\SDK\Core\Exceptions\BaseException; +use Bitrix24\SDK\Core\Exceptions\InvalidArgumentException; +use Bitrix24\SDK\Core\Response\DTO\ResponseData; +use Generator; + +/** + * Class Batch + * + * Overrides base Batch to handle parameter naming differences in biconnector.connector.* REST methods: + * - list uses 'page' (page number, 50 records per page) instead of 'start' (offset) for pagination + * - delete uses lowercase 'id' instead of 'ID' + * + * @see https://apidocs.bitrix24.com/api-reference/biconnector/connector/biconnector-connector-list.html + * @see https://apidocs.bitrix24.com/api-reference/biconnector/connector/biconnector-connector-delete.html + */ +class Batch extends \Bitrix24\SDK\Core\Batch +{ + /** + * Determines the ID key — lowercase 'id' for biconnector connector + */ + #[\Override] + protected function determineKeyId(string $apiMethod, ?array $additionalParameters): string + { + return 'id'; + } + + /** + * Delete entity items with batch call using lowercase 'id' parameter + * + * @param int[] $entityItemId + * @param array|null $additionalParameters + * + * @return Generator|ResponseData[] + * @throws BaseException + */ + #[\Override] + public function deleteEntityItems( + string $apiMethod, + array $entityItemId, + ?array $additionalParameters = null + ): Generator { + $this->logger->debug( + 'deleteEntityItems.start', + [ + 'apiMethod' => $apiMethod, + 'entityItems' => $entityItemId, + 'additionalParameters' => $additionalParameters, + ] + ); + + try { + $this->clearCommands(); + foreach ($entityItemId as $cnt => $itemId) { + if (!is_int($itemId)) { + throw new InvalidArgumentException( + sprintf( + 'invalid type «%s» of connector id «%s» at position %s, connector id must be integer type', + gettype($itemId), + $itemId, + $cnt + ) + ); + } + + $this->registerCommand($apiMethod, ['id' => $itemId]); + } + + foreach ($this->getTraversable(true) as $cnt => $deletedItemResult) { + yield $cnt => $deletedItemResult; + } + } catch (InvalidArgumentException $exception) { + $errorMessage = sprintf('batch delete connector items: %s', $exception->getMessage()); + $this->logger->error( + $errorMessage, + [ + 'trace' => $exception->getTrace(), + ] + ); + throw $exception; + } catch (\Throwable $exception) { + $errorMessage = sprintf('batch delete connector items: %s', $exception->getMessage()); + $this->logger->error( + $errorMessage, + [ + 'trace' => $exception->getTrace(), + ] + ); + + throw new BaseException($errorMessage, $exception->getCode(), $exception); + } + + $this->logger->debug('deleteEntityItems.finish'); + } + + /** + * Get traversable list using page-based pagination. + * + * The biconnector.connector.list method uses 'page' parameter (page number, 50 records per page) + * instead of the standard 'start' (offset) parameter used by most other REST methods. + * + * @link https://apidocs.bitrix24.com/api-reference/biconnector/connector/biconnector-connector-list.html + * + * @param array $order + * @param array $filter + * @param array $select + * + * @return Generator + * @throws BaseException + * @throws \Bitrix24\SDK\Core\Exceptions\TransportException + */ + #[\Override] + public function getTraversableList( + string $apiMethod, + ?array $order = [], + ?array $filter = [], + ?array $select = [], + ?int $limit = null, + ?array $additionalParameters = null + ): Generator { + yield from $this->getTraversableListWithCount( + $apiMethod, + $order ?? [], + $filter ?? [], + $select ?? [], + $limit, + $additionalParameters + ); + } + + /** + * Get traversable list using page-based pagination (page number, 50 records per page). + * + * The biconnector.connector.list method accepts 'page' parameter instead of 'start'. + * Page 1 returns items 1–50, page 2 returns items 51–100, etc. + * + * @link https://apidocs.bitrix24.com/api-reference/biconnector/connector/biconnector-connector-list.html + * + * @param array $order + * @param array $filter + * @param array $select + * + * @return Generator + * @throws BaseException + * @throws \Bitrix24\SDK\Core\Exceptions\TransportException + */ + #[\Override] + public function getTraversableListWithCount( + string $apiMethod, + array $order, + array $filter, + array $select, + ?int $limit = null, + ?array $additionalParameters = null + ): Generator { + $this->logger->debug( + 'getTraversableListWithCount.start', + [ + 'apiMethod' => $apiMethod, + 'order' => $order, + 'filter' => $filter, + 'select' => $select, + 'limit' => $limit, + 'additionalParameters' => $additionalParameters, + ] + ); + + $this->clearCommands(); + + // Fetch first page to determine total count + $params = [ + 'order' => $order, + 'filter' => $filter, + 'select' => $select, + 'page' => 1, + ]; + + if ($additionalParameters !== null) { + $params = array_merge($params, $additionalParameters); + } + + $response = $this->core->call($apiMethod, $params); + $total = $response->getResponseData()->getPagination()->getTotal(); + + $this->logger->debug( + 'getTraversableListWithCount.totalElementsCount', + [ + 'totalElementsCount' => $total, + ] + ); + + if ($total <= self::MAX_ELEMENTS_IN_PAGE) { + $elementsCounter = 0; + foreach ($response->getResponseData()->getResult() as $item) { + $elementsCounter++; + if ($limit !== null && $elementsCounter > $limit) { + return; + } + + yield $item; + } + + return; + } + + // Register batch commands for all pages + $totalPages = (int)ceil($total / self::MAX_ELEMENTS_IN_PAGE); + for ($page = 1; $page <= $totalPages; $page++) { + $pageParams = [ + 'order' => $order, + 'filter' => $filter, + 'select' => $select, + 'page' => $page, + ]; + + if ($additionalParameters !== null) { + $pageParams = array_merge($pageParams, $additionalParameters); + } + + $this->registerCommand($apiMethod, $pageParams); + + if ($limit !== null && $limit < $page * self::MAX_ELEMENTS_IN_PAGE) { + break; + } + } + + $this->logger->debug( + 'getTraversableListWithCount.commandsRegistered', + [ + 'commandsCount' => $this->commands->count(), + 'totalItemsToSelect' => $total, + ] + ); + + $elementsCounter = 0; + foreach ($this->getTraversable(true) as $queryResultData) { + $resultElements = $this->extractElementsFromBatchResult($queryResultData, false); + foreach ($resultElements as $resultElement) { + ++$elementsCounter; + if ($limit !== null && $elementsCounter > $limit) { + return; + } + + yield $resultElement; + } + } + + $this->logger->debug('getTraversableListWithCount.finish'); + } +} + + diff --git a/src/Services/Biconnector/Connector/Service/Connector.php b/src/Services/Biconnector/Connector/Service/Connector.php index 30448dd4..3747a882 100644 --- a/src/Services/Biconnector/Connector/Service/Connector.php +++ b/src/Services/Biconnector/Connector/Service/Connector.php @@ -141,7 +141,7 @@ public function get(int $id): ConnectorResult * @param array $order - sort fields, e.g. ['id' => 'ASC'] * @param array $filter - filter fields * @param array $select - fields to include in the result - * @param int $start - offset for pagination + * @param int $page - page number for pagination (page size is 50 records per page) * * @throws BaseException * @throws TransportException @@ -151,7 +151,7 @@ public function get(int $id): ConnectorResult 'https://apidocs.bitrix24.com/api-reference/biconnector/connector/biconnector-connector-list.html', 'Get a list of connectors' )] - public function list(array $order = [], array $filter = [], array $select = [], int $start = 0): ConnectorsResult + public function list(array $order = [], array $filter = [], array $select = [], int $page = 1): ConnectorsResult { return new ConnectorsResult( $this->core->call( @@ -160,7 +160,7 @@ public function list(array $order = [], array $filter = [], array $select = [], 'order' => $order, 'filter' => $filter, 'select' => $select, - 'start' => $start, + 'page' => $page, ] ) ); From a03f5b5d1982e778c1b6a8ba6507b5178e6cc878 Mon Sep 17 00:00:00 2001 From: Dmitriy Ignatenko Date: Sat, 9 May 2026 01:25:51 +0400 Subject: [PATCH 03/18] Fix the fields for the add connector method --- .../Connector/Result/ConnectorItemResult.php | 26 +++-- .../Connector/Service/Connector.php | 27 ++++-- .../ConnectorItemResultAnnotationsTest.php | 80 +++++++++------- .../Connector/Service/BatchTest.php | 60 ++++++++---- .../Connector/Service/ConnectorTest.php | 95 +++++++++++-------- 5 files changed, 179 insertions(+), 109 deletions(-) diff --git a/src/Services/Biconnector/Connector/Result/ConnectorItemResult.php b/src/Services/Biconnector/Connector/Result/ConnectorItemResult.php index 7eef189e..8208b61c 100644 --- a/src/Services/Biconnector/Connector/Result/ConnectorItemResult.php +++ b/src/Services/Biconnector/Connector/Result/ConnectorItemResult.php @@ -14,17 +14,27 @@ namespace Bitrix24\SDK\Services\Biconnector\Connector\Result; use Bitrix24\SDK\Core\Result\AbstractItem; +use Carbon\CarbonImmutable; /** * Class ConnectorItemResult * + * Field names correspond to the actual API response returned by biconnector.connector.get / biconnector.connector.list. + * + * @see https://apidocs.bitrix24.com/api-reference/biconnector/connector/biconnector-connector-fields.html + * * @property-read int $id - * @property-read string $name - * @property-read string $code + * @property-read string $title + * @property-read string $logo * @property-read string|null $description - * @property-read string|null $pictureUrl + * @property-read int|null $sort + * @property-read string|null $urlCheck + * @property-read string|null $urlData + * @property-read string|null $urlTableList + * @property-read string|null $urlTableDescription * @property-read array|null $settings - * @property-read bool $isEnabled + * @property-read CarbonImmutable $dateCreate + * @property-read bool|null $supportMapping */ class ConnectorItemResult extends AbstractItem { @@ -32,9 +42,11 @@ class ConnectorItemResult extends AbstractItem public function __get($offset): mixed { return match ($offset) { - 'isEnabled' => isset($this->data[$offset]) ? (bool)$this->data[$offset] : null, - 'id' => isset($this->data[$offset]) ? (int)$this->data[$offset] : null, - default => $this->data[$offset] ?? null, + 'id' => isset($this->data[$offset]) ? (int)$this->data[$offset] : null, + 'sort' => isset($this->data[$offset]) ? (int)$this->data[$offset] : null, + 'dateCreate' => isset($this->data[$offset]) ? CarbonImmutable::parse($this->data[$offset]) : null, + 'supportMapping' => isset($this->data[$offset]) ? (bool)$this->data[$offset] : null, + default => $this->data[$offset] ?? null, }; } } diff --git a/src/Services/Biconnector/Connector/Service/Connector.php b/src/Services/Biconnector/Connector/Service/Connector.php index 3747a882..358587e5 100644 --- a/src/Services/Biconnector/Connector/Service/Connector.php +++ b/src/Services/Biconnector/Connector/Service/Connector.php @@ -45,12 +45,15 @@ public function __construct(public Batch $batch, CoreInterface $core, LoggerInte * @link https://apidocs.bitrix24.com/api-reference/biconnector/connector/biconnector-connector-add.html * * @param array{ - * name: string, - * code: string, + * title: string, + * logo: string, + * urlCheck: string, + * urlData: string, + * urlTableList: string, + * urlTableDescription: string, + * settings: array, * description?: string, - * pictureUrl?: string, - * settings?: array, - * isEnabled?: bool + * sort?: int, * } $fields * * @throws BaseException @@ -79,12 +82,16 @@ public function add(array $fields): AddedConnectorResult * @link https://apidocs.bitrix24.com/api-reference/biconnector/connector/biconnector-connector-update.html * * @param array{ - * name?: string, - * code?: string, + * title?: string, + * logo?: string, * description?: string, - * pictureUrl?: string, + * sort?: int, + * urlCheck?: string, + * urlData?: string, + * urlTableList?: string, + * urlTableDescription?: string, * settings?: array, - * isEnabled?: bool + * supportMapping?: bool, * } $fields * * @throws BaseException @@ -217,6 +224,6 @@ public function fields(): FieldsResult */ public function count(): int { - return $this->list()->getCoreResponse()->getResponseData()->getPagination()->getTotal(); + return (int)$this->list()->getCoreResponse()->getResponseData()->getPagination()->getTotal(); } } diff --git a/tests/Integration/Services/Biconnector/Connector/Result/ConnectorItemResultAnnotationsTest.php b/tests/Integration/Services/Biconnector/Connector/Result/ConnectorItemResultAnnotationsTest.php index 30d092f4..e420606e 100644 --- a/tests/Integration/Services/Biconnector/Connector/Result/ConnectorItemResultAnnotationsTest.php +++ b/tests/Integration/Services/Biconnector/Connector/Result/ConnectorItemResultAnnotationsTest.php @@ -18,12 +18,10 @@ use Bitrix24\SDK\Services\Biconnector\Connector\Service\Connector; use Bitrix24\SDK\Tests\CustomAssertions\CustomBitrix24Assertions; use Bitrix24\SDK\Tests\Integration\Factory; -use Faker\Generator; use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\Attributes\Test; use PHPUnit\Framework\Attributes\TestDox; use PHPUnit\Framework\TestCase; -use Faker; #[CoversClass(ConnectorItemResult::class)] class ConnectorItemResultAnnotationsTest extends TestCase @@ -32,10 +30,6 @@ class ConnectorItemResultAnnotationsTest extends TestCase private Connector $connectorService; - private Generator $faker; - - private int $createdConnectorId; - /** * @throws InvalidArgumentException */ @@ -43,50 +37,68 @@ class ConnectorItemResultAnnotationsTest extends TestCase protected function setUp(): void { $this->connectorService = Factory::getServiceBuilder()->getBiconnectorScope()->connector(); - $this->faker = Faker\Factory::create(); - - // Create a connector to use in tests - $this->createdConnectorId = $this->connectorService->add([ - 'name' => 'test-annotations-' . $this->faker->uuid(), - 'code' => 'code_' . substr($this->faker->uuid(), 0, 8), - ])->getId(); - } - - #[\Override] - protected function tearDown(): void - { - $this->connectorService->delete($this->createdConnectorId); } #[Test] - #[TestDox('all fields in ConnectorItemResult are annotated and match live API response')] + #[TestDox('all fields in ConnectorItemResult are annotated and match live API fields schema')] public function testAllSystemFieldsAnnotated(): void { - $rawItem = $this->connectorService->get($this->createdConnectorId) - ->getCoreResponse() - ->getResponseData() - ->getResult(); - - // Handle nested envelope: result['connector'] or result directly - if (!empty($rawItem['connector']) && is_array($rawItem['connector'])) { - $rawItem = $rawItem['connector']; - } + $fieldCodes = $this->getConnectorFieldCodes(); $this->assertBitrix24AllResultItemFieldsAnnotated( - array_keys($rawItem), + $fieldCodes, ConnectorItemResult::class ); } #[Test] - #[TestDox('all fields in ConnectorItemResult have valid type casting in magic getters')] + #[TestDox('all fields in ConnectorItemResult have valid type casting matching API fields schema')] public function testAllSystemFieldsHasValidTypeAnnotation(): void { - $connectorItemResult = $this->connectorService->get($this->createdConnectorId)->connector(); + $fieldTypesMap = $this->getConnectorFieldTypesMap(); - $this->assertBitrix24ResultItemFieldsTypeCastMatchAnnotations( - $connectorItemResult, + $this->assertBitrix24AllResultItemFieldsHasValidTypeAnnotation( + $fieldTypesMap, ConnectorItemResult::class ); } + + /** + * Returns list of field codes from biconnector.connector.fields API. + * + * @return array + */ + private function getConnectorFieldCodes(): array + { + $raw = $this->connectorService->fields()->getFieldsDescription(); + $fields = $raw['fields'] ?? []; + + return array_column($fields, 'title'); + } + + /** + * Returns field type map compatible with assertBitrix24AllResultItemFieldsHasValidTypeAnnotation. + * Normalises biconnector-specific types to types known by the shared assertion. + * + * @return array + */ + private function getConnectorFieldTypesMap(): array + { + $raw = $this->connectorService->fields()->getFieldsDescription(); + $fields = $raw['fields'] ?? []; + + $result = []; + foreach ($fields as $field) { + $apiType = $field['type']; + + // biconnector uses 'boolean' — map to 'char' which the shared assertion handles as bool + if ($apiType === 'boolean') { + $apiType = 'char'; + } + + $result[$field['title']] = ['type' => $apiType]; + } + + return $result; + } } diff --git a/tests/Integration/Services/Biconnector/Connector/Service/BatchTest.php b/tests/Integration/Services/Biconnector/Connector/Service/BatchTest.php index 68fd41e7..69709423 100644 --- a/tests/Integration/Services/Biconnector/Connector/Service/BatchTest.php +++ b/tests/Integration/Services/Biconnector/Connector/Service/BatchTest.php @@ -42,18 +42,51 @@ protected function setUp(): void $this->faker = Faker\Factory::create(); } + /** + * Returns the minimum set of required fields to create a connector. + * + * @return array{ + * title: string, + * logo: string, + * urlCheck: string, + * urlData: string, + * urlTableList: string, + * urlTableDescription: string, + * settings: array, + * } + */ + private function makeConnectorFields(string $title): array + { + return [ + 'title' => $title, + 'logo' => 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjIiIGhlaWdodD0iMjIiIHZpZXdCb3g9IjAgMCAyMiAyMiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KCTxjaXJjbGUgY3g9IjExIiBjeT0iMTEiIHI9IjEwIiBmaWxsPSIjRkYzQjNCIiAvPgoJPHRleHQgeD0iMTEiIHk9IjEzIiBmb250LWZhbWlseT0iQXJpYWwsIHNhbnMtc2VyaWYiIGZvbnQtc2l6ZT0iNiIgZmlsbD0iI0ZGRkZGRiIgdGV4dC1hbmNob3I9Im1pZGRsZSIgZm9udC13ZWlnaHQ9ImJvbGQiPlJFU1Q8L3RleHQ+Cjwvc3ZnPg==', + 'urlCheck' => 'http://example.com/api/check', + 'urlTableList' => 'http://example.com/api/table_list', + 'urlTableDescription' => 'http://example.com/api/table_description', + 'urlData' => 'http://example.com/api/data', + 'settings' => [ + [ + 'name' => 'Login', + 'type' => 'STRING', + 'code' => 'login', + ], + [ + 'name' => 'Password', + 'type' => 'STRING', + 'code' => 'password', + ], + ], + ]; + } + /** * @throws BaseException * @throws TransportException */ public function testBatchList(): void { - $name = 'connector-' . $this->faker->uuid(); - $code = 'code_' . substr($this->faker->uuid(), 0, 8); - $id = $this->connectorService->add([ - 'name' => $name, - 'code' => $code, - ])->getId(); + $title = 'connector-' . $this->faker->uuid(); + $id = $this->connectorService->add($this->makeConnectorFields($title))->getId(); $count = 0; foreach ($this->connectorService->batch->list([], [], [], 10) as $item) { @@ -74,14 +107,8 @@ public function testBatchList(): void public function testBatchAdd(): void { $connectors = []; - $codes = []; for ($i = 0; $i < 3; $i++) { - $code = 'code_' . substr($this->faker->uuid(), 0, 8); - $codes[] = $code; - $connectors[] = [ - 'name' => 'connector-batch-' . $this->faker->uuid(), - 'code' => $code, - ]; + $connectors[] = $this->makeConnectorFields('connector-batch-' . $this->faker->uuid()); } $addedIds = []; @@ -106,10 +133,9 @@ public function testBatchDelete(): void { $ids = []; for ($i = 0; $i < 2; $i++) { - $ids[] = $this->connectorService->add([ - 'name' => 'connector-del-batch-' . $this->faker->uuid(), - 'code' => 'code_' . substr($this->faker->uuid(), 0, 8), - ])->getId(); + $ids[] = $this->connectorService->add( + $this->makeConnectorFields('connector-del-batch-' . $this->faker->uuid()) + )->getId(); } foreach ($this->connectorService->batch->delete($ids) as $result) { diff --git a/tests/Integration/Services/Biconnector/Connector/Service/ConnectorTest.php b/tests/Integration/Services/Biconnector/Connector/Service/ConnectorTest.php index bb7486c3..cfac87a0 100644 --- a/tests/Integration/Services/Biconnector/Connector/Service/ConnectorTest.php +++ b/tests/Integration/Services/Biconnector/Connector/Service/ConnectorTest.php @@ -44,18 +44,51 @@ protected function setUp(): void $this->faker = Faker\Factory::create(); } + /** + * Returns the minimum set of required fields to create a connector. + * + * @return array{ + * title: string, + * logo: string, + * urlCheck: string, + * urlData: string, + * urlTableList: string, + * urlTableDescription: string, + * settings: array, + * } + */ + private function makeConnectorFields(string $title): array + { + return [ + 'title' => $title, + 'logo' => 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjIiIGhlaWdodD0iMjIiIHZpZXdCb3g9IjAgMCAyMiAyMiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KCTxjaXJjbGUgY3g9IjExIiBjeT0iMTEiIHI9IjEwIiBmaWxsPSIjRkYzQjNCIiAvPgoJPHRleHQgeD0iMTEiIHk9IjEzIiBmb250LWZhbWlseT0iQXJpYWwsIHNhbnMtc2VyaWYiIGZvbnQtc2l6ZT0iNiIgZmlsbD0iI0ZGRkZGRiIgdGV4dC1hbmNob3I9Im1pZGRsZSIgZm9udC13ZWlnaHQ9ImJvbGQiPlJFU1Q8L3RleHQ+Cjwvc3ZnPg==', + 'urlCheck' => 'http://example.com/api/check', + 'urlTableList' => 'http://example.com/api/table_list', + 'urlTableDescription' => 'http://example.com/api/table_description', + 'urlData' => 'http://example.com/api/data', + 'settings' => [ + [ + 'name' => 'Login', + 'type' => 'STRING', + 'code' => 'login', + ], + [ + 'name' => 'Password', + 'type' => 'STRING', + 'code' => 'password', + ], + ], + ]; + } + /** * @throws BaseException * @throws TransportException */ public function testAdd(): void { - $name = 'connector-' . $this->faker->uuid(); - $code = 'code_' . substr($this->faker->uuid(), 0, 8); - $id = $this->connectorService->add([ - 'name' => $name, - 'code' => $code, - ])->getId(); + $title = 'connector-' . $this->faker->uuid(); + $id = $this->connectorService->add($this->makeConnectorFields($title))->getId(); self::assertGreaterThanOrEqual(1, $id); @@ -69,17 +102,13 @@ public function testAdd(): void */ public function testGet(): void { - $name = 'connector-' . $this->faker->uuid(); - $code = 'code_' . substr($this->faker->uuid(), 0, 8); - $id = $this->connectorService->add([ - 'name' => $name, - 'code' => $code, - ])->getId(); + $title = 'connector-' . $this->faker->uuid(); + $id = $this->connectorService->add($this->makeConnectorFields($title))->getId(); $connectorItemResult = $this->connectorService->get($id)->connector(); self::assertInstanceOf(ConnectorItemResult::class, $connectorItemResult); self::assertEquals($id, $connectorItemResult->id); - self::assertEquals($name, $connectorItemResult->name); + self::assertEquals($title, $connectorItemResult->title); // Cleanup $this->connectorService->delete($id); @@ -91,12 +120,8 @@ public function testGet(): void */ public function testList(): void { - $name = 'connector-' . $this->faker->uuid(); - $code = 'code_' . substr($this->faker->uuid(), 0, 8); - $id = $this->connectorService->add([ - 'name' => $name, - 'code' => $code, - ])->getId(); + $title = 'connector-' . $this->faker->uuid(); + $id = $this->connectorService->add($this->makeConnectorFields($title))->getId(); $list = $this->connectorService->list()->getConnectors(); self::assertIsArray($list); @@ -112,21 +137,17 @@ public function testList(): void */ public function testUpdate(): void { - $name = 'connector-' . $this->faker->uuid(); - $code = 'code_' . substr($this->faker->uuid(), 0, 8); - $id = $this->connectorService->add([ - 'name' => $name, - 'code' => $code, - ])->getId(); - - $newName = $name . '-updated'; + $title = 'connector-' . $this->faker->uuid(); + $id = $this->connectorService->add($this->makeConnectorFields($title))->getId(); + + $newTitle = $title . '-updated'; self::assertTrue( $this->connectorService->update($id, [ - 'name' => $newName, + 'title' => $newTitle, ])->isSuccess() ); - self::assertEquals($newName, $this->connectorService->get($id)->connector()->name); + self::assertEquals($newTitle, $this->connectorService->get($id)->connector()->title); // Cleanup $this->connectorService->delete($id); @@ -138,12 +159,8 @@ public function testUpdate(): void */ public function testDelete(): void { - $name = 'connector-' . $this->faker->uuid(); - $code = 'code_' . substr($this->faker->uuid(), 0, 8); - $id = $this->connectorService->add([ - 'name' => $name, - 'code' => $code, - ])->getId(); + $title = 'connector-' . $this->faker->uuid(); + $id = $this->connectorService->add($this->makeConnectorFields($title))->getId(); self::assertTrue($this->connectorService->delete($id)->isSuccess()); } @@ -167,12 +184,8 @@ public function testCount(): void { $countBefore = $this->connectorService->count(); - $name = 'connector-' . $this->faker->uuid(); - $code = 'code_' . substr($this->faker->uuid(), 0, 8); - $id = $this->connectorService->add([ - 'name' => $name, - 'code' => $code, - ])->getId(); + $title = 'connector-' . $this->faker->uuid(); + $id = $this->connectorService->add($this->makeConnectorFields($title))->getId(); $countAfter = $this->connectorService->count(); self::assertEquals($countBefore + 1, $countAfter); From d5ff82d3ead5a5f8c89c9bc0be96b425cd760cbe Mon Sep 17 00:00:00 2001 From: Dmitriy Ignatenko Date: Wed, 13 May 2026 15:01:55 +0400 Subject: [PATCH 04/18] Fix adding connector --- .../Connector/Result/ConnectorItemResultAnnotationsTest.php | 2 +- .../Services/Biconnector/Connector/Service/BatchTest.php | 2 +- .../Services/Biconnector/Connector/Service/ConnectorTest.php | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/Integration/Services/Biconnector/Connector/Result/ConnectorItemResultAnnotationsTest.php b/tests/Integration/Services/Biconnector/Connector/Result/ConnectorItemResultAnnotationsTest.php index e420606e..6b6e8dd0 100644 --- a/tests/Integration/Services/Biconnector/Connector/Result/ConnectorItemResultAnnotationsTest.php +++ b/tests/Integration/Services/Biconnector/Connector/Result/ConnectorItemResultAnnotationsTest.php @@ -36,7 +36,7 @@ class ConnectorItemResultAnnotationsTest extends TestCase #[\Override] protected function setUp(): void { - $this->connectorService = Factory::getServiceBuilder()->getBiconnectorScope()->connector(); + $this->connectorService = Factory::getServiceBuilder(true)->getBiconnectorScope()->connector(); } #[Test] diff --git a/tests/Integration/Services/Biconnector/Connector/Service/BatchTest.php b/tests/Integration/Services/Biconnector/Connector/Service/BatchTest.php index 69709423..91c46159 100644 --- a/tests/Integration/Services/Biconnector/Connector/Service/BatchTest.php +++ b/tests/Integration/Services/Biconnector/Connector/Service/BatchTest.php @@ -38,7 +38,7 @@ class BatchTest extends TestCase #[\Override] protected function setUp(): void { - $this->connectorService = Factory::getServiceBuilder()->getBiconnectorScope()->connector(); + $this->connectorService = Factory::getServiceBuilder(true)->getBiconnectorScope()->connector(); $this->faker = Faker\Factory::create(); } diff --git a/tests/Integration/Services/Biconnector/Connector/Service/ConnectorTest.php b/tests/Integration/Services/Biconnector/Connector/Service/ConnectorTest.php index cfac87a0..dcf523e4 100644 --- a/tests/Integration/Services/Biconnector/Connector/Service/ConnectorTest.php +++ b/tests/Integration/Services/Biconnector/Connector/Service/ConnectorTest.php @@ -40,7 +40,7 @@ class ConnectorTest extends TestCase #[\Override] protected function setUp(): void { - $this->connectorService = Factory::getServiceBuilder()->getBiconnectorScope()->connector(); + $this->connectorService = Factory::getServiceBuilder(true)->getBiconnectorScope()->connector(); $this->faker = Faker\Factory::create(); } From 6eb3143c37e081d0e1fff1750ec852e817266c23 Mon Sep 17 00:00:00 2001 From: Dmitriy Ignatenko Date: Wed, 13 May 2026 16:45:03 +0400 Subject: [PATCH 05/18] Done biconnector.connector --- .../Connector/Result/ConnectorItemResult.php | 11 ++++++----- .../Biconnector/Connector/Result/ConnectorResult.php | 6 ++++-- .../Biconnector/Connector/Service/Connector.php | 10 +++++++++- 3 files changed, 19 insertions(+), 8 deletions(-) diff --git a/src/Services/Biconnector/Connector/Result/ConnectorItemResult.php b/src/Services/Biconnector/Connector/Result/ConnectorItemResult.php index 8208b61c..fdf1ae70 100644 --- a/src/Services/Biconnector/Connector/Result/ConnectorItemResult.php +++ b/src/Services/Biconnector/Connector/Result/ConnectorItemResult.php @@ -33,8 +33,8 @@ * @property-read string|null $urlTableList * @property-read string|null $urlTableDescription * @property-read array|null $settings - * @property-read CarbonImmutable $dateCreate * @property-read bool|null $supportMapping + * @property-read CarbonImmutable $dateCreate */ class ConnectorItemResult extends AbstractItem { @@ -42,11 +42,12 @@ class ConnectorItemResult extends AbstractItem public function __get($offset): mixed { return match ($offset) { - 'id' => isset($this->data[$offset]) ? (int)$this->data[$offset] : null, - 'sort' => isset($this->data[$offset]) ? (int)$this->data[$offset] : null, - 'dateCreate' => isset($this->data[$offset]) ? CarbonImmutable::parse($this->data[$offset]) : null, + 'id', 'sort' => isset($this->data[$offset]) ? (int)$this->data[$offset] : null, 'supportMapping' => isset($this->data[$offset]) ? (bool)$this->data[$offset] : null, - default => $this->data[$offset] ?? null, + 'dateCreate' => isset($this->data[$offset]) + ? CarbonImmutable::parse($this->data[$offset]) + : null, + default => $this->data[$offset] ?? null, }; } } diff --git a/src/Services/Biconnector/Connector/Result/ConnectorResult.php b/src/Services/Biconnector/Connector/Result/ConnectorResult.php index edf0edc1..dd1dbadd 100644 --- a/src/Services/Biconnector/Connector/Result/ConnectorResult.php +++ b/src/Services/Biconnector/Connector/Result/ConnectorResult.php @@ -28,10 +28,12 @@ public function connector(): ConnectorItemResult { $result = $this->getCoreResponse()->getResponseData()->getResult(); - if (!empty($result['connector']) && is_array($result['connector'])) { - $result = $result['connector']; + // biconnector.connector.get returns the item under the 'item' key + if (!empty($result['item']) && is_array($result['item'])) { + return new ConnectorItemResult($result['item']); } + // Fallback: flat object at result level {"id": ..., "title": ..., ...} return new ConnectorItemResult($result); } } diff --git a/src/Services/Biconnector/Connector/Service/Connector.php b/src/Services/Biconnector/Connector/Service/Connector.php index 358587e5..3acf15d7 100644 --- a/src/Services/Biconnector/Connector/Service/Connector.php +++ b/src/Services/Biconnector/Connector/Service/Connector.php @@ -219,11 +219,19 @@ public function fields(): FieldsResult /** * Count connectors * + * Note: biconnector.connector.list does not return a total count in pagination, + * so we iterate all available items via batch to count them. + * * @throws BaseException * @throws TransportException */ public function count(): int { - return (int)$this->list()->getCoreResponse()->getResponseData()->getPagination()->getTotal(); + $count = 0; + foreach ($this->batch->list() as $item) { + $count++; + } + + return $count; } } From 87a829806f61788df1e0c093278bd0705d9be482 Mon Sep 17 00:00:00 2001 From: Dmitriy Ignatenko Date: Wed, 13 May 2026 16:53:45 +0400 Subject: [PATCH 06/18] Change author email --- src/Services/Biconnector/BiconnectorServiceBuilder.php | 2 +- src/Services/Biconnector/Connector/Batch.php | 2 +- .../Biconnector/Connector/Result/AddedConnectorBatchResult.php | 2 +- .../Biconnector/Connector/Result/AddedConnectorResult.php | 2 +- .../Biconnector/Connector/Result/ConnectorItemResult.php | 2 +- src/Services/Biconnector/Connector/Result/ConnectorResult.php | 2 +- src/Services/Biconnector/Connector/Result/ConnectorsResult.php | 2 +- .../Connector/Result/DeletedConnectorBatchResult.php | 2 +- .../Biconnector/Connector/Result/DeletedConnectorResult.php | 2 +- .../Connector/Result/UpdatedConnectorBatchResult.php | 2 +- .../Biconnector/Connector/Result/UpdatedConnectorResult.php | 2 +- src/Services/Biconnector/Connector/Service/Batch.php | 2 +- src/Services/Biconnector/Connector/Service/Connector.php | 2 +- .../Connector/Result/ConnectorItemResultAnnotationsTest.php | 2 +- .../Services/Biconnector/Connector/Service/BatchTest.php | 2 +- .../Services/Biconnector/Connector/Service/ConnectorTest.php | 2 +- .../Unit/Services/Biconnector/BiconnectorServiceBuilderTest.php | 2 +- 17 files changed, 17 insertions(+), 17 deletions(-) diff --git a/src/Services/Biconnector/BiconnectorServiceBuilder.php b/src/Services/Biconnector/BiconnectorServiceBuilder.php index cba02ece..42bbdaf2 100644 --- a/src/Services/Biconnector/BiconnectorServiceBuilder.php +++ b/src/Services/Biconnector/BiconnectorServiceBuilder.php @@ -3,7 +3,7 @@ /** * This file is part of the bitrix24-php-sdk package. * - * © Dmitriy Ignatenko + * © Dmitriy Ignatenko * * For the full copyright and license information, please view the MIT-LICENSE.txt * file that was distributed with this source code. diff --git a/src/Services/Biconnector/Connector/Batch.php b/src/Services/Biconnector/Connector/Batch.php index 29c1d96c..b1323e80 100644 --- a/src/Services/Biconnector/Connector/Batch.php +++ b/src/Services/Biconnector/Connector/Batch.php @@ -3,7 +3,7 @@ /** * This file is part of the bitrix24-php-sdk package. * - * © Dmitriy Ignatenko + * © Dmitriy Ignatenko * * For the full copyright and license information, please view the MIT-LICENSE.txt * file that was distributed with this source code. diff --git a/src/Services/Biconnector/Connector/Result/AddedConnectorBatchResult.php b/src/Services/Biconnector/Connector/Result/AddedConnectorBatchResult.php index 9db7f5e6..6a3c7f13 100644 --- a/src/Services/Biconnector/Connector/Result/AddedConnectorBatchResult.php +++ b/src/Services/Biconnector/Connector/Result/AddedConnectorBatchResult.php @@ -3,7 +3,7 @@ /** * This file is part of the bitrix24-php-sdk package. * - * © Dmitriy Ignatenko + * © Dmitriy Ignatenko * * For the full copyright and license information, please view the MIT-LICENSE.txt * file that was distributed with this source code. diff --git a/src/Services/Biconnector/Connector/Result/AddedConnectorResult.php b/src/Services/Biconnector/Connector/Result/AddedConnectorResult.php index 1c04a49c..6f16fc0e 100644 --- a/src/Services/Biconnector/Connector/Result/AddedConnectorResult.php +++ b/src/Services/Biconnector/Connector/Result/AddedConnectorResult.php @@ -3,7 +3,7 @@ /** * This file is part of the bitrix24-php-sdk package. * - * © Dmitriy Ignatenko + * © Dmitriy Ignatenko * * For the full copyright and license information, please view the MIT-LICENSE.txt * file that was distributed with this source code. diff --git a/src/Services/Biconnector/Connector/Result/ConnectorItemResult.php b/src/Services/Biconnector/Connector/Result/ConnectorItemResult.php index fdf1ae70..479b16b6 100644 --- a/src/Services/Biconnector/Connector/Result/ConnectorItemResult.php +++ b/src/Services/Biconnector/Connector/Result/ConnectorItemResult.php @@ -3,7 +3,7 @@ /** * This file is part of the bitrix24-php-sdk package. * - * © Dmitriy Ignatenko + * © Dmitriy Ignatenko * * For the full copyright and license information, please view the MIT-LICENSE.txt * file that was distributed with this source code. diff --git a/src/Services/Biconnector/Connector/Result/ConnectorResult.php b/src/Services/Biconnector/Connector/Result/ConnectorResult.php index dd1dbadd..ae162bce 100644 --- a/src/Services/Biconnector/Connector/Result/ConnectorResult.php +++ b/src/Services/Biconnector/Connector/Result/ConnectorResult.php @@ -3,7 +3,7 @@ /** * This file is part of the bitrix24-php-sdk package. * - * © Dmitriy Ignatenko + * © Dmitriy Ignatenko * * For the full copyright and license information, please view the MIT-LICENSE.txt * file that was distributed with this source code. diff --git a/src/Services/Biconnector/Connector/Result/ConnectorsResult.php b/src/Services/Biconnector/Connector/Result/ConnectorsResult.php index 3fbfa144..96530ccf 100644 --- a/src/Services/Biconnector/Connector/Result/ConnectorsResult.php +++ b/src/Services/Biconnector/Connector/Result/ConnectorsResult.php @@ -3,7 +3,7 @@ /** * This file is part of the bitrix24-php-sdk package. * - * © Dmitriy Ignatenko + * © Dmitriy Ignatenko * * For the full copyright and license information, please view the MIT-LICENSE.txt * file that was distributed with this source code. diff --git a/src/Services/Biconnector/Connector/Result/DeletedConnectorBatchResult.php b/src/Services/Biconnector/Connector/Result/DeletedConnectorBatchResult.php index ae8f5d51..a3790231 100644 --- a/src/Services/Biconnector/Connector/Result/DeletedConnectorBatchResult.php +++ b/src/Services/Biconnector/Connector/Result/DeletedConnectorBatchResult.php @@ -3,7 +3,7 @@ /** * This file is part of the bitrix24-php-sdk package. * - * © Dmitriy Ignatenko + * © Dmitriy Ignatenko * * For the full copyright and license information, please view the MIT-LICENSE.txt * file that was distributed with this source code. diff --git a/src/Services/Biconnector/Connector/Result/DeletedConnectorResult.php b/src/Services/Biconnector/Connector/Result/DeletedConnectorResult.php index 979bf396..b06bba3a 100644 --- a/src/Services/Biconnector/Connector/Result/DeletedConnectorResult.php +++ b/src/Services/Biconnector/Connector/Result/DeletedConnectorResult.php @@ -3,7 +3,7 @@ /** * This file is part of the bitrix24-php-sdk package. * - * © Dmitriy Ignatenko + * © Dmitriy Ignatenko * * For the full copyright and license information, please view the MIT-LICENSE.txt * file that was distributed with this source code. diff --git a/src/Services/Biconnector/Connector/Result/UpdatedConnectorBatchResult.php b/src/Services/Biconnector/Connector/Result/UpdatedConnectorBatchResult.php index 26b50085..b0cbe9c4 100644 --- a/src/Services/Biconnector/Connector/Result/UpdatedConnectorBatchResult.php +++ b/src/Services/Biconnector/Connector/Result/UpdatedConnectorBatchResult.php @@ -3,7 +3,7 @@ /** * This file is part of the bitrix24-php-sdk package. * - * © Dmitriy Ignatenko + * © Dmitriy Ignatenko * * For the full copyright and license information, please view the MIT-LICENSE.txt * file that was distributed with this source code. diff --git a/src/Services/Biconnector/Connector/Result/UpdatedConnectorResult.php b/src/Services/Biconnector/Connector/Result/UpdatedConnectorResult.php index eec333d8..5687dfc4 100644 --- a/src/Services/Biconnector/Connector/Result/UpdatedConnectorResult.php +++ b/src/Services/Biconnector/Connector/Result/UpdatedConnectorResult.php @@ -3,7 +3,7 @@ /** * This file is part of the bitrix24-php-sdk package. * - * © Dmitriy Ignatenko + * © Dmitriy Ignatenko * * For the full copyright and license information, please view the MIT-LICENSE.txt * file that was distributed with this source code. diff --git a/src/Services/Biconnector/Connector/Service/Batch.php b/src/Services/Biconnector/Connector/Service/Batch.php index 01c31a8d..bdfbcf25 100644 --- a/src/Services/Biconnector/Connector/Service/Batch.php +++ b/src/Services/Biconnector/Connector/Service/Batch.php @@ -3,7 +3,7 @@ /** * This file is part of the bitrix24-php-sdk package. * - * © Dmitriy Ignatenko + * © Dmitriy Ignatenko * * For the full copyright and license information, please view the MIT-LICENSE.txt * file that was distributed with this source code. diff --git a/src/Services/Biconnector/Connector/Service/Connector.php b/src/Services/Biconnector/Connector/Service/Connector.php index 3acf15d7..1ef76aeb 100644 --- a/src/Services/Biconnector/Connector/Service/Connector.php +++ b/src/Services/Biconnector/Connector/Service/Connector.php @@ -3,7 +3,7 @@ /** * This file is part of the bitrix24-php-sdk package. * - * © Dmitriy Ignatenko + * © Dmitriy Ignatenko * * For the full copyright and license information, please view the MIT-LICENSE.txt * file that was distributed with this source code. diff --git a/tests/Integration/Services/Biconnector/Connector/Result/ConnectorItemResultAnnotationsTest.php b/tests/Integration/Services/Biconnector/Connector/Result/ConnectorItemResultAnnotationsTest.php index 6b6e8dd0..f70b47b6 100644 --- a/tests/Integration/Services/Biconnector/Connector/Result/ConnectorItemResultAnnotationsTest.php +++ b/tests/Integration/Services/Biconnector/Connector/Result/ConnectorItemResultAnnotationsTest.php @@ -3,7 +3,7 @@ /** * This file is part of the bitrix24-php-sdk package. * - * © Dmitriy Ignatenko + * © Dmitriy Ignatenko * * For the full copyright and license information, please view the MIT-LICENSE.txt * file that was distributed with this source code. diff --git a/tests/Integration/Services/Biconnector/Connector/Service/BatchTest.php b/tests/Integration/Services/Biconnector/Connector/Service/BatchTest.php index 91c46159..87df4ab6 100644 --- a/tests/Integration/Services/Biconnector/Connector/Service/BatchTest.php +++ b/tests/Integration/Services/Biconnector/Connector/Service/BatchTest.php @@ -3,7 +3,7 @@ /** * This file is part of the bitrix24-php-sdk package. * - * © Dmitriy Ignatenko + * © Dmitriy Ignatenko * * For the full copyright and license information, please view the MIT-LICENSE.txt * file that was distributed with this source code. diff --git a/tests/Integration/Services/Biconnector/Connector/Service/ConnectorTest.php b/tests/Integration/Services/Biconnector/Connector/Service/ConnectorTest.php index dcf523e4..ffa766fc 100644 --- a/tests/Integration/Services/Biconnector/Connector/Service/ConnectorTest.php +++ b/tests/Integration/Services/Biconnector/Connector/Service/ConnectorTest.php @@ -3,7 +3,7 @@ /** * This file is part of the bitrix24-php-sdk package. * - * © Dmitriy Ignatenko + * © Dmitriy Ignatenko * * For the full copyright and license information, please view the MIT-LICENSE.txt * file that was distributed with this source code. diff --git a/tests/Unit/Services/Biconnector/BiconnectorServiceBuilderTest.php b/tests/Unit/Services/Biconnector/BiconnectorServiceBuilderTest.php index b939cb53..783b2cb2 100644 --- a/tests/Unit/Services/Biconnector/BiconnectorServiceBuilderTest.php +++ b/tests/Unit/Services/Biconnector/BiconnectorServiceBuilderTest.php @@ -3,7 +3,7 @@ /** * This file is part of the bitrix24-php-sdk package. * - * © Dmitriy Ignatenko + * © Dmitriy Ignatenko * * For the full copyright and license information, please view the MIT-LICENSE.txt * file that was distributed with this source code. From 51fb4bb2945b3a846b5039910c4abae45ebca759 Mon Sep 17 00:00:00 2001 From: Dmitriy Ignatenko Date: Thu, 14 May 2026 20:24:48 +0400 Subject: [PATCH 07/18] This is the first implementation for biconnector.source methods --- .tasks/469/plan.md | 124 ++++++++++ CHANGELOG.md | 10 + Makefile | 4 + phpunit.xml.dist | 4 + .../Biconnector/BiconnectorServiceBuilder.php | 26 ++ src/Services/Biconnector/Source/Batch.php | 108 +++++++++ .../Source/Result/AddedSourceBatchResult.php | 35 +++ .../Source/Result/AddedSourceResult.php | 42 ++++ .../Result/DeletedSourceBatchResult.php | 29 +++ .../Source/Result/DeletedSourceResult.php | 33 +++ .../Source/Result/SourceItemResult.php | 54 +++++ .../Source/Result/SourceResult.php | 56 +++++ .../Source/Result/SourcesResult.php | 46 ++++ .../Result/UpdatedSourceBatchResult.php | 29 +++ .../Source/Result/UpdatedSourceResult.php | 33 +++ .../Biconnector/Source/Service/Batch.php | 170 +++++++++++++ .../Biconnector/Source/Service/Source.php | 223 +++++++++++++++++ .../SourceItemResultAnnotationsTest.php | 167 +++++++++++++ .../Biconnector/Source/Service/BatchTest.php | 167 +++++++++++++ .../Biconnector/Source/Service/SourceTest.php | 225 ++++++++++++++++++ .../BiconnectorServiceBuilderTest.php | 5 + 21 files changed, 1590 insertions(+) create mode 100644 .tasks/469/plan.md create mode 100644 src/Services/Biconnector/Source/Batch.php create mode 100644 src/Services/Biconnector/Source/Result/AddedSourceBatchResult.php create mode 100644 src/Services/Biconnector/Source/Result/AddedSourceResult.php create mode 100644 src/Services/Biconnector/Source/Result/DeletedSourceBatchResult.php create mode 100644 src/Services/Biconnector/Source/Result/DeletedSourceResult.php create mode 100644 src/Services/Biconnector/Source/Result/SourceItemResult.php create mode 100644 src/Services/Biconnector/Source/Result/SourceResult.php create mode 100644 src/Services/Biconnector/Source/Result/SourcesResult.php create mode 100644 src/Services/Biconnector/Source/Result/UpdatedSourceBatchResult.php create mode 100644 src/Services/Biconnector/Source/Result/UpdatedSourceResult.php create mode 100644 src/Services/Biconnector/Source/Service/Batch.php create mode 100644 src/Services/Biconnector/Source/Service/Source.php create mode 100644 tests/Integration/Services/Biconnector/Source/Result/SourceItemResultAnnotationsTest.php create mode 100644 tests/Integration/Services/Biconnector/Source/Service/BatchTest.php create mode 100644 tests/Integration/Services/Biconnector/Source/Service/SourceTest.php diff --git a/.tasks/469/plan.md b/.tasks/469/plan.md new file mode 100644 index 00000000..601b3883 --- /dev/null +++ b/.tasks/469/plan.md @@ -0,0 +1,124 @@ +# Plan: Add `biconnector.source.*` service support (issue #469) + +## Context + +The `biconnector` scope already has `Connector` (add/get/list/update/delete/fields/count) implemented. +Now we add the `Source` entity, covering six REST methods: + +| REST method | Response envelope | Notes | +|------------------------------|-------------------------------|----------------------------| +| `biconnector.source.add` | `result.id` (int) | Fields wrapped in `fields` | +| `biconnector.source.update` | `result` = `true` | | +| `biconnector.source.get` | `result.item.connection` + root connectorId/settings | Nested; must be flattened | +| `biconnector.source.list` | `result` = flat array | Standard list method | +| `biconnector.source.delete` | `result` = `true` | | +| `biconnector.source.fields` | `result.fields` array | Returns `FieldsResult` | + +### Key API notes +- `biconnector.source.delete` uses lowercase `id` parameter (not `ID`) +- `biconnector.source.get` returns nested: `result.item.connection.{id,type,code,title,...}` plus `result.item.connectorId` and `result.item.settings` +- `SourceResult` must flatten `connection` + root fields into a single `SourceItemResult` +- `biconnector.source.fields` lists: id, title, type, code, description, active, dateCreate, dateUpdate, createdById, updatedById, connectorId, settings (all camelCase) + +### SourceItemResult field types +| Field | Bitrix24 type | PHP type | +|---------------|---------------|-------------------| +| id | integer | int | +| title | string | string | +| type | string | string\|null | +| code | string | string\|null | +| description | string | string\|null | +| active | boolean | bool\|null | +| dateCreate | datetime | CarbonImmutable | +| dateUpdate | datetime | CarbonImmutable | +| createdById | integer | int | +| updatedById | integer | int | +| connectorId | integer | int | +| settings | array | array\|null | + +--- + +## Files to Create + +### 1. `src/Services/Biconnector/Source/Result/SourceItemResult.php` +All 12 fields annotated with @property-read, `__get` with match expression. + +### 2. `src/Services/Biconnector/Source/Result/SourceResult.php` +Reads `result.item.connection` merged with root fields (connectorId, settings), returns `SourceItemResult`. + +### 3. `src/Services/Biconnector/Source/Result/SourcesResult.php` +Reads flat `result` array, returns `SourceItemResult[]` via `getSources()`. + +### 4. `src/Services/Biconnector/Source/Result/AddedSourceResult.php` +Extends `AddedItemResult`, reads `result['id']`. + +### 5. `src/Services/Biconnector/Source/Result/AddedSourceBatchResult.php` +Extends `AddedItemBatchResult`, reads `result['id']`. + +### 6. `src/Services/Biconnector/Source/Result/UpdatedSourceResult.php` +Extends `UpdatedItemResult`, `isSuccess()` returns `(bool)$result`. + +### 7. `src/Services/Biconnector/Source/Result/UpdatedSourceBatchResult.php` +Extends `UpdatedItemBatchResult`, `isSuccess()` returns `(bool)$result`. + +### 8. `src/Services/Biconnector/Source/Result/DeletedSourceResult.php` +Extends `DeletedItemResult`, `isSuccess()` returns `(bool)$result`. + +### 9. `src/Services/Biconnector/Source/Result/DeletedSourceBatchResult.php` +Extends `DeletedItemBatchResult`, `isSuccess()` returns `(bool)$result`. + +### 10. `src/Services/Biconnector/Source/Batch.php` +Extends `\Bitrix24\SDK\Core\Batch`, overrides `determineKeyId` → 'id' and `deleteEntityItems` → lowercase 'id'. + +### 11. `src/Services/Biconnector/Source/Service/Source.php` +Methods: add, update, get, list, delete, fields, count. + +### 12. `src/Services/Biconnector/Source/Service/Batch.php` +Batch service: list, add, update, delete. + +### 13. `tests/Unit/Services/Biconnector/SourceServiceBuilderTest.php` +Unit test: sourceService is cached. + +### 14. `tests/Integration/Services/Biconnector/Source/Service/SourceTest.php` +Integration tests: add, get, list, update, delete, fields, count. + +### 15. `tests/Integration/Services/Biconnector/Source/Service/BatchTest.php` +Integration tests: batchList, batchAdd, batchDelete. + +### 16. `tests/Integration/Services/Biconnector/Source/Result/SourceItemResultAnnotationsTest.php` +Annotation tests: testAllSystemFieldsAnnotated, testAllSystemFieldsHasValidTypeAnnotation. + +--- + +## Files to Modify + +### 1. `src/Services/Biconnector/BiconnectorServiceBuilder.php` +Add `source(): Source` method using `SourceBatch` custom batch. + +### 2. `phpunit.xml.dist` +Add `integration_tests_biconnector_source` test suite. + +### 3. `Makefile` +Add `test-integration-biconnector-source` target. + +### 4. `CHANGELOG.md` +Add entry under `## 3.2.0 – UNRELEASED` → `### Added`. + +--- + +## Deptrac compliance +New namespaces under `Biconnector\Source` follow the same layer as `Biconnector\Connector` — no new cross-layer dependencies. + +--- + +## Verification + +```bash +make lint-cs-fixer +make lint-rector +make lint-phpstan +make lint-deptrac +make test-unit +make test-integration-biconnector-source +``` + diff --git a/CHANGELOG.md b/CHANGELOG.md index 600534e3..a705375f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,16 @@ ### Added +- Added service `Services\Biconnector\Source` with support methods, + see [biconnector.source.* methods](https://apidocs.bitrix24.com/api-reference/biconnector/source/index.html) ([#469](https://github.com/bitrix24/b24phpsdk/issues/469)): + - `add` adds a new data source, with batch calls support + - `update` updates an existing data source, with batch calls support + - `get` gets information about the data source by its identifier + - `list` gets the list of data sources, with batch calls support + - `delete` deletes a data source, with batch calls support + - `fields` returns the fields description + - `count` counts data sources +- Added `source()` accessor to `BiconnectorServiceBuilder` ([#469](https://github.com/bitrix24/b24phpsdk/issues/469)) - Added service `Services\Biconnector\Connector` with support methods, see [biconnector.connector.* methods](https://github.com/bitrix24/b24phpsdk/issues/469): - `add` adds a new connector, with batch calls support diff --git a/Makefile b/Makefile index c0801cbc..4c0c295b 100644 --- a/Makefile +++ b/Makefile @@ -615,6 +615,10 @@ test-integration-scope-biconnector: test-integration-biconnector-connector: docker compose run --rm php-cli vendor/bin/phpunit --testsuite integration_tests_biconnector_connector +.PHONY: test-integration-biconnector-source +test-integration-biconnector-source: + docker compose run --rm php-cli vendor/bin/phpunit --testsuite integration_tests_biconnector_source + .PHONY: integration_tests_crm_documentgenerator_document integration_tests_crm_documentgenerator_document: docker compose run --rm php-cli vendor/bin/phpunit --testsuite integration_tests_crm_documentgenerator_document diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 4ab7033d..0fa842c5 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -339,6 +339,10 @@ ./tests/Integration/Services/Biconnector/Connector/Service/ ./tests/Integration/Services/Biconnector/Connector/Result/ + + ./tests/Integration/Services/Biconnector/Source/Service/ + ./tests/Integration/Services/Biconnector/Source/Result/ + diff --git a/src/Services/Biconnector/BiconnectorServiceBuilder.php b/src/Services/Biconnector/BiconnectorServiceBuilder.php index 42bbdaf2..b32ffa3d 100644 --- a/src/Services/Biconnector/BiconnectorServiceBuilder.php +++ b/src/Services/Biconnector/BiconnectorServiceBuilder.php @@ -19,6 +19,9 @@ use Bitrix24\SDK\Services\Biconnector\Connector\Batch as ConnectorBatch; use Bitrix24\SDK\Services\Biconnector\Connector\Service\Batch; use Bitrix24\SDK\Services\Biconnector\Connector\Service\Connector; +use Bitrix24\SDK\Services\Biconnector\Source\Batch as SourceBatch; +use Bitrix24\SDK\Services\Biconnector\Source\Service\Batch as SourceServiceBatch; +use Bitrix24\SDK\Services\Biconnector\Source\Service\Source; #[ApiServiceBuilderMetadata(new Scope(['biconnector']))] class BiconnectorServiceBuilder extends AbstractServiceBuilder @@ -47,4 +50,27 @@ public function connector(): Connector return $this->serviceCache[__METHOD__]; } + + /** + * Get the Source service + * + * Uses a specialized SourceBatch to handle biconnector.source.* REST API differences: + * - delete uses lowercase 'id' instead of 'ID' + */ + public function source(): Source + { + if (!isset($this->serviceCache[__METHOD__])) { + $sourceBatch = new SourceBatch( + $this->core, + $this->log + ); + $this->serviceCache[__METHOD__] = new Source( + new SourceServiceBatch($sourceBatch, $this->log), + $this->core, + $this->log + ); + } + + return $this->serviceCache[__METHOD__]; + } } diff --git a/src/Services/Biconnector/Source/Batch.php b/src/Services/Biconnector/Source/Batch.php new file mode 100644 index 00000000..280772f9 --- /dev/null +++ b/src/Services/Biconnector/Source/Batch.php @@ -0,0 +1,108 @@ + + * + * For the full copyright and license information, please view the MIT-LICENSE.txt + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Bitrix24\SDK\Services\Biconnector\Source; + +use Bitrix24\SDK\Core\Exceptions\BaseException; +use Bitrix24\SDK\Core\Exceptions\InvalidArgumentException; +use Bitrix24\SDK\Core\Response\DTO\ResponseData; +use Generator; + +/** + * Class Batch + * + * Overrides base Batch to handle parameter naming differences in biconnector.source.* REST methods: + * - delete uses lowercase 'id' instead of 'ID' + * + * @see https://apidocs.bitrix24.com/api-reference/biconnector/source/biconnector-source-delete.html + */ +class Batch extends \Bitrix24\SDK\Core\Batch +{ + /** + * Determines the ID key — lowercase 'id' for biconnector source + */ + #[\Override] + protected function determineKeyId(string $apiMethod, ?array $additionalParameters): string + { + return 'id'; + } + + /** + * Delete entity items with batch call using lowercase 'id' parameter + * + * @param int[] $entityItemId + * @param array|null $additionalParameters + * + * @return Generator|ResponseData[] + * @throws BaseException + */ + #[\Override] + public function deleteEntityItems( + string $apiMethod, + array $entityItemId, + ?array $additionalParameters = null + ): Generator { + $this->logger->debug( + 'deleteEntityItems.start', + [ + 'apiMethod' => $apiMethod, + 'entityItems' => $entityItemId, + 'additionalParameters' => $additionalParameters, + ] + ); + + try { + $this->clearCommands(); + foreach ($entityItemId as $cnt => $itemId) { + if (!is_int($itemId)) { + throw new InvalidArgumentException( + sprintf( + 'invalid type «%s» of source id «%s» at position %s, source id must be integer type', + gettype($itemId), + $itemId, + $cnt + ) + ); + } + + $this->registerCommand($apiMethod, ['id' => $itemId]); + } + + foreach ($this->getTraversable(true) as $cnt => $deletedItemResult) { + yield $cnt => $deletedItemResult; + } + } catch (InvalidArgumentException $exception) { + $errorMessage = sprintf('batch delete source items: %s', $exception->getMessage()); + $this->logger->error( + $errorMessage, + [ + 'trace' => $exception->getTrace(), + ] + ); + throw $exception; + } catch (\Throwable $exception) { + $errorMessage = sprintf('batch delete source items: %s', $exception->getMessage()); + $this->logger->error( + $errorMessage, + [ + 'trace' => $exception->getTrace(), + ] + ); + + throw new BaseException($errorMessage, $exception->getCode(), $exception); + } + + $this->logger->debug('deleteEntityItems.finish'); + } +} + diff --git a/src/Services/Biconnector/Source/Result/AddedSourceBatchResult.php b/src/Services/Biconnector/Source/Result/AddedSourceBatchResult.php new file mode 100644 index 00000000..626d80f5 --- /dev/null +++ b/src/Services/Biconnector/Source/Result/AddedSourceBatchResult.php @@ -0,0 +1,35 @@ + + * + * For the full copyright and license information, please view the MIT-LICENSE.txt + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Bitrix24\SDK\Services\Biconnector\Source\Result; + +use Bitrix24\SDK\Core\Result\AddedItemBatchResult; + +/** + * Class AddedSourceBatchResult + */ +class AddedSourceBatchResult extends AddedItemBatchResult +{ + #[\Override] + public function getId(): int + { + $result = $this->getResponseData()->getResult(); + + if (!empty($result['id'])) { + return (int)$result['id']; + } + + return (int)$result; + } +} + diff --git a/src/Services/Biconnector/Source/Result/AddedSourceResult.php b/src/Services/Biconnector/Source/Result/AddedSourceResult.php new file mode 100644 index 00000000..a47a806d --- /dev/null +++ b/src/Services/Biconnector/Source/Result/AddedSourceResult.php @@ -0,0 +1,42 @@ + + * + * For the full copyright and license information, please view the MIT-LICENSE.txt + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Bitrix24\SDK\Services\Biconnector\Source\Result; + +use Bitrix24\SDK\Core\Exceptions\BaseException; +use Bitrix24\SDK\Core\Result\AddedItemResult; + +/** + * Class AddedSourceResult + * + * Wraps the response from biconnector.source.add. + * The API returns: result.id (integer) + */ +class AddedSourceResult extends AddedItemResult +{ + /** + * @throws BaseException + */ + #[\Override] + public function getId(): int + { + $result = $this->getCoreResponse()->getResponseData()->getResult(); + + if (!empty($result['id'])) { + return (int)$result['id']; + } + + return (int)$result; + } +} + diff --git a/src/Services/Biconnector/Source/Result/DeletedSourceBatchResult.php b/src/Services/Biconnector/Source/Result/DeletedSourceBatchResult.php new file mode 100644 index 00000000..154729eb --- /dev/null +++ b/src/Services/Biconnector/Source/Result/DeletedSourceBatchResult.php @@ -0,0 +1,29 @@ + + * + * For the full copyright and license information, please view the MIT-LICENSE.txt + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Bitrix24\SDK\Services\Biconnector\Source\Result; + +use Bitrix24\SDK\Core\Result\DeletedItemBatchResult; + +/** + * Class DeletedSourceBatchResult + */ +class DeletedSourceBatchResult extends DeletedItemBatchResult +{ + #[\Override] + public function isSuccess(): bool + { + return (bool)$this->getResponseData()->getResult(); + } +} + diff --git a/src/Services/Biconnector/Source/Result/DeletedSourceResult.php b/src/Services/Biconnector/Source/Result/DeletedSourceResult.php new file mode 100644 index 00000000..0b2c24c5 --- /dev/null +++ b/src/Services/Biconnector/Source/Result/DeletedSourceResult.php @@ -0,0 +1,33 @@ + + * + * For the full copyright and license information, please view the MIT-LICENSE.txt + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Bitrix24\SDK\Services\Biconnector\Source\Result; + +use Bitrix24\SDK\Core\Exceptions\BaseException; +use Bitrix24\SDK\Core\Result\DeletedItemResult; + +/** + * Class DeletedSourceResult + */ +class DeletedSourceResult extends DeletedItemResult +{ + /** + * @throws BaseException + */ + #[\Override] + public function isSuccess(): bool + { + return (bool)$this->getCoreResponse()->getResponseData()->getResult(); + } +} + diff --git a/src/Services/Biconnector/Source/Result/SourceItemResult.php b/src/Services/Biconnector/Source/Result/SourceItemResult.php new file mode 100644 index 00000000..bddfad96 --- /dev/null +++ b/src/Services/Biconnector/Source/Result/SourceItemResult.php @@ -0,0 +1,54 @@ + + * + * For the full copyright and license information, please view the MIT-LICENSE.txt + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Bitrix24\SDK\Services\Biconnector\Source\Result; + +use Bitrix24\SDK\Core\Result\AbstractItem; +use Carbon\CarbonImmutable; + +/** + * Class SourceItemResult + * + * Field names correspond to the actual API response returned by biconnector.source.get / biconnector.source.list. + * + * @see https://apidocs.bitrix24.com/api-reference/biconnector/source/biconnector-source-fields.html + * + * @property-read int $id + * @property-read string $title + * @property-read string|null $type + * @property-read string|null $code + * @property-read string|null $description + * @property-read bool|null $active + * @property-read CarbonImmutable $dateCreate + * @property-read CarbonImmutable $dateUpdate + * @property-read int $createdById + * @property-read int $updatedById + * @property-read int $connectorId + * @property-read array|null $settings + */ +class SourceItemResult extends AbstractItem +{ + #[\Override] + public function __get($offset): mixed + { + return match ($offset) { + 'id', 'createdById', 'updatedById', 'connectorId' => isset($this->data[$offset]) ? (int)$this->data[$offset] : null, + 'active' => isset($this->data[$offset]) ? (bool)$this->data[$offset] : null, + 'dateCreate', 'dateUpdate' => isset($this->data[$offset]) + ? CarbonImmutable::parse($this->data[$offset]) + : null, + default => $this->data[$offset] ?? null, + }; + } +} + diff --git a/src/Services/Biconnector/Source/Result/SourceResult.php b/src/Services/Biconnector/Source/Result/SourceResult.php new file mode 100644 index 00000000..b062e51f --- /dev/null +++ b/src/Services/Biconnector/Source/Result/SourceResult.php @@ -0,0 +1,56 @@ + + * + * For the full copyright and license information, please view the MIT-LICENSE.txt + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Bitrix24\SDK\Services\Biconnector\Source\Result; + +use Bitrix24\SDK\Core\Exceptions\BaseException; +use Bitrix24\SDK\Core\Result\AbstractResult; + +/** + * Class SourceResult + * + * Wraps the response from biconnector.source.get. + * + * The API returns: + * result.item.connection.{id, type, code, title, description, active, dateCreate, dateUpdate, createdById, updatedById} + * result.item.connectorId + * result.item.settings + * + * We flatten connection fields to the root level so SourceItemResult has a consistent flat structure. + */ +class SourceResult extends AbstractResult +{ + /** + * @throws BaseException + */ + public function source(): SourceItemResult + { + $result = $this->getCoreResponse()->getResponseData()->getResult(); + + if (!empty($result['item']) && is_array($result['item'])) { + $item = $result['item']; + + // Flatten nested 'connection' fields to root level + if (!empty($item['connection']) && is_array($item['connection'])) { + $connection = $item['connection']; + unset($item['connection']); + $item = array_merge($connection, $item); + } + + return new SourceItemResult($item); + } + + return new SourceItemResult($result); + } +} + diff --git a/src/Services/Biconnector/Source/Result/SourcesResult.php b/src/Services/Biconnector/Source/Result/SourcesResult.php new file mode 100644 index 00000000..40ebd733 --- /dev/null +++ b/src/Services/Biconnector/Source/Result/SourcesResult.php @@ -0,0 +1,46 @@ + + * + * For the full copyright and license information, please view the MIT-LICENSE.txt + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Bitrix24\SDK\Services\Biconnector\Source\Result; + +use Bitrix24\SDK\Core\Exceptions\BaseException; +use Bitrix24\SDK\Core\Result\AbstractResult; + +/** + * Class SourcesResult + * + * Wraps the response from biconnector.source.list. + * The API returns a flat array of source items. + */ +class SourcesResult extends AbstractResult +{ + /** + * @return SourceItemResult[] + * @throws BaseException + */ + public function getSources(): array + { + $items = []; + $result = $this->getCoreResponse()->getResponseData()->getResult(); + + if (array_is_list($result)) { + foreach ($result as $item) { + $items[] = new SourceItemResult($item); + } + } + + return $items; + } +} + + diff --git a/src/Services/Biconnector/Source/Result/UpdatedSourceBatchResult.php b/src/Services/Biconnector/Source/Result/UpdatedSourceBatchResult.php new file mode 100644 index 00000000..b7aaff2d --- /dev/null +++ b/src/Services/Biconnector/Source/Result/UpdatedSourceBatchResult.php @@ -0,0 +1,29 @@ + + * + * For the full copyright and license information, please view the MIT-LICENSE.txt + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Bitrix24\SDK\Services\Biconnector\Source\Result; + +use Bitrix24\SDK\Core\Result\UpdatedItemBatchResult; + +/** + * Class UpdatedSourceBatchResult + */ +class UpdatedSourceBatchResult extends UpdatedItemBatchResult +{ + #[\Override] + public function isSuccess(): bool + { + return (bool)$this->getResponseData()->getResult(); + } +} + diff --git a/src/Services/Biconnector/Source/Result/UpdatedSourceResult.php b/src/Services/Biconnector/Source/Result/UpdatedSourceResult.php new file mode 100644 index 00000000..6e8cbdfc --- /dev/null +++ b/src/Services/Biconnector/Source/Result/UpdatedSourceResult.php @@ -0,0 +1,33 @@ + + * + * For the full copyright and license information, please view the MIT-LICENSE.txt + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Bitrix24\SDK\Services\Biconnector\Source\Result; + +use Bitrix24\SDK\Core\Exceptions\BaseException; +use Bitrix24\SDK\Core\Result\UpdatedItemResult; + +/** + * Class UpdatedSourceResult + */ +class UpdatedSourceResult extends UpdatedItemResult +{ + /** + * @throws BaseException + */ + #[\Override] + public function isSuccess(): bool + { + return (bool)$this->getCoreResponse()->getResponseData()->getResult(); + } +} + diff --git a/src/Services/Biconnector/Source/Service/Batch.php b/src/Services/Biconnector/Source/Service/Batch.php new file mode 100644 index 00000000..a62d5bd8 --- /dev/null +++ b/src/Services/Biconnector/Source/Service/Batch.php @@ -0,0 +1,170 @@ + + * + * For the full copyright and license information, please view the MIT-LICENSE.txt + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Bitrix24\SDK\Services\Biconnector\Source\Service; + +use Bitrix24\SDK\Attributes\ApiBatchMethodMetadata; +use Bitrix24\SDK\Attributes\ApiBatchServiceMetadata; +use Bitrix24\SDK\Core\Contracts\BatchOperationsInterface; +use Bitrix24\SDK\Core\Credentials\Scope; +use Bitrix24\SDK\Core\Exceptions\BaseException; +use Bitrix24\SDK\Services\Biconnector\Source\Result\AddedSourceBatchResult; +use Bitrix24\SDK\Services\Biconnector\Source\Result\DeletedSourceBatchResult; +use Bitrix24\SDK\Services\Biconnector\Source\Result\SourceItemResult; +use Bitrix24\SDK\Services\Biconnector\Source\Result\UpdatedSourceBatchResult; +use Generator; +use Psr\Log\LoggerInterface; + +#[ApiBatchServiceMetadata(new Scope(['biconnector']))] +class Batch +{ + /** + * Batch constructor + */ + public function __construct(protected BatchOperationsInterface $batch, protected LoggerInterface $log) + { + } + + /** + * Batch list sources + * + * @link https://apidocs.bitrix24.com/api-reference/biconnector/source/biconnector-source-list.html + * + * @return Generator + * @throws BaseException + */ + #[ApiBatchMethodMetadata( + 'biconnector.source.list', + 'https://apidocs.bitrix24.com/api-reference/biconnector/source/biconnector-source-list.html', + 'Batch list sources' + )] + public function list( + array $order = [], + array $filter = [], + array $select = [], + ?int $limit = null + ): Generator { + $this->log->debug( + 'batchList', + [ + 'order' => $order, + 'filter' => $filter, + 'select' => $select, + 'limit' => $limit, + ] + ); + + foreach ( + $this->batch->getTraversableList( + 'biconnector.source.list', + $order, + $filter, + $select, + $limit + ) as $key => $value + ) { + yield $key => new SourceItemResult($value); + } + } + + /** + * Batch add sources + * + * @link https://apidocs.bitrix24.com/api-reference/biconnector/source/biconnector-source-add.html + * + * @param array $sources + * + * @return Generator + * @throws BaseException + */ + #[ApiBatchMethodMetadata( + 'biconnector.source.add', + 'https://apidocs.bitrix24.com/api-reference/biconnector/source/biconnector-source-add.html', + 'Batch add sources' + )] + public function add(array $sources): Generator + { + $items = []; + foreach ($sources as $item) { + $items[] = [ + 'fields' => $item, + ]; + } + + foreach ($this->batch->addEntityItems('biconnector.source.add', $items) as $key => $item) { + yield $key => new AddedSourceBatchResult($item); + } + } + + /** + * Batch update sources + * + * Update elements in array with structure: + * id => [ // Source id + * 'fields' => [] // Source fields to update + * ] + * + * @param array $entityItems + * + * @return Generator + * @throws BaseException + */ + #[ApiBatchMethodMetadata( + 'biconnector.source.update', + 'https://apidocs.bitrix24.com/api-reference/biconnector/source/biconnector-source-update.html', + 'Batch update sources' + )] + public function update(array $entityItems): Generator + { + foreach ( + $this->batch->updateEntityItems( + 'biconnector.source.update', + $entityItems + ) as $key => $item + ) { + yield $key => new UpdatedSourceBatchResult($item); + } + } + + /** + * Batch delete sources + * + * @param int[] $sourceIds + * + * @return Generator + * @throws BaseException + */ + #[ApiBatchMethodMetadata( + 'biconnector.source.delete', + 'https://apidocs.bitrix24.com/api-reference/biconnector/source/biconnector-source-delete.html', + 'Batch delete sources' + )] + public function delete(array $sourceIds): Generator + { + foreach ( + $this->batch->deleteEntityItems( + 'biconnector.source.delete', + $sourceIds + ) as $key => $item + ) { + yield $key => new DeletedSourceBatchResult($item); + } + } +} + diff --git a/src/Services/Biconnector/Source/Service/Source.php b/src/Services/Biconnector/Source/Service/Source.php new file mode 100644 index 00000000..8e975d6e --- /dev/null +++ b/src/Services/Biconnector/Source/Service/Source.php @@ -0,0 +1,223 @@ + + * + * For the full copyright and license information, please view the MIT-LICENSE.txt + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Bitrix24\SDK\Services\Biconnector\Source\Service; + +use Bitrix24\SDK\Attributes\ApiEndpointMetadata; +use Bitrix24\SDK\Attributes\ApiServiceMetadata; +use Bitrix24\SDK\Core\Contracts\CoreInterface; +use Bitrix24\SDK\Core\Credentials\Scope; +use Bitrix24\SDK\Core\Exceptions\BaseException; +use Bitrix24\SDK\Core\Exceptions\TransportException; +use Bitrix24\SDK\Core\Result\FieldsResult; +use Bitrix24\SDK\Services\AbstractService; +use Bitrix24\SDK\Services\Biconnector\Source\Result\AddedSourceResult; +use Bitrix24\SDK\Services\Biconnector\Source\Result\DeletedSourceResult; +use Bitrix24\SDK\Services\Biconnector\Source\Result\SourceResult; +use Bitrix24\SDK\Services\Biconnector\Source\Result\SourcesResult; +use Bitrix24\SDK\Services\Biconnector\Source\Result\UpdatedSourceResult; +use Psr\Log\LoggerInterface; + +#[ApiServiceMetadata(new Scope(['biconnector']))] +class Source extends AbstractService +{ + /** + * Source constructor + */ + public function __construct(public Batch $batch, CoreInterface $core, LoggerInterface $logger) + { + parent::__construct($core, $logger); + } + + /** + * Add a new data source + * + * @link https://apidocs.bitrix24.com/api-reference/biconnector/source/biconnector-source-add.html + * + * @param array{ + * title: string, + * connectorId: int, + * description?: string, + * active?: bool, + * settings?: array, + * } $fields + * + * @throws BaseException + * @throws TransportException + */ + #[ApiEndpointMetadata( + 'biconnector.source.add', + 'https://apidocs.bitrix24.com/api-reference/biconnector/source/biconnector-source-add.html', + 'Add a new data source' + )] + public function add(array $fields): AddedSourceResult + { + return new AddedSourceResult( + $this->core->call( + 'biconnector.source.add', + [ + 'fields' => $fields, + ] + ) + ); + } + + /** + * Update an existing data source + * + * @link https://apidocs.bitrix24.com/api-reference/biconnector/source/biconnector-source-update.html + * + * @param array{ + * title?: string, + * description?: string, + * active?: bool, + * settings?: array, + * } $fields + * + * @throws BaseException + * @throws TransportException + */ + #[ApiEndpointMetadata( + 'biconnector.source.update', + 'https://apidocs.bitrix24.com/api-reference/biconnector/source/biconnector-source-update.html', + 'Update an existing data source' + )] + public function update(int $id, array $fields): UpdatedSourceResult + { + return new UpdatedSourceResult( + $this->core->call( + 'biconnector.source.update', + [ + 'id' => $id, + 'fields' => $fields, + ] + ) + ); + } + + /** + * Get a data source by its ID + * + * @link https://apidocs.bitrix24.com/api-reference/biconnector/source/biconnector-source-get.html + * + * @throws BaseException + * @throws TransportException + */ + #[ApiEndpointMetadata( + 'biconnector.source.get', + 'https://apidocs.bitrix24.com/api-reference/biconnector/source/biconnector-source-get.html', + 'Get a data source by its ID' + )] + public function get(int $id): SourceResult + { + return new SourceResult( + $this->core->call( + 'biconnector.source.get', + [ + 'id' => $id, + ] + ) + ); + } + + /** + * Get a list of data sources + * + * @link https://apidocs.bitrix24.com/api-reference/biconnector/source/biconnector-source-list.html + * + * @param array $order - sort fields, e.g. ['id' => 'ASC'] + * @param array $filter - filter fields + * @param array $select - fields to include in the result + * + * @throws BaseException + * @throws TransportException + */ + #[ApiEndpointMetadata( + 'biconnector.source.list', + 'https://apidocs.bitrix24.com/api-reference/biconnector/source/biconnector-source-list.html', + 'Get a list of data sources' + )] + public function list(array $order = [], array $filter = [], array $select = []): SourcesResult + { + return new SourcesResult( + $this->core->call( + 'biconnector.source.list', + [ + 'order' => $order, + 'filter' => $filter, + 'select' => $select, + ] + ) + ); + } + + /** + * Delete a data source by its ID + * + * @link https://apidocs.bitrix24.com/api-reference/biconnector/source/biconnector-source-delete.html + * + * @throws BaseException + * @throws TransportException + */ + #[ApiEndpointMetadata( + 'biconnector.source.delete', + 'https://apidocs.bitrix24.com/api-reference/biconnector/source/biconnector-source-delete.html', + 'Delete a data source by its ID' + )] + public function delete(int $id): DeletedSourceResult + { + return new DeletedSourceResult( + $this->core->call( + 'biconnector.source.delete', + [ + 'id' => $id, + ] + ) + ); + } + + /** + * Get the fields description for data sources + * + * @link https://apidocs.bitrix24.com/api-reference/biconnector/source/biconnector-source-fields.html + * + * @throws BaseException + * @throws TransportException + */ + #[ApiEndpointMetadata( + 'biconnector.source.fields', + 'https://apidocs.bitrix24.com/api-reference/biconnector/source/biconnector-source-fields.html', + 'Get the fields description for data sources' + )] + public function fields(): FieldsResult + { + return new FieldsResult($this->core->call('biconnector.source.fields')); + } + + /** + * Count data sources + * + * @throws BaseException + * @throws TransportException + */ + public function count(): int + { + $count = 0; + foreach ($this->batch->list() as $item) { + $count++; + } + + return $count; + } +} + diff --git a/tests/Integration/Services/Biconnector/Source/Result/SourceItemResultAnnotationsTest.php b/tests/Integration/Services/Biconnector/Source/Result/SourceItemResultAnnotationsTest.php new file mode 100644 index 00000000..fe9bc594 --- /dev/null +++ b/tests/Integration/Services/Biconnector/Source/Result/SourceItemResultAnnotationsTest.php @@ -0,0 +1,167 @@ + + * + * For the full copyright and license information, please view the MIT-LICENSE.txt + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Bitrix24\SDK\Tests\Integration\Services\Biconnector\Source\Result; + +use Bitrix24\SDK\Core\Exceptions\BaseException; +use Bitrix24\SDK\Core\Exceptions\InvalidArgumentException; +use Bitrix24\SDK\Core\Exceptions\TransportException; +use Bitrix24\SDK\Services\Biconnector\Connector\Service\Connector; +use Bitrix24\SDK\Services\Biconnector\Source\Result\SourceItemResult; +use Bitrix24\SDK\Services\Biconnector\Source\Service\Source; +use Bitrix24\SDK\Tests\CustomAssertions\CustomBitrix24Assertions; +use Bitrix24\SDK\Tests\Integration\Factory; +use Faker\Generator; +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\Test; +use PHPUnit\Framework\Attributes\TestDox; +use PHPUnit\Framework\TestCase; +use Faker; + +#[CoversClass(SourceItemResult::class)] +class SourceItemResultAnnotationsTest extends TestCase +{ + use CustomBitrix24Assertions; + + private Source $sourceService; + + private Connector $connectorService; + + private Generator $faker; + + private int $connectorId; + + private int $sourceId; + + /** + * @throws InvalidArgumentException + * @throws BaseException + * @throws TransportException + */ + #[\Override] + protected function setUp(): void + { + $builder = Factory::getServiceBuilder(true)->getBiconnectorScope(); + $this->sourceService = $builder->source(); + $this->connectorService = $builder->connector(); + $this->faker = Faker\Factory::create(); + + // Create a connector, then a source for annotation tests + $this->connectorId = $this->connectorService->add($this->makeConnectorFields( + 'connector-annotations-' . $this->faker->uuid() + ))->getId(); + + $this->sourceId = $this->sourceService->add([ + 'title' => 'source-annotations-' . $this->faker->uuid(), + 'connectorId' => $this->connectorId, + 'settings' => ['token' => 'test-token-' . $this->faker->uuid()], + ])->getId(); + } + + /** + * @throws BaseException + * @throws TransportException + */ + #[\Override] + protected function tearDown(): void + { + try { + $this->sourceService->delete($this->sourceId); + } catch (\Throwable) { + } + + try { + $this->connectorService->delete($this->connectorId); + } catch (\Throwable) { + } + } + + private function makeConnectorFields(string $title): array + { + return [ + 'title' => $title, + 'logo' => 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjIiIGhlaWdodD0iMjIiIHZpZXdCb3g9IjAgMCAyMiAyMiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KCTxjaXJjbGUgY3g9IjExIiBjeT0iMTEiIHI9IjEwIiBmaWxsPSIjRkYzQjNCIiAvPgoJPHRleHQgeD0iMTEiIHk9IjEzIiBmb250LWZhbWlseT0iQXJpYWwsIHNhbnMtc2VyaWYiIGZvbnQtc2l6ZT0iNiIgZmlsbD0iI0ZGRkZGRiIgdGV4dC1hbmNob3I9Im1pZGRsZSIgZm9udC13ZWlnaHQ9ImJvbGQiPlJFU1Q8L3RleHQ+Cjwvc3ZnPg==', + 'urlCheck' => 'http://example.com/api/check', + 'urlTableList' => 'http://example.com/api/table_list', + 'urlTableDescription' => 'http://example.com/api/table_description', + 'urlData' => 'http://example.com/api/data', + 'settings' => [ + ['name' => 'Token', 'type' => 'STRING', 'code' => 'token'], + ], + ]; + } + + #[Test] + #[TestDox('all fields in SourceItemResult are annotated and match live API fields schema')] + public function testAllSystemFieldsAnnotated(): void + { + $fieldCodes = $this->getSourceFieldCodes(); + + $this->assertBitrix24AllResultItemFieldsAnnotated( + $fieldCodes, + SourceItemResult::class + ); + } + + #[Test] + #[TestDox('all fields in SourceItemResult have valid type casting matching API fields schema')] + public function testAllSystemFieldsHasValidTypeAnnotation(): void + { + $fieldTypesMap = $this->getSourceFieldTypesMap(); + + $this->assertBitrix24AllResultItemFieldsHasValidTypeAnnotation( + $fieldTypesMap, + SourceItemResult::class + ); + } + + /** + * Returns list of field codes from biconnector.source.fields API. + * + * @return array + */ + private function getSourceFieldCodes(): array + { + $raw = $this->sourceService->fields()->getFieldsDescription(); + $fields = $raw['fields'] ?? []; + + return array_column($fields, 'title'); + } + + /** + * Returns field type map compatible with assertBitrix24AllResultItemFieldsHasValidTypeAnnotation. + * Normalises biconnector-specific types to types known by the shared assertion. + * + * @return array + */ + private function getSourceFieldTypesMap(): array + { + $raw = $this->sourceService->fields()->getFieldsDescription(); + $fields = $raw['fields'] ?? []; + + $result = []; + foreach ($fields as $field) { + $apiType = $field['type']; + + // biconnector uses 'boolean' — map to 'char' which the shared assertion handles as bool + if ($apiType === 'boolean') { + $apiType = 'char'; + } + + $result[$field['title']] = ['type' => $apiType]; + } + + return $result; + } +} + diff --git a/tests/Integration/Services/Biconnector/Source/Service/BatchTest.php b/tests/Integration/Services/Biconnector/Source/Service/BatchTest.php new file mode 100644 index 00000000..4f909c45 --- /dev/null +++ b/tests/Integration/Services/Biconnector/Source/Service/BatchTest.php @@ -0,0 +1,167 @@ + + * + * For the full copyright and license information, please view the MIT-LICENSE.txt + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Bitrix24\SDK\Tests\Integration\Services\Biconnector\Source\Service; + +use Bitrix24\SDK\Core\Exceptions\BaseException; +use Bitrix24\SDK\Core\Exceptions\InvalidArgumentException; +use Bitrix24\SDK\Core\Exceptions\TransportException; +use Bitrix24\SDK\Services\Biconnector\Connector\Service\Connector; +use Bitrix24\SDK\Services\Biconnector\Source\Result\SourceItemResult; +use Bitrix24\SDK\Services\Biconnector\Source\Service\Batch; +use Bitrix24\SDK\Services\Biconnector\Source\Service\Source; +use Bitrix24\SDK\Tests\Integration\Factory; +use Faker\Generator; +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\TestCase; +use Faker; + +#[CoversClass(Batch::class)] +class BatchTest extends TestCase +{ + private Source $sourceService; + + private Connector $connectorService; + + private Generator $faker; + + private int $connectorId; + + /** + * @throws InvalidArgumentException + * @throws BaseException + * @throws TransportException + */ + #[\Override] + protected function setUp(): void + { + $builder = Factory::getServiceBuilder(true)->getBiconnectorScope(); + $this->sourceService = $builder->source(); + $this->connectorService = $builder->connector(); + $this->faker = Faker\Factory::create(); + + // Create a connector to use for source tests + $this->connectorId = $this->connectorService->add($this->makeConnectorFields( + 'connector-for-source-batch-' . $this->faker->uuid() + ))->getId(); + } + + /** + * @throws BaseException + * @throws TransportException + */ + #[\Override] + protected function tearDown(): void + { + try { + $this->connectorService->delete($this->connectorId); + } catch (\Throwable) { + // Ignore cleanup errors + } + } + + private function makeConnectorFields(string $title): array + { + return [ + 'title' => $title, + 'logo' => 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjIiIGhlaWdodD0iMjIiIHZpZXdCb3g9IjAgMCAyMiAyMiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KCTxjaXJjbGUgY3g9IjExIiBjeT0iMTEiIHI9IjEwIiBmaWxsPSIjRkYzQjNCIiAvPgoJPHRleHQgeD0iMTEiIHk9IjEzIiBmb250LWZhbWlseT0iQXJpYWwsIHNhbnMtc2VyaWYiIGZvbnQtc2l6ZT0iNiIgZmlsbD0iI0ZGRkZGRiIgdGV4dC1hbmNob3I9Im1pZGRsZSIgZm9udC13ZWlnaHQ9ImJvbGQiPlJFU1Q8L3RleHQ+Cjwvc3ZnPg==', + 'urlCheck' => 'http://example.com/api/check', + 'urlTableList' => 'http://example.com/api/table_list', + 'urlTableDescription' => 'http://example.com/api/table_description', + 'urlData' => 'http://example.com/api/data', + 'settings' => [ + [ + 'name' => 'Token', + 'type' => 'STRING', + 'code' => 'token', + ], + ], + ]; + } + + private function makeSourceFields(string $title): array + { + return [ + 'title' => $title, + 'connectorId' => $this->connectorId, + 'settings' => [ + 'token' => 'test-token-' . $this->faker->uuid(), + ], + ]; + } + + /** + * @throws BaseException + * @throws TransportException + */ + public function testBatchList(): void + { + $title = 'source-' . $this->faker->uuid(); + $id = $this->sourceService->add($this->makeSourceFields($title))->getId(); + + $count = 0; + foreach ($this->sourceService->batch->list([], [], [], 10) as $item) { + self::assertInstanceOf(SourceItemResult::class, $item); + $count++; + } + + self::assertGreaterThanOrEqual(1, $count); + + // Cleanup + $this->sourceService->delete($id); + } + + /** + * @throws BaseException + * @throws TransportException + */ + public function testBatchAdd(): void + { + $sources = []; + for ($i = 0; $i < 3; $i++) { + $sources[] = $this->makeSourceFields('source-batch-' . $this->faker->uuid()); + } + + $addedIds = []; + foreach ($this->sourceService->batch->add($sources) as $result) { + $addedIds[] = $result->getId(); + self::assertGreaterThanOrEqual(1, $result->getId()); + } + + self::assertCount(3, $addedIds); + + // Cleanup + foreach ($this->sourceService->batch->delete($addedIds) as $deleteResult) { + self::assertTrue($deleteResult->isSuccess()); + } + } + + /** + * @throws BaseException + * @throws TransportException + */ + public function testBatchDelete(): void + { + $ids = []; + for ($i = 0; $i < 2; $i++) { + $ids[] = $this->sourceService->add( + $this->makeSourceFields('source-del-batch-' . $this->faker->uuid()) + )->getId(); + } + + foreach ($this->sourceService->batch->delete($ids) as $result) { + self::assertTrue($result->isSuccess()); + } + } +} + diff --git a/tests/Integration/Services/Biconnector/Source/Service/SourceTest.php b/tests/Integration/Services/Biconnector/Source/Service/SourceTest.php new file mode 100644 index 00000000..0b2cfaf0 --- /dev/null +++ b/tests/Integration/Services/Biconnector/Source/Service/SourceTest.php @@ -0,0 +1,225 @@ + + * + * For the full copyright and license information, please view the MIT-LICENSE.txt + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Bitrix24\SDK\Tests\Integration\Services\Biconnector\Source\Service; + +use Bitrix24\SDK\Core\Exceptions\BaseException; +use Bitrix24\SDK\Core\Exceptions\InvalidArgumentException; +use Bitrix24\SDK\Core\Exceptions\TransportException; +use Bitrix24\SDK\Services\Biconnector\Connector\Service\Connector; +use Bitrix24\SDK\Services\Biconnector\Source\Result\SourceItemResult; +use Bitrix24\SDK\Services\Biconnector\Source\Service\Source; +use Bitrix24\SDK\Tests\CustomAssertions\CustomBitrix24Assertions; +use Bitrix24\SDK\Tests\Integration\Factory; +use Faker\Generator; +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\TestCase; +use Faker; + +#[CoversClass(Source::class)] +class SourceTest extends TestCase +{ + use CustomBitrix24Assertions; + + private Source $sourceService; + + private Connector $connectorService; + + private Generator $faker; + + private int $connectorId; + + /** + * @throws InvalidArgumentException + * @throws BaseException + * @throws TransportException + */ + #[\Override] + protected function setUp(): void + { + $builder = Factory::getServiceBuilder(true)->getBiconnectorScope(); + $this->sourceService = $builder->source(); + $this->connectorService = $builder->connector(); + $this->faker = Faker\Factory::create(); + + // Create a connector to use for source tests + $this->connectorId = $this->connectorService->add($this->makeConnectorFields( + 'connector-for-source-' . $this->faker->uuid() + ))->getId(); + } + + /** + * @throws BaseException + * @throws TransportException + */ + #[\Override] + protected function tearDown(): void + { + // Clean up the connector created for tests + try { + $this->connectorService->delete($this->connectorId); + } catch (\Throwable) { + // Ignore cleanup errors + } + } + + /** + * Returns the minimum set of required fields to create a connector. + */ + private function makeConnectorFields(string $title): array + { + return [ + 'title' => $title, + 'logo' => 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjIiIGhlaWdodD0iMjIiIHZpZXdCb3g9IjAgMCAyMiAyMiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KCTxjaXJjbGUgY3g9IjExIiBjeT0iMTEiIHI9IjEwIiBmaWxsPSIjRkYzQjNCIiAvPgoJPHRleHQgeD0iMTEiIHk9IjEzIiBmb250LWZhbWlseT0iQXJpYWwsIHNhbnMtc2VyaWYiIGZvbnQtc2l6ZT0iNiIgZmlsbD0iI0ZGRkZGRiIgdGV4dC1hbmNob3I9Im1pZGRsZSIgZm9udC13ZWlnaHQ9ImJvbGQiPlJFU1Q8L3RleHQ+Cjwvc3ZnPg==', + 'urlCheck' => 'http://example.com/api/check', + 'urlTableList' => 'http://example.com/api/table_list', + 'urlTableDescription' => 'http://example.com/api/table_description', + 'urlData' => 'http://example.com/api/data', + 'settings' => [ + [ + 'name' => 'Token', + 'type' => 'STRING', + 'code' => 'token', + ], + ], + ]; + } + + /** + * Returns the minimum set of required fields to create a source. + */ + private function makeSourceFields(string $title): array + { + return [ + 'title' => $title, + 'connectorId' => $this->connectorId, + 'settings' => [ + 'token' => 'test-token-' . $this->faker->uuid(), + ], + ]; + } + + /** + * @throws BaseException + * @throws TransportException + */ + public function testAdd(): void + { + $title = 'source-' . $this->faker->uuid(); + $id = $this->sourceService->add($this->makeSourceFields($title))->getId(); + + self::assertGreaterThanOrEqual(1, $id); + + // Cleanup + $this->sourceService->delete($id); + } + + /** + * @throws BaseException + * @throws TransportException + */ + public function testGet(): void + { + $title = 'source-' . $this->faker->uuid(); + $id = $this->sourceService->add($this->makeSourceFields($title))->getId(); + + $sourceItemResult = $this->sourceService->get($id)->source(); + self::assertInstanceOf(SourceItemResult::class, $sourceItemResult); + self::assertEquals($id, $sourceItemResult->id); + self::assertEquals($title, $sourceItemResult->title); + + // Cleanup + $this->sourceService->delete($id); + } + + /** + * @throws BaseException + * @throws TransportException + */ + public function testList(): void + { + $title = 'source-' . $this->faker->uuid(); + $id = $this->sourceService->add($this->makeSourceFields($title))->getId(); + + $list = $this->sourceService->list()->getSources(); + self::assertIsArray($list); + self::assertGreaterThanOrEqual(1, count($list)); + + // Cleanup + $this->sourceService->delete($id); + } + + /** + * @throws BaseException + * @throws TransportException + */ + public function testUpdate(): void + { + $title = 'source-' . $this->faker->uuid(); + $id = $this->sourceService->add($this->makeSourceFields($title))->getId(); + + $newTitle = $title . '-updated'; + self::assertTrue( + $this->sourceService->update($id, [ + 'title' => $newTitle, + ])->isSuccess() + ); + + self::assertEquals($newTitle, $this->sourceService->get($id)->source()->title); + + // Cleanup + $this->sourceService->delete($id); + } + + /** + * @throws BaseException + * @throws TransportException + */ + public function testDelete(): void + { + $title = 'source-' . $this->faker->uuid(); + $id = $this->sourceService->add($this->makeSourceFields($title))->getId(); + + self::assertTrue($this->sourceService->delete($id)->isSuccess()); + } + + /** + * @throws BaseException + * @throws TransportException + */ + public function testFields(): void + { + $fields = $this->sourceService->fields()->getFieldsDescription(); + self::assertIsArray($fields); + self::assertNotEmpty($fields); + } + + /** + * @throws BaseException + * @throws TransportException + */ + public function testCount(): void + { + $countBefore = $this->sourceService->count(); + + $title = 'source-' . $this->faker->uuid(); + $id = $this->sourceService->add($this->makeSourceFields($title))->getId(); + + $countAfter = $this->sourceService->count(); + self::assertEquals($countBefore + 1, $countAfter); + + // Cleanup + $this->sourceService->delete($id); + } +} + diff --git a/tests/Unit/Services/Biconnector/BiconnectorServiceBuilderTest.php b/tests/Unit/Services/Biconnector/BiconnectorServiceBuilderTest.php index 783b2cb2..b0884ef0 100644 --- a/tests/Unit/Services/Biconnector/BiconnectorServiceBuilderTest.php +++ b/tests/Unit/Services/Biconnector/BiconnectorServiceBuilderTest.php @@ -41,4 +41,9 @@ public function testConnectorServiceIsCached(): void { $this::assertSame($this->serviceBuilder->connector(), $this->serviceBuilder->connector()); } + + public function testSourceServiceIsCached(): void + { + $this::assertSame($this->serviceBuilder->source(), $this->serviceBuilder->source()); + } } From 64cbcc1551360d5eaac032bbefd996503548788e Mon Sep 17 00:00:00 2001 From: Dmitriy Ignatenko Date: Thu, 14 May 2026 20:50:15 +0400 Subject: [PATCH 08/18] Add the page parameter to the list method, and minor fix --- .tasks/469/plan.md | 2 +- src/Services/Biconnector/Source/Batch.php | 159 +++++++++++++++++- .../Source/Result/AddedSourceBatchResult.php | 1 - .../Source/Result/AddedSourceResult.php | 1 - .../Result/DeletedSourceBatchResult.php | 1 - .../Source/Result/DeletedSourceResult.php | 1 - .../Source/Result/SourceItemResult.php | 1 - .../Source/Result/SourceResult.php | 1 - .../Source/Result/SourcesResult.php | 2 - .../Result/UpdatedSourceBatchResult.php | 1 - .../Source/Result/UpdatedSourceResult.php | 1 - .../Biconnector/Source/Service/Batch.php | 1 - .../Biconnector/Source/Service/Source.php | 5 +- .../SourceItemResultAnnotationsTest.php | 1 - .../Biconnector/Source/Service/BatchTest.php | 1 - .../Biconnector/Source/Service/SourceTest.php | 1 - 16 files changed, 162 insertions(+), 18 deletions(-) diff --git a/.tasks/469/plan.md b/.tasks/469/plan.md index 601b3883..dbf07cc4 100644 --- a/.tasks/469/plan.md +++ b/.tasks/469/plan.md @@ -10,7 +10,7 @@ Now we add the `Source` entity, covering six REST methods: | `biconnector.source.add` | `result.id` (int) | Fields wrapped in `fields` | | `biconnector.source.update` | `result` = `true` | | | `biconnector.source.get` | `result.item.connection` + root connectorId/settings | Nested; must be flattened | -| `biconnector.source.list` | `result` = flat array | Standard list method | +| `biconnector.source.list` | `result` = flat array | Uses 'page' pagination (same as connector.list) | | `biconnector.source.delete` | `result` = `true` | | | `biconnector.source.fields` | `result.fields` array | Returns `FieldsResult` | diff --git a/src/Services/Biconnector/Source/Batch.php b/src/Services/Biconnector/Source/Batch.php index 280772f9..9cddffd0 100644 --- a/src/Services/Biconnector/Source/Batch.php +++ b/src/Services/Biconnector/Source/Batch.php @@ -15,6 +15,7 @@ use Bitrix24\SDK\Core\Exceptions\BaseException; use Bitrix24\SDK\Core\Exceptions\InvalidArgumentException; +use Bitrix24\SDK\Core\Exceptions\TransportException; use Bitrix24\SDK\Core\Response\DTO\ResponseData; use Generator; @@ -22,8 +23,10 @@ * Class Batch * * Overrides base Batch to handle parameter naming differences in biconnector.source.* REST methods: + * - list uses 'page' (page number, 50 records per page) instead of 'start' (offset) for pagination * - delete uses lowercase 'id' instead of 'ID' * + * @see https://apidocs.bitrix24.com/api-reference/biconnector/source/biconnector-source-list.html * @see https://apidocs.bitrix24.com/api-reference/biconnector/source/biconnector-source-delete.html */ class Batch extends \Bitrix24\SDK\Core\Batch @@ -104,5 +107,159 @@ public function deleteEntityItems( $this->logger->debug('deleteEntityItems.finish'); } -} + /** + * Get traversable list using page-based pagination. + * + * The biconnector.source.list method uses 'page' parameter (page number, 50 records per page) + * instead of the standard 'start' (offset) parameter used by most other REST methods. + * + * @link https://apidocs.bitrix24.com/api-reference/biconnector/source/biconnector-source-list.html + * + * @param array $order + * @param array $filter + * @param array $select + * + * @return Generator + * @throws BaseException + * @throws TransportException + */ + #[\Override] + public function getTraversableList( + string $apiMethod, + ?array $order = [], + ?array $filter = [], + ?array $select = [], + ?int $limit = null, + ?array $additionalParameters = null + ): Generator { + yield from $this->getTraversableListWithCount( + $apiMethod, + $order ?? [], + $filter ?? [], + $select ?? [], + $limit, + $additionalParameters + ); + } + + /** + * Get traversable list using page-based pagination (page number, 50 records per page). + * + * The biconnector.source.list method accepts 'page' parameter instead of 'start'. + * Page 1 returns items 1–50, page 2 returns items 51–100, etc. + * + * @link https://apidocs.bitrix24.com/api-reference/biconnector/source/biconnector-source-list.html + * + * @param array $order + * @param array $filter + * @param array $select + * + * @return Generator + * @throws BaseException + * @throws TransportException + */ + #[\Override] + public function getTraversableListWithCount( + string $apiMethod, + array $order, + array $filter, + array $select, + ?int $limit = null, + ?array $additionalParameters = null + ): Generator { + $this->logger->debug( + 'getTraversableListWithCount.start', + [ + 'apiMethod' => $apiMethod, + 'order' => $order, + 'filter' => $filter, + 'select' => $select, + 'limit' => $limit, + 'additionalParameters' => $additionalParameters, + ] + ); + + $this->clearCommands(); + + // Fetch first page to determine total count + $params = [ + 'order' => $order, + 'filter' => $filter, + 'select' => $select, + 'page' => 1, + ]; + + if ($additionalParameters !== null) { + $params = array_merge($params, $additionalParameters); + } + + $response = $this->core->call($apiMethod, $params); + $total = $response->getResponseData()->getPagination()->getTotal(); + + $this->logger->debug( + 'getTraversableListWithCount.totalElementsCount', + [ + 'totalElementsCount' => $total, + ] + ); + + if ($total <= self::MAX_ELEMENTS_IN_PAGE) { + $elementsCounter = 0; + foreach ($response->getResponseData()->getResult() as $item) { + $elementsCounter++; + if ($limit !== null && $elementsCounter > $limit) { + return; + } + + yield $item; + } + + return; + } + + // Register batch commands for all pages + $totalPages = (int)ceil($total / self::MAX_ELEMENTS_IN_PAGE); + for ($page = 1; $page <= $totalPages; $page++) { + $pageParams = [ + 'order' => $order, + 'filter' => $filter, + 'select' => $select, + 'page' => $page, + ]; + + if ($additionalParameters !== null) { + $pageParams = array_merge($pageParams, $additionalParameters); + } + + $this->registerCommand($apiMethod, $pageParams); + + if ($limit !== null && $limit < $page * self::MAX_ELEMENTS_IN_PAGE) { + break; + } + } + + $this->logger->debug( + 'getTraversableListWithCount.commandsRegistered', + [ + 'commandsCount' => $this->commands->count(), + 'totalItemsToSelect' => $total, + ] + ); + + $elementsCounter = 0; + foreach ($this->getTraversable(true) as $queryResultData) { + $resultElements = $this->extractElementsFromBatchResult($queryResultData, false); + foreach ($resultElements as $resultElement) { + ++$elementsCounter; + if ($limit !== null && $elementsCounter > $limit) { + return; + } + + yield $resultElement; + } + } + + $this->logger->debug('getTraversableListWithCount.finish'); + } +} diff --git a/src/Services/Biconnector/Source/Result/AddedSourceBatchResult.php b/src/Services/Biconnector/Source/Result/AddedSourceBatchResult.php index 626d80f5..f9107407 100644 --- a/src/Services/Biconnector/Source/Result/AddedSourceBatchResult.php +++ b/src/Services/Biconnector/Source/Result/AddedSourceBatchResult.php @@ -32,4 +32,3 @@ public function getId(): int return (int)$result; } } - diff --git a/src/Services/Biconnector/Source/Result/AddedSourceResult.php b/src/Services/Biconnector/Source/Result/AddedSourceResult.php index a47a806d..a4c73f89 100644 --- a/src/Services/Biconnector/Source/Result/AddedSourceResult.php +++ b/src/Services/Biconnector/Source/Result/AddedSourceResult.php @@ -39,4 +39,3 @@ public function getId(): int return (int)$result; } } - diff --git a/src/Services/Biconnector/Source/Result/DeletedSourceBatchResult.php b/src/Services/Biconnector/Source/Result/DeletedSourceBatchResult.php index 154729eb..f3e927f9 100644 --- a/src/Services/Biconnector/Source/Result/DeletedSourceBatchResult.php +++ b/src/Services/Biconnector/Source/Result/DeletedSourceBatchResult.php @@ -26,4 +26,3 @@ public function isSuccess(): bool return (bool)$this->getResponseData()->getResult(); } } - diff --git a/src/Services/Biconnector/Source/Result/DeletedSourceResult.php b/src/Services/Biconnector/Source/Result/DeletedSourceResult.php index 0b2c24c5..3808c934 100644 --- a/src/Services/Biconnector/Source/Result/DeletedSourceResult.php +++ b/src/Services/Biconnector/Source/Result/DeletedSourceResult.php @@ -30,4 +30,3 @@ public function isSuccess(): bool return (bool)$this->getCoreResponse()->getResponseData()->getResult(); } } - diff --git a/src/Services/Biconnector/Source/Result/SourceItemResult.php b/src/Services/Biconnector/Source/Result/SourceItemResult.php index bddfad96..2cde005d 100644 --- a/src/Services/Biconnector/Source/Result/SourceItemResult.php +++ b/src/Services/Biconnector/Source/Result/SourceItemResult.php @@ -51,4 +51,3 @@ public function __get($offset): mixed }; } } - diff --git a/src/Services/Biconnector/Source/Result/SourceResult.php b/src/Services/Biconnector/Source/Result/SourceResult.php index b062e51f..3f8534fa 100644 --- a/src/Services/Biconnector/Source/Result/SourceResult.php +++ b/src/Services/Biconnector/Source/Result/SourceResult.php @@ -53,4 +53,3 @@ public function source(): SourceItemResult return new SourceItemResult($result); } } - diff --git a/src/Services/Biconnector/Source/Result/SourcesResult.php b/src/Services/Biconnector/Source/Result/SourcesResult.php index 40ebd733..eddc14b3 100644 --- a/src/Services/Biconnector/Source/Result/SourcesResult.php +++ b/src/Services/Biconnector/Source/Result/SourcesResult.php @@ -42,5 +42,3 @@ public function getSources(): array return $items; } } - - diff --git a/src/Services/Biconnector/Source/Result/UpdatedSourceBatchResult.php b/src/Services/Biconnector/Source/Result/UpdatedSourceBatchResult.php index b7aaff2d..99525485 100644 --- a/src/Services/Biconnector/Source/Result/UpdatedSourceBatchResult.php +++ b/src/Services/Biconnector/Source/Result/UpdatedSourceBatchResult.php @@ -26,4 +26,3 @@ public function isSuccess(): bool return (bool)$this->getResponseData()->getResult(); } } - diff --git a/src/Services/Biconnector/Source/Result/UpdatedSourceResult.php b/src/Services/Biconnector/Source/Result/UpdatedSourceResult.php index 6e8cbdfc..aa01634f 100644 --- a/src/Services/Biconnector/Source/Result/UpdatedSourceResult.php +++ b/src/Services/Biconnector/Source/Result/UpdatedSourceResult.php @@ -30,4 +30,3 @@ public function isSuccess(): bool return (bool)$this->getCoreResponse()->getResponseData()->getResult(); } } - diff --git a/src/Services/Biconnector/Source/Service/Batch.php b/src/Services/Biconnector/Source/Service/Batch.php index a62d5bd8..5a988272 100644 --- a/src/Services/Biconnector/Source/Service/Batch.php +++ b/src/Services/Biconnector/Source/Service/Batch.php @@ -167,4 +167,3 @@ public function delete(array $sourceIds): Generator } } } - diff --git a/src/Services/Biconnector/Source/Service/Source.php b/src/Services/Biconnector/Source/Service/Source.php index 8e975d6e..3c4d7e4c 100644 --- a/src/Services/Biconnector/Source/Service/Source.php +++ b/src/Services/Biconnector/Source/Service/Source.php @@ -138,6 +138,7 @@ public function get(int $id): SourceResult * @param array $order - sort fields, e.g. ['id' => 'ASC'] * @param array $filter - filter fields * @param array $select - fields to include in the result + * @param int $page - page number for pagination (page size is 50 records per page) * * @throws BaseException * @throws TransportException @@ -147,7 +148,7 @@ public function get(int $id): SourceResult 'https://apidocs.bitrix24.com/api-reference/biconnector/source/biconnector-source-list.html', 'Get a list of data sources' )] - public function list(array $order = [], array $filter = [], array $select = []): SourcesResult + public function list(array $order = [], array $filter = [], array $select = [], int $page = 1): SourcesResult { return new SourcesResult( $this->core->call( @@ -156,6 +157,7 @@ public function list(array $order = [], array $filter = [], array $select = []): 'order' => $order, 'filter' => $filter, 'select' => $select, + 'page' => $page, ] ) ); @@ -220,4 +222,3 @@ public function count(): int return $count; } } - diff --git a/tests/Integration/Services/Biconnector/Source/Result/SourceItemResultAnnotationsTest.php b/tests/Integration/Services/Biconnector/Source/Result/SourceItemResultAnnotationsTest.php index fe9bc594..d09fc9bb 100644 --- a/tests/Integration/Services/Biconnector/Source/Result/SourceItemResultAnnotationsTest.php +++ b/tests/Integration/Services/Biconnector/Source/Result/SourceItemResultAnnotationsTest.php @@ -164,4 +164,3 @@ private function getSourceFieldTypesMap(): array return $result; } } - diff --git a/tests/Integration/Services/Biconnector/Source/Service/BatchTest.php b/tests/Integration/Services/Biconnector/Source/Service/BatchTest.php index 4f909c45..099c42a3 100644 --- a/tests/Integration/Services/Biconnector/Source/Service/BatchTest.php +++ b/tests/Integration/Services/Biconnector/Source/Service/BatchTest.php @@ -164,4 +164,3 @@ public function testBatchDelete(): void } } } - diff --git a/tests/Integration/Services/Biconnector/Source/Service/SourceTest.php b/tests/Integration/Services/Biconnector/Source/Service/SourceTest.php index 0b2cfaf0..0d11bdb2 100644 --- a/tests/Integration/Services/Biconnector/Source/Service/SourceTest.php +++ b/tests/Integration/Services/Biconnector/Source/Service/SourceTest.php @@ -222,4 +222,3 @@ public function testCount(): void $this->sourceService->delete($id); } } - From 0f488134bba174e70abee05c3b24ff275f7aecee Mon Sep 17 00:00:00 2001 From: Dmitriy Ignatenko Date: Fri, 15 May 2026 20:49:00 +0400 Subject: [PATCH 09/18] Change parameters of connector and source tests --- .../Connector/Service/BatchTest.php | 29 ++++++++++++---- .../Connector/Service/ConnectorTest.php | 29 ++++++++++++---- .../SourceItemResultAnnotationsTest.php | 34 ++++++++++++++++--- .../Biconnector/Source/Service/BatchTest.php | 32 +++++++++++++---- .../Biconnector/Source/Service/SourceTest.php | 32 +++++++++++++---- 5 files changed, 125 insertions(+), 31 deletions(-) diff --git a/tests/Integration/Services/Biconnector/Connector/Service/BatchTest.php b/tests/Integration/Services/Biconnector/Connector/Service/BatchTest.php index 87df4ab6..e1e700a4 100644 --- a/tests/Integration/Services/Biconnector/Connector/Service/BatchTest.php +++ b/tests/Integration/Services/Biconnector/Connector/Service/BatchTest.php @@ -60,15 +60,30 @@ private function makeConnectorFields(string $title): array return [ 'title' => $title, 'logo' => 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjIiIGhlaWdodD0iMjIiIHZpZXdCb3g9IjAgMCAyMiAyMiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KCTxjaXJjbGUgY3g9IjExIiBjeT0iMTEiIHI9IjEwIiBmaWxsPSIjRkYzQjNCIiAvPgoJPHRleHQgeD0iMTEiIHk9IjEzIiBmb250LWZhbWlseT0iQXJpYWwsIHNhbnMtc2VyaWYiIGZvbnQtc2l6ZT0iNiIgZmlsbD0iI0ZGRkZGRiIgdGV4dC1hbmNob3I9Im1pZGRsZSIgZm9udC13ZWlnaHQ9ImJvbGQiPlJFU1Q8L3RleHQ+Cjwvc3ZnPg==', - 'urlCheck' => 'http://example.com/api/check', - 'urlTableList' => 'http://example.com/api/table_list', - 'urlTableDescription' => 'http://example.com/api/table_description', - 'urlData' => 'http://example.com/api/data', - 'settings' => [ + 'urlCheck' => 'https://digitmind8080.cloudpub.ru/?connection_type=mysql&action=check', + 'urlTableList' => 'https://digitmind8080.cloudpub.ru/?connection_type=mysql&action=table_list', + 'urlTableDescription' => 'https://digitmind8080.cloudpub.ru/?connection_type=mysql&action=table_description', + 'urlData' => 'https://digitmind8080.cloudpub.ru/?connection_type=mysql&action=data', + 'settings' => [ [ - 'name' => 'Login', + 'name' => 'Host', 'type' => 'STRING', - 'code' => 'login', + 'code' => 'host', + ], + [ + 'name' => 'Port', + 'type' => 'STRING', + 'code' => 'port', + ], + [ + 'name' => 'Database', + 'type' => 'STRING', + 'code' => 'database', + ], + [ + 'name' => 'Username', + 'type' => 'STRING', + 'code' => 'username', ], [ 'name' => 'Password', diff --git a/tests/Integration/Services/Biconnector/Connector/Service/ConnectorTest.php b/tests/Integration/Services/Biconnector/Connector/Service/ConnectorTest.php index ffa766fc..99478351 100644 --- a/tests/Integration/Services/Biconnector/Connector/Service/ConnectorTest.php +++ b/tests/Integration/Services/Biconnector/Connector/Service/ConnectorTest.php @@ -62,15 +62,30 @@ private function makeConnectorFields(string $title): array return [ 'title' => $title, 'logo' => 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjIiIGhlaWdodD0iMjIiIHZpZXdCb3g9IjAgMCAyMiAyMiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KCTxjaXJjbGUgY3g9IjExIiBjeT0iMTEiIHI9IjEwIiBmaWxsPSIjRkYzQjNCIiAvPgoJPHRleHQgeD0iMTEiIHk9IjEzIiBmb250LWZhbWlseT0iQXJpYWwsIHNhbnMtc2VyaWYiIGZvbnQtc2l6ZT0iNiIgZmlsbD0iI0ZGRkZGRiIgdGV4dC1hbmNob3I9Im1pZGRsZSIgZm9udC13ZWlnaHQ9ImJvbGQiPlJFU1Q8L3RleHQ+Cjwvc3ZnPg==', - 'urlCheck' => 'http://example.com/api/check', - 'urlTableList' => 'http://example.com/api/table_list', - 'urlTableDescription' => 'http://example.com/api/table_description', - 'urlData' => 'http://example.com/api/data', - 'settings' => [ + 'urlCheck' => 'https://digitmind8080.cloudpub.ru/?connection_type=mysql&action=check', + 'urlTableList' => 'https://digitmind8080.cloudpub.ru/?connection_type=mysql&action=table_list', + 'urlTableDescription' => 'https://digitmind8080.cloudpub.ru/?connection_type=mysql&action=table_description', + 'urlData' => 'https://digitmind8080.cloudpub.ru/?connection_type=mysql&action=data', + 'settings' => [ [ - 'name' => 'Login', + 'name' => 'Host', 'type' => 'STRING', - 'code' => 'login', + 'code' => 'host', + ], + [ + 'name' => 'Port', + 'type' => 'STRING', + 'code' => 'port', + ], + [ + 'name' => 'Database', + 'type' => 'STRING', + 'code' => 'database', + ], + [ + 'name' => 'Username', + 'type' => 'STRING', + 'code' => 'username', ], [ 'name' => 'Password', diff --git a/tests/Integration/Services/Biconnector/Source/Result/SourceItemResultAnnotationsTest.php b/tests/Integration/Services/Biconnector/Source/Result/SourceItemResultAnnotationsTest.php index d09fc9bb..ad956827 100644 --- a/tests/Integration/Services/Biconnector/Source/Result/SourceItemResultAnnotationsTest.php +++ b/tests/Integration/Services/Biconnector/Source/Result/SourceItemResultAnnotationsTest.php @@ -91,12 +91,36 @@ private function makeConnectorFields(string $title): array return [ 'title' => $title, 'logo' => 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjIiIGhlaWdodD0iMjIiIHZpZXdCb3g9IjAgMCAyMiAyMiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KCTxjaXJjbGUgY3g9IjExIiBjeT0iMTEiIHI9IjEwIiBmaWxsPSIjRkYzQjNCIiAvPgoJPHRleHQgeD0iMTEiIHk9IjEzIiBmb250LWZhbWlseT0iQXJpYWwsIHNhbnMtc2VyaWYiIGZvbnQtc2l6ZT0iNiIgZmlsbD0iI0ZGRkZGRiIgdGV4dC1hbmNob3I9Im1pZGRsZSIgZm9udC13ZWlnaHQ9ImJvbGQiPlJFU1Q8L3RleHQ+Cjwvc3ZnPg==', - 'urlCheck' => 'http://example.com/api/check', - 'urlTableList' => 'http://example.com/api/table_list', - 'urlTableDescription' => 'http://example.com/api/table_description', - 'urlData' => 'http://example.com/api/data', + 'urlCheck' => 'https://digitmind8080.cloudpub.ru/?connection_type=mysql&action=check', + 'urlTableList' => 'https://digitmind8080.cloudpub.ru/?connection_type=mysql&action=table_list', + 'urlTableDescription' => 'https://digitmind8080.cloudpub.ru/?connection_type=mysql&action=table_description', + 'urlData' => 'https://digitmind8080.cloudpub.ru/?connection_type=mysql&action=data', 'settings' => [ - ['name' => 'Token', 'type' => 'STRING', 'code' => 'token'], + [ + 'name' => 'Host', + 'type' => 'STRING', + 'code' => 'host', + ], + [ + 'name' => 'Port', + 'type' => 'STRING', + 'code' => 'port', + ], + [ + 'name' => 'Database', + 'type' => 'STRING', + 'code' => 'database', + ], + [ + 'name' => 'Username', + 'type' => 'STRING', + 'code' => 'username', + ], + [ + 'name' => 'Password', + 'type' => 'STRING', + 'code' => 'password', + ], ], ]; } diff --git a/tests/Integration/Services/Biconnector/Source/Service/BatchTest.php b/tests/Integration/Services/Biconnector/Source/Service/BatchTest.php index 099c42a3..92552633 100644 --- a/tests/Integration/Services/Biconnector/Source/Service/BatchTest.php +++ b/tests/Integration/Services/Biconnector/Source/Service/BatchTest.php @@ -75,15 +75,35 @@ private function makeConnectorFields(string $title): array return [ 'title' => $title, 'logo' => 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjIiIGhlaWdodD0iMjIiIHZpZXdCb3g9IjAgMCAyMiAyMiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KCTxjaXJjbGUgY3g9IjExIiBjeT0iMTEiIHI9IjEwIiBmaWxsPSIjRkYzQjNCIiAvPgoJPHRleHQgeD0iMTEiIHk9IjEzIiBmb250LWZhbWlseT0iQXJpYWwsIHNhbnMtc2VyaWYiIGZvbnQtc2l6ZT0iNiIgZmlsbD0iI0ZGRkZGRiIgdGV4dC1hbmNob3I9Im1pZGRsZSIgZm9udC13ZWlnaHQ9ImJvbGQiPlJFU1Q8L3RleHQ+Cjwvc3ZnPg==', - 'urlCheck' => 'http://example.com/api/check', - 'urlTableList' => 'http://example.com/api/table_list', - 'urlTableDescription' => 'http://example.com/api/table_description', - 'urlData' => 'http://example.com/api/data', + 'urlCheck' => 'https://digitmind8080.cloudpub.ru/?connection_type=mysql&action=check', + 'urlTableList' => 'https://digitmind8080.cloudpub.ru/?connection_type=mysql&action=table_list', + 'urlTableDescription' => 'https://digitmind8080.cloudpub.ru/?connection_type=mysql&action=table_description', + 'urlData' => 'https://digitmind8080.cloudpub.ru/?connection_type=mysql&action=data', 'settings' => [ [ - 'name' => 'Token', + 'name' => 'Host', 'type' => 'STRING', - 'code' => 'token', + 'code' => 'host', + ], + [ + 'name' => 'Port', + 'type' => 'STRING', + 'code' => 'port', + ], + [ + 'name' => 'Database', + 'type' => 'STRING', + 'code' => 'database', + ], + [ + 'name' => 'Username', + 'type' => 'STRING', + 'code' => 'username', + ], + [ + 'name' => 'Password', + 'type' => 'STRING', + 'code' => 'password', ], ], ]; diff --git a/tests/Integration/Services/Biconnector/Source/Service/SourceTest.php b/tests/Integration/Services/Biconnector/Source/Service/SourceTest.php index 0d11bdb2..cab3853d 100644 --- a/tests/Integration/Services/Biconnector/Source/Service/SourceTest.php +++ b/tests/Integration/Services/Biconnector/Source/Service/SourceTest.php @@ -81,15 +81,35 @@ private function makeConnectorFields(string $title): array return [ 'title' => $title, 'logo' => 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjIiIGhlaWdodD0iMjIiIHZpZXdCb3g9IjAgMCAyMiAyMiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KCTxjaXJjbGUgY3g9IjExIiBjeT0iMTEiIHI9IjEwIiBmaWxsPSIjRkYzQjNCIiAvPgoJPHRleHQgeD0iMTEiIHk9IjEzIiBmb250LWZhbWlseT0iQXJpYWwsIHNhbnMtc2VyaWYiIGZvbnQtc2l6ZT0iNiIgZmlsbD0iI0ZGRkZGRiIgdGV4dC1hbmNob3I9Im1pZGRsZSIgZm9udC13ZWlnaHQ9ImJvbGQiPlJFU1Q8L3RleHQ+Cjwvc3ZnPg==', - 'urlCheck' => 'http://example.com/api/check', - 'urlTableList' => 'http://example.com/api/table_list', - 'urlTableDescription' => 'http://example.com/api/table_description', - 'urlData' => 'http://example.com/api/data', + 'urlCheck' => 'https://digitmind8080.cloudpub.ru/?connection_type=mysql&action=check', + 'urlTableList' => 'https://digitmind8080.cloudpub.ru/?connection_type=mysql&action=table_list', + 'urlTableDescription' => 'https://digitmind8080.cloudpub.ru/?connection_type=mysql&action=table_description', + 'urlData' => 'https://digitmind8080.cloudpub.ru/?connection_type=mysql&action=data', 'settings' => [ [ - 'name' => 'Token', + 'name' => 'Host', 'type' => 'STRING', - 'code' => 'token', + 'code' => 'host', + ], + [ + 'name' => 'Port', + 'type' => 'STRING', + 'code' => 'port', + ], + [ + 'name' => 'Database', + 'type' => 'STRING', + 'code' => 'database', + ], + [ + 'name' => 'Username', + 'type' => 'STRING', + 'code' => 'username', + ], + [ + 'name' => 'Password', + 'type' => 'STRING', + 'code' => 'password', ], ], ]; From bebede07993ec2303e15c71dc12b8c8a882d6dda Mon Sep 17 00:00:00 2001 From: Dmitriy Ignatenko Date: Mon, 18 May 2026 17:43:30 +0400 Subject: [PATCH 10/18] Fix biconnector.source.update test. Done biconnector.source --- .../Services/Biconnector/Source/Service/SourceTest.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/Integration/Services/Biconnector/Source/Service/SourceTest.php b/tests/Integration/Services/Biconnector/Source/Service/SourceTest.php index cab3853d..e02b5c5a 100644 --- a/tests/Integration/Services/Biconnector/Source/Service/SourceTest.php +++ b/tests/Integration/Services/Biconnector/Source/Service/SourceTest.php @@ -186,12 +186,14 @@ public function testList(): void public function testUpdate(): void { $title = 'source-' . $this->faker->uuid(); - $id = $this->sourceService->add($this->makeSourceFields($title))->getId(); + $sourceFields = $this->makeSourceFields($title); + $id = $this->sourceService->add($sourceFields)->getId(); $newTitle = $title . '-updated'; self::assertTrue( $this->sourceService->update($id, [ - 'title' => $newTitle, + 'title' => $newTitle, + 'settings' => $sourceFields['settings'], ])->isSuccess() ); From 3d867ad060d98866fab66cc575b755a8366485f7 Mon Sep 17 00:00:00 2001 From: Dmitriy Ignatenko Date: Tue, 19 May 2026 17:03:19 +0400 Subject: [PATCH 11/18] Add Dataset service for biconnector.dataset.* support (#469) --- CHANGELOG.md | 11 + Makefile | 4 + phpunit.xml.dist | 4 + .../Biconnector/BiconnectorServiceBuilder.php | 18 ++ .../Result/AddedDatasetBatchResult.php | 35 +++ .../Dataset/Result/AddedDatasetResult.php | 42 +++ .../Dataset/Result/DatasetItemResult.php | 55 ++++ .../Dataset/Result/DatasetResult.php | 41 +++ .../Dataset/Result/DatasetsResult.php | 45 +++ .../Result/DeletedDatasetBatchResult.php | 29 ++ .../Dataset/Result/DeletedDatasetResult.php | 33 +++ .../Result/UpdatedDatasetBatchResult.php | 29 ++ .../Dataset/Result/UpdatedDatasetResult.php | 33 +++ .../Biconnector/Dataset/Service/Batch.php | 171 ++++++++++++ .../Biconnector/Dataset/Service/Dataset.php | 259 +++++++++++++++++ .../DatasetItemResultAnnotationsTest.php | 184 ++++++++++++ .../Biconnector/Dataset/Service/BatchTest.php | 184 ++++++++++++ .../Dataset/Service/DatasetTest.php | 264 ++++++++++++++++++ 18 files changed, 1441 insertions(+) create mode 100644 src/Services/Biconnector/Dataset/Result/AddedDatasetBatchResult.php create mode 100644 src/Services/Biconnector/Dataset/Result/AddedDatasetResult.php create mode 100644 src/Services/Biconnector/Dataset/Result/DatasetItemResult.php create mode 100644 src/Services/Biconnector/Dataset/Result/DatasetResult.php create mode 100644 src/Services/Biconnector/Dataset/Result/DatasetsResult.php create mode 100644 src/Services/Biconnector/Dataset/Result/DeletedDatasetBatchResult.php create mode 100644 src/Services/Biconnector/Dataset/Result/DeletedDatasetResult.php create mode 100644 src/Services/Biconnector/Dataset/Result/UpdatedDatasetBatchResult.php create mode 100644 src/Services/Biconnector/Dataset/Result/UpdatedDatasetResult.php create mode 100644 src/Services/Biconnector/Dataset/Service/Batch.php create mode 100644 src/Services/Biconnector/Dataset/Service/Dataset.php create mode 100644 tests/Integration/Services/Biconnector/Dataset/Result/DatasetItemResultAnnotationsTest.php create mode 100644 tests/Integration/Services/Biconnector/Dataset/Service/BatchTest.php create mode 100644 tests/Integration/Services/Biconnector/Dataset/Service/DatasetTest.php diff --git a/CHANGELOG.md b/CHANGELOG.md index a705375f..c563a2ad 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,17 @@ ### Added +- Added service `Services\Biconnector\Dataset` with support methods, + see [biconnector.dataset.* methods](https://apidocs.bitrix24.com/api-reference/biconnector/dataset/index.html) ([#469](https://github.com/bitrix24/b24phpsdk/issues/469)): + - `add` adds a new dataset, with batch calls support + - `update` updates an existing dataset description, with batch calls support + - `get` gets information about the dataset by its identifier + - `list` gets the list of datasets, with batch calls support + - `delete` deletes a dataset, with batch calls support + - `fields` returns the fields description + - `updateFields` adds, updates visibility of, or deletes individual dataset columns (`biconnector.dataset.fields.update`) + - `count` counts datasets +- Added `dataset()` accessor to `BiconnectorServiceBuilder` ([#469](https://github.com/bitrix24/b24phpsdk/issues/469)) - Added service `Services\Biconnector\Source` with support methods, see [biconnector.source.* methods](https://apidocs.bitrix24.com/api-reference/biconnector/source/index.html) ([#469](https://github.com/bitrix24/b24phpsdk/issues/469)): - `add` adds a new data source, with batch calls support diff --git a/Makefile b/Makefile index 4c0c295b..04ffab13 100644 --- a/Makefile +++ b/Makefile @@ -619,6 +619,10 @@ test-integration-biconnector-connector: test-integration-biconnector-source: docker compose run --rm php-cli vendor/bin/phpunit --testsuite integration_tests_biconnector_source +.PHONY: test-integration-biconnector-dataset +test-integration-biconnector-dataset: + docker compose run --rm php-cli vendor/bin/phpunit --testsuite integration_tests_biconnector_dataset + .PHONY: integration_tests_crm_documentgenerator_document integration_tests_crm_documentgenerator_document: docker compose run --rm php-cli vendor/bin/phpunit --testsuite integration_tests_crm_documentgenerator_document diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 0fa842c5..0954dadd 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -343,6 +343,10 @@ ./tests/Integration/Services/Biconnector/Source/Service/ ./tests/Integration/Services/Biconnector/Source/Result/ + + ./tests/Integration/Services/Biconnector/Dataset/Service/ + ./tests/Integration/Services/Biconnector/Dataset/Result/ + diff --git a/src/Services/Biconnector/BiconnectorServiceBuilder.php b/src/Services/Biconnector/BiconnectorServiceBuilder.php index b32ffa3d..79680baf 100644 --- a/src/Services/Biconnector/BiconnectorServiceBuilder.php +++ b/src/Services/Biconnector/BiconnectorServiceBuilder.php @@ -19,6 +19,8 @@ use Bitrix24\SDK\Services\Biconnector\Connector\Batch as ConnectorBatch; use Bitrix24\SDK\Services\Biconnector\Connector\Service\Batch; use Bitrix24\SDK\Services\Biconnector\Connector\Service\Connector; +use Bitrix24\SDK\Services\Biconnector\Dataset\Service\Batch as DatasetServiceBatch; +use Bitrix24\SDK\Services\Biconnector\Dataset\Service\Dataset; use Bitrix24\SDK\Services\Biconnector\Source\Batch as SourceBatch; use Bitrix24\SDK\Services\Biconnector\Source\Service\Batch as SourceServiceBatch; use Bitrix24\SDK\Services\Biconnector\Source\Service\Source; @@ -51,6 +53,22 @@ public function connector(): Connector return $this->serviceCache[__METHOD__]; } + /** + * Get the Dataset service + */ + public function dataset(): Dataset + { + if (!isset($this->serviceCache[__METHOD__])) { + $this->serviceCache[__METHOD__] = new Dataset( + new DatasetServiceBatch($this->batch, $this->log), + $this->core, + $this->log + ); + } + + return $this->serviceCache[__METHOD__]; + } + /** * Get the Source service * diff --git a/src/Services/Biconnector/Dataset/Result/AddedDatasetBatchResult.php b/src/Services/Biconnector/Dataset/Result/AddedDatasetBatchResult.php new file mode 100644 index 00000000..0d87d7f0 --- /dev/null +++ b/src/Services/Biconnector/Dataset/Result/AddedDatasetBatchResult.php @@ -0,0 +1,35 @@ + + * + * For the full copyright and license information, please view the MIT-LICENSE.txt + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Bitrix24\SDK\Services\Biconnector\Dataset\Result; + +use Bitrix24\SDK\Core\Result\AddedItemBatchResult; + +/** + * Class AddedDatasetBatchResult + */ +class AddedDatasetBatchResult extends AddedItemBatchResult +{ + #[\Override] + public function getId(): int + { + $result = $this->getResponseData()->getResult(); + + if (!empty($result['id'])) { + return (int)$result['id']; + } + + return (int)$result; + } +} + diff --git a/src/Services/Biconnector/Dataset/Result/AddedDatasetResult.php b/src/Services/Biconnector/Dataset/Result/AddedDatasetResult.php new file mode 100644 index 00000000..1ae6707e --- /dev/null +++ b/src/Services/Biconnector/Dataset/Result/AddedDatasetResult.php @@ -0,0 +1,42 @@ + + * + * For the full copyright and license information, please view the MIT-LICENSE.txt + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Bitrix24\SDK\Services\Biconnector\Dataset\Result; + +use Bitrix24\SDK\Core\Exceptions\BaseException; +use Bitrix24\SDK\Core\Result\AddedItemResult; + +/** + * Class AddedDatasetResult + * + * Wraps the response from biconnector.dataset.add. + * The API returns: result.id (integer) + */ +class AddedDatasetResult extends AddedItemResult +{ + /** + * @throws BaseException + */ + #[\Override] + public function getId(): int + { + $result = $this->getCoreResponse()->getResponseData()->getResult(); + + if (!empty($result['id'])) { + return (int)$result['id']; + } + + return (int)$result; + } +} + diff --git a/src/Services/Biconnector/Dataset/Result/DatasetItemResult.php b/src/Services/Biconnector/Dataset/Result/DatasetItemResult.php new file mode 100644 index 00000000..c807c4ba --- /dev/null +++ b/src/Services/Biconnector/Dataset/Result/DatasetItemResult.php @@ -0,0 +1,55 @@ + + * + * For the full copyright and license information, please view the MIT-LICENSE.txt + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Bitrix24\SDK\Services\Biconnector\Dataset\Result; + +use Bitrix24\SDK\Core\Result\AbstractItem; +use Carbon\CarbonImmutable; + +/** + * Class DatasetItemResult + * + * Field names correspond to the actual API response returned by biconnector.dataset.get / biconnector.dataset.list. + * + * @see https://apidocs.bitrix24.com/api-reference/biconnector/dataset/biconnector-dataset-fields.html + * + * @property-read int $id + * @property-read int|null $sourceId + * @property-read string|null $name + * @property-read string|null $type + * @property-read string|null $description + * @property-read string|null $externalName + * @property-read string|null $externalCode + * @property-read int|null $externalId + * @property-read CarbonImmutable $dateCreate + * @property-read CarbonImmutable $dateUpdate + * @property-read int $createdById + * @property-read int $updatedById + * @property-read array|null $fields + */ +class DatasetItemResult extends AbstractItem +{ + #[\Override] + public function __get($offset): mixed + { + return match ($offset) { + 'id', 'createdById', 'updatedById' => isset($this->data[$offset]) ? (int)$this->data[$offset] : null, + 'sourceId', 'externalId' => isset($this->data[$offset]) ? (int)$this->data[$offset] : null, + 'dateCreate', 'dateUpdate' => isset($this->data[$offset]) + ? CarbonImmutable::parse($this->data[$offset]) + : null, + default => $this->data[$offset] ?? null, + }; + } +} + diff --git a/src/Services/Biconnector/Dataset/Result/DatasetResult.php b/src/Services/Biconnector/Dataset/Result/DatasetResult.php new file mode 100644 index 00000000..09352c67 --- /dev/null +++ b/src/Services/Biconnector/Dataset/Result/DatasetResult.php @@ -0,0 +1,41 @@ + + * + * For the full copyright and license information, please view the MIT-LICENSE.txt + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Bitrix24\SDK\Services\Biconnector\Dataset\Result; + +use Bitrix24\SDK\Core\Exceptions\BaseException; +use Bitrix24\SDK\Core\Result\AbstractResult; + +/** + * Class DatasetResult + * + * Wraps the response from biconnector.dataset.get. + * The API returns: result.item (object) + */ +class DatasetResult extends AbstractResult +{ + /** + * @throws BaseException + */ + public function dataset(): DatasetItemResult + { + $result = $this->getCoreResponse()->getResponseData()->getResult(); + + if (!empty($result['item']) && is_array($result['item'])) { + return new DatasetItemResult($result['item']); + } + + return new DatasetItemResult($result); + } +} + diff --git a/src/Services/Biconnector/Dataset/Result/DatasetsResult.php b/src/Services/Biconnector/Dataset/Result/DatasetsResult.php new file mode 100644 index 00000000..bf17fcf1 --- /dev/null +++ b/src/Services/Biconnector/Dataset/Result/DatasetsResult.php @@ -0,0 +1,45 @@ + + * + * For the full copyright and license information, please view the MIT-LICENSE.txt + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Bitrix24\SDK\Services\Biconnector\Dataset\Result; + +use Bitrix24\SDK\Core\Exceptions\BaseException; +use Bitrix24\SDK\Core\Result\AbstractResult; + +/** + * Class DatasetsResult + * + * Wraps the response from biconnector.dataset.list. + * The API returns a flat array of dataset items. + */ +class DatasetsResult extends AbstractResult +{ + /** + * @return DatasetItemResult[] + * @throws BaseException + */ + public function getDatasets(): array + { + $items = []; + $result = $this->getCoreResponse()->getResponseData()->getResult(); + + if (array_is_list($result)) { + foreach ($result as $item) { + $items[] = new DatasetItemResult($item); + } + } + + return $items; + } +} + diff --git a/src/Services/Biconnector/Dataset/Result/DeletedDatasetBatchResult.php b/src/Services/Biconnector/Dataset/Result/DeletedDatasetBatchResult.php new file mode 100644 index 00000000..74d79e70 --- /dev/null +++ b/src/Services/Biconnector/Dataset/Result/DeletedDatasetBatchResult.php @@ -0,0 +1,29 @@ + + * + * For the full copyright and license information, please view the MIT-LICENSE.txt + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Bitrix24\SDK\Services\Biconnector\Dataset\Result; + +use Bitrix24\SDK\Core\Result\DeletedItemBatchResult; + +/** + * Class DeletedDatasetBatchResult + */ +class DeletedDatasetBatchResult extends DeletedItemBatchResult +{ + #[\Override] + public function isSuccess(): bool + { + return (bool)$this->getResponseData()->getResult(); + } +} + diff --git a/src/Services/Biconnector/Dataset/Result/DeletedDatasetResult.php b/src/Services/Biconnector/Dataset/Result/DeletedDatasetResult.php new file mode 100644 index 00000000..08afd5f7 --- /dev/null +++ b/src/Services/Biconnector/Dataset/Result/DeletedDatasetResult.php @@ -0,0 +1,33 @@ + + * + * For the full copyright and license information, please view the MIT-LICENSE.txt + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Bitrix24\SDK\Services\Biconnector\Dataset\Result; + +use Bitrix24\SDK\Core\Exceptions\BaseException; +use Bitrix24\SDK\Core\Result\DeletedItemResult; + +/** + * Class DeletedDatasetResult + */ +class DeletedDatasetResult extends DeletedItemResult +{ + /** + * @throws BaseException + */ + #[\Override] + public function isSuccess(): bool + { + return (bool)$this->getCoreResponse()->getResponseData()->getResult(); + } +} + diff --git a/src/Services/Biconnector/Dataset/Result/UpdatedDatasetBatchResult.php b/src/Services/Biconnector/Dataset/Result/UpdatedDatasetBatchResult.php new file mode 100644 index 00000000..f9818607 --- /dev/null +++ b/src/Services/Biconnector/Dataset/Result/UpdatedDatasetBatchResult.php @@ -0,0 +1,29 @@ + + * + * For the full copyright and license information, please view the MIT-LICENSE.txt + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Bitrix24\SDK\Services\Biconnector\Dataset\Result; + +use Bitrix24\SDK\Core\Result\UpdatedItemBatchResult; + +/** + * Class UpdatedDatasetBatchResult + */ +class UpdatedDatasetBatchResult extends UpdatedItemBatchResult +{ + #[\Override] + public function isSuccess(): bool + { + return (bool)$this->getResponseData()->getResult(); + } +} + diff --git a/src/Services/Biconnector/Dataset/Result/UpdatedDatasetResult.php b/src/Services/Biconnector/Dataset/Result/UpdatedDatasetResult.php new file mode 100644 index 00000000..9a209f8c --- /dev/null +++ b/src/Services/Biconnector/Dataset/Result/UpdatedDatasetResult.php @@ -0,0 +1,33 @@ + + * + * For the full copyright and license information, please view the MIT-LICENSE.txt + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Bitrix24\SDK\Services\Biconnector\Dataset\Result; + +use Bitrix24\SDK\Core\Exceptions\BaseException; +use Bitrix24\SDK\Core\Result\UpdatedItemResult; + +/** + * Class UpdatedDatasetResult + */ +class UpdatedDatasetResult extends UpdatedItemResult +{ + /** + * @throws BaseException + */ + #[\Override] + public function isSuccess(): bool + { + return (bool)$this->getCoreResponse()->getResponseData()->getResult(); + } +} + diff --git a/src/Services/Biconnector/Dataset/Service/Batch.php b/src/Services/Biconnector/Dataset/Service/Batch.php new file mode 100644 index 00000000..ad51a3ec --- /dev/null +++ b/src/Services/Biconnector/Dataset/Service/Batch.php @@ -0,0 +1,171 @@ + + * + * For the full copyright and license information, please view the MIT-LICENSE.txt + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Bitrix24\SDK\Services\Biconnector\Dataset\Service; + +use Bitrix24\SDK\Attributes\ApiBatchMethodMetadata; +use Bitrix24\SDK\Attributes\ApiBatchServiceMetadata; +use Bitrix24\SDK\Core\Contracts\BatchOperationsInterface; +use Bitrix24\SDK\Core\Credentials\Scope; +use Bitrix24\SDK\Core\Exceptions\BaseException; +use Bitrix24\SDK\Services\Biconnector\Dataset\Result\AddedDatasetBatchResult; +use Bitrix24\SDK\Services\Biconnector\Dataset\Result\DatasetItemResult; +use Bitrix24\SDK\Services\Biconnector\Dataset\Result\DeletedDatasetBatchResult; +use Bitrix24\SDK\Services\Biconnector\Dataset\Result\UpdatedDatasetBatchResult; +use Generator; +use Psr\Log\LoggerInterface; + +#[ApiBatchServiceMetadata(new Scope(['biconnector']))] +class Batch +{ + /** + * Batch constructor + */ + public function __construct(protected BatchOperationsInterface $batch, protected LoggerInterface $log) + { + } + + /** + * Batch list datasets + * + * @link https://apidocs.bitrix24.com/api-reference/biconnector/dataset/biconnector-dataset-list.html + * + * @return Generator + * @throws BaseException + */ + #[ApiBatchMethodMetadata( + 'biconnector.dataset.list', + 'https://apidocs.bitrix24.com/api-reference/biconnector/dataset/biconnector-dataset-list.html', + 'Batch list datasets' + )] + public function list( + array $order = [], + array $filter = [], + array $select = [], + ?int $limit = null + ): Generator { + $this->log->debug( + 'batchList', + [ + 'order' => $order, + 'filter' => $filter, + 'select' => $select, + 'limit' => $limit, + ] + ); + + foreach ( + $this->batch->getTraversableList( + 'biconnector.dataset.list', + $order, + $filter, + $select, + $limit + ) as $key => $value + ) { + yield $key => new DatasetItemResult($value); + } + } + + /** + * Batch add datasets + * + * @link https://apidocs.bitrix24.com/api-reference/biconnector/dataset/biconnector-dataset-add.html + * + * @param array $datasets + * + * @return Generator + * @throws BaseException + */ + #[ApiBatchMethodMetadata( + 'biconnector.dataset.add', + 'https://apidocs.bitrix24.com/api-reference/biconnector/dataset/biconnector-dataset-add.html', + 'Batch add datasets' + )] + public function add(array $datasets): Generator + { + $items = []; + foreach ($datasets as $item) { + $items[] = [ + 'fields' => $item, + ]; + } + + foreach ($this->batch->addEntityItems('biconnector.dataset.add', $items) as $key => $item) { + yield $key => new AddedDatasetBatchResult($item); + } + } + + /** + * Batch update datasets + * + * Update elements in array with structure: + * id => [ // Dataset id + * 'fields' => [] // Dataset fields to update + * ] + * + * @param array $entityItems + * + * @return Generator + * @throws BaseException + */ + #[ApiBatchMethodMetadata( + 'biconnector.dataset.update', + 'https://apidocs.bitrix24.com/api-reference/biconnector/dataset/biconnector-dataset-update.html', + 'Batch update datasets' + )] + public function update(array $entityItems): Generator + { + foreach ( + $this->batch->updateEntityItems( + 'biconnector.dataset.update', + $entityItems + ) as $key => $item + ) { + yield $key => new UpdatedDatasetBatchResult($item); + } + } + + /** + * Batch delete datasets + * + * @param int[] $datasetIds + * + * @return Generator + * @throws BaseException + */ + #[ApiBatchMethodMetadata( + 'biconnector.dataset.delete', + 'https://apidocs.bitrix24.com/api-reference/biconnector/dataset/biconnector-dataset-delete.html', + 'Batch delete datasets' + )] + public function delete(array $datasetIds): Generator + { + foreach ( + $this->batch->deleteEntityItems( + 'biconnector.dataset.delete', + $datasetIds + ) as $key => $item + ) { + yield $key => new DeletedDatasetBatchResult($item); + } + } +} + diff --git a/src/Services/Biconnector/Dataset/Service/Dataset.php b/src/Services/Biconnector/Dataset/Service/Dataset.php new file mode 100644 index 00000000..dc069452 --- /dev/null +++ b/src/Services/Biconnector/Dataset/Service/Dataset.php @@ -0,0 +1,259 @@ + + * + * For the full copyright and license information, please view the MIT-LICENSE.txt + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Bitrix24\SDK\Services\Biconnector\Dataset\Service; + +use Bitrix24\SDK\Attributes\ApiEndpointMetadata; +use Bitrix24\SDK\Attributes\ApiServiceMetadata; +use Bitrix24\SDK\Core\Contracts\CoreInterface; +use Bitrix24\SDK\Core\Credentials\Scope; +use Bitrix24\SDK\Core\Exceptions\BaseException; +use Bitrix24\SDK\Core\Exceptions\TransportException; +use Bitrix24\SDK\Core\Result\FieldsResult; +use Bitrix24\SDK\Services\AbstractService; +use Bitrix24\SDK\Services\Biconnector\Dataset\Result\AddedDatasetResult; +use Bitrix24\SDK\Services\Biconnector\Dataset\Result\DatasetResult; +use Bitrix24\SDK\Services\Biconnector\Dataset\Result\DatasetsResult; +use Bitrix24\SDK\Services\Biconnector\Dataset\Result\DeletedDatasetResult; +use Bitrix24\SDK\Services\Biconnector\Dataset\Result\UpdatedDatasetResult; +use Psr\Log\LoggerInterface; + +#[ApiServiceMetadata(new Scope(['biconnector']))] +class Dataset extends AbstractService +{ + /** + * Dataset constructor + */ + public function __construct(public Batch $batch, CoreInterface $core, LoggerInterface $logger) + { + parent::__construct($core, $logger); + } + + /** + * Add a new dataset + * + * @link https://apidocs.bitrix24.com/api-reference/biconnector/dataset/biconnector-dataset-add.html + * + * @param array{ + * name: string, + * externalName: string, + * externalCode: string, + * sourceId: int, + * description?: string, + * fields?: array, + * } $fields + * + * @throws BaseException + * @throws TransportException + */ + #[ApiEndpointMetadata( + 'biconnector.dataset.add', + 'https://apidocs.bitrix24.com/api-reference/biconnector/dataset/biconnector-dataset-add.html', + 'Add a new dataset' + )] + public function add(array $fields): AddedDatasetResult + { + return new AddedDatasetResult( + $this->core->call( + 'biconnector.dataset.add', + [ + 'fields' => $fields, + ] + ) + ); + } + + /** + * Update an existing dataset + * + * @link https://apidocs.bitrix24.com/api-reference/biconnector/dataset/biconnector-dataset-update.html + * + * @param array{ + * description?: string, + * } $fields + * + * @throws BaseException + * @throws TransportException + */ + #[ApiEndpointMetadata( + 'biconnector.dataset.update', + 'https://apidocs.bitrix24.com/api-reference/biconnector/dataset/biconnector-dataset-update.html', + 'Update an existing dataset' + )] + public function update(int $id, array $fields): UpdatedDatasetResult + { + return new UpdatedDatasetResult( + $this->core->call( + 'biconnector.dataset.update', + [ + 'id' => $id, + 'fields' => $fields, + ] + ) + ); + } + + /** + * Get a dataset by its ID + * + * @link https://apidocs.bitrix24.com/api-reference/biconnector/dataset/biconnector-dataset-get.html + * + * @throws BaseException + * @throws TransportException + */ + #[ApiEndpointMetadata( + 'biconnector.dataset.get', + 'https://apidocs.bitrix24.com/api-reference/biconnector/dataset/biconnector-dataset-get.html', + 'Get a dataset by its ID' + )] + public function get(int $id): DatasetResult + { + return new DatasetResult( + $this->core->call( + 'biconnector.dataset.get', + [ + 'id' => $id, + ] + ) + ); + } + + /** + * Get a list of datasets + * + * @link https://apidocs.bitrix24.com/api-reference/biconnector/dataset/biconnector-dataset-list.html + * + * @param array $order - sort fields, e.g. ['dateCreate' => 'DESC'] + * @param array $filter - filter fields + * @param array $select - fields to include in the result + * + * @throws BaseException + * @throws TransportException + */ + #[ApiEndpointMetadata( + 'biconnector.dataset.list', + 'https://apidocs.bitrix24.com/api-reference/biconnector/dataset/biconnector-dataset-list.html', + 'Get a list of datasets' + )] + public function list(array $order = [], array $filter = [], array $select = []): DatasetsResult + { + return new DatasetsResult( + $this->core->call( + 'biconnector.dataset.list', + [ + 'order' => $order, + 'filter' => $filter, + 'select' => $select, + ] + ) + ); + } + + /** + * Delete a dataset by its ID + * + * @link https://apidocs.bitrix24.com/api-reference/biconnector/dataset/biconnector-dataset-delete.html + * + * @throws BaseException + * @throws TransportException + */ + #[ApiEndpointMetadata( + 'biconnector.dataset.delete', + 'https://apidocs.bitrix24.com/api-reference/biconnector/dataset/biconnector-dataset-delete.html', + 'Delete a dataset by its ID' + )] + public function delete(int $id): DeletedDatasetResult + { + return new DeletedDatasetResult( + $this->core->call( + 'biconnector.dataset.delete', + [ + 'id' => $id, + ] + ) + ); + } + + /** + * Get the fields description for datasets + * + * @link https://apidocs.bitrix24.com/api-reference/biconnector/dataset/biconnector-dataset-fields.html + * + * @throws BaseException + * @throws TransportException + */ + #[ApiEndpointMetadata( + 'biconnector.dataset.fields', + 'https://apidocs.bitrix24.com/api-reference/biconnector/dataset/biconnector-dataset-fields.html', + 'Get the fields description for datasets' + )] + public function fields(): FieldsResult + { + return new FieldsResult($this->core->call('biconnector.dataset.fields')); + } + + /** + * Update fields of an existing dataset (add, update visibility, or delete dataset columns) + * + * @link https://apidocs.bitrix24.com/api-reference/biconnector/dataset/biconnector-dataset-fields-update.html + * + * @param array $add - fields to add + * @param array $update - fields to update (visibility) + * @param int[] $delete - field IDs to delete + * + * @throws BaseException + * @throws TransportException + */ + #[ApiEndpointMetadata( + 'biconnector.dataset.fields.update', + 'https://apidocs.bitrix24.com/api-reference/biconnector/dataset/biconnector-dataset-fields-update.html', + 'Update fields of an existing dataset' + )] + public function updateFields(int $id, array $add = [], array $update = [], array $delete = []): UpdatedDatasetResult + { + $params = ['id' => $id]; + + if ($add !== []) { + $params['add'] = $add; + } + + if ($update !== []) { + $params['update'] = $update; + } + + if ($delete !== []) { + $params['delete'] = $delete; + } + + return new UpdatedDatasetResult( + $this->core->call('biconnector.dataset.fields.update', $params) + ); + } + + /** + * Count datasets + * + * @throws BaseException + * @throws TransportException + */ + public function count(): int + { + $count = 0; + foreach ($this->batch->list() as $item) { + $count++; + } + + return $count; + } +} + diff --git a/tests/Integration/Services/Biconnector/Dataset/Result/DatasetItemResultAnnotationsTest.php b/tests/Integration/Services/Biconnector/Dataset/Result/DatasetItemResultAnnotationsTest.php new file mode 100644 index 00000000..7f8528eb --- /dev/null +++ b/tests/Integration/Services/Biconnector/Dataset/Result/DatasetItemResultAnnotationsTest.php @@ -0,0 +1,184 @@ + + * + * For the full copyright and license information, please view the MIT-LICENSE.txt + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Bitrix24\SDK\Tests\Integration\Services\Biconnector\Dataset\Result; + +use Bitrix24\SDK\Core\Exceptions\BaseException; +use Bitrix24\SDK\Core\Exceptions\TransportException; +use Bitrix24\SDK\Services\Biconnector\Connector\Service\Connector; +use Bitrix24\SDK\Services\Biconnector\Dataset\Result\DatasetItemResult; +use Bitrix24\SDK\Services\Biconnector\Dataset\Service\Dataset; +use Bitrix24\SDK\Services\Biconnector\Source\Service\Source; +use Bitrix24\SDK\Tests\CustomAssertions\CustomBitrix24Assertions; +use Bitrix24\SDK\Tests\Integration\Factory; +use Faker\Generator; +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\Test; +use PHPUnit\Framework\Attributes\TestDox; +use PHPUnit\Framework\TestCase; +use Faker; + +#[CoversClass(DatasetItemResult::class)] +class DatasetItemResultAnnotationsTest extends TestCase +{ + use CustomBitrix24Assertions; + + private Dataset $datasetService; + + private Source $sourceService; + + private Connector $connectorService; + + private Generator $faker; + + private int $connectorId; + + private int $sourceId; + + private int $datasetId; + + /** + * @throws BaseException + * @throws TransportException + */ + #[\Override] + protected function setUp(): void + { + $biconnectorServiceBuilder = Factory::getServiceBuilder(true)->getBiconnectorScope(); + $this->datasetService = $biconnectorServiceBuilder->dataset(); + $this->sourceService = $biconnectorServiceBuilder->source(); + $this->connectorService = $biconnectorServiceBuilder->connector(); + $this->faker = Faker\Factory::create(); + + // Create connector, source, and dataset for annotation tests + $this->connectorId = $this->connectorService->add($this->makeConnectorFields( + 'connector-annotations-' . $this->faker->uuid() + ))->getId(); + + $this->sourceId = $this->sourceService->add([ + 'title' => 'source-annotations-' . $this->faker->uuid(), + 'connectorId' => $this->connectorId, + 'settings' => ['token' => 'test-token-' . $this->faker->uuid()], + ])->getId(); + + $name = 'ds' . substr(str_replace('-', '', $this->faker->uuid()), 0, 20); + $this->datasetId = $this->datasetService->add([ + 'sourceId' => $this->sourceId, + 'name' => $name, + 'externalName' => 'ext_' . $name, + 'externalCode' => 'code_' . $name, + 'fields' => [ + ['type' => 'int', 'name' => 'ID', 'externalCode' => 'ID'], + ['type' => 'string', 'name' => 'NAME', 'externalCode' => 'NAME'], + ], + ])->getId(); + } + + /** + * @throws BaseException + * @throws TransportException + */ + #[\Override] + protected function tearDown(): void + { + try { + $this->datasetService->delete($this->datasetId); + } catch (\Throwable) { + } + + try { + $this->sourceService->delete($this->sourceId); + } catch (\Throwable) { + } + + try { + $this->connectorService->delete($this->connectorId); + } catch (\Throwable) { + } + } + + private function makeConnectorFields(string $title): array + { + return [ + 'title' => $title, + 'logo' => 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjIiIGhlaWdodD0iMjIiIHZpZXdCb3g9IjAgMCAyMiAyMiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KCTxjaXJjbGUgY3g9IjExIiBjeT0iMTEiIHI9IjEwIiBmaWxsPSIjRkYzQjNCIiAvPgoJPHRleHQgeD0iMTEiIHk9IjEzIiBmb250LWZhbWlseT0iQXJpYWwsIHNhbnMtc2VyaWYiIGZvbnQtc2l6ZT0iNiIgZmlsbD0iI0ZGRkZGRiIgdGV4dC1hbmNob3I9Im1pZGRsZSIgZm9udC13ZWlnaHQ9ImJvbGQiPlJFU1Q8L3RleHQ+Cjwvc3ZnPg==', + 'urlCheck' => 'https://digitmind8080.cloudpub.ru/?connection_type=mysql&action=check', + 'urlTableList' => 'https://digitmind8080.cloudpub.ru/?connection_type=mysql&action=table_list', + 'urlTableDescription' => 'https://digitmind8080.cloudpub.ru/?connection_type=mysql&action=table_description', + 'urlData' => 'https://digitmind8080.cloudpub.ru/?connection_type=mysql&action=data', + 'settings' => [ + ['name' => 'Host', 'type' => 'STRING', 'code' => 'host'], + ['name' => 'Port', 'type' => 'STRING', 'code' => 'port'], + ['name' => 'Database', 'type' => 'STRING', 'code' => 'database'], + ['name' => 'Username', 'type' => 'STRING', 'code' => 'username'], + ['name' => 'Password', 'type' => 'STRING', 'code' => 'password'], + ], + ]; + } + + #[Test] + #[TestDox('all fields in DatasetItemResult are annotated and match live API fields schema')] + public function testAllSystemFieldsAnnotated(): void + { + $fieldCodes = $this->getDatasetFieldCodes(); + + $this->assertBitrix24AllResultItemFieldsAnnotated( + $fieldCodes, + DatasetItemResult::class + ); + } + + #[Test] + #[TestDox('all fields in DatasetItemResult have valid type casting matching API fields schema')] + public function testAllSystemFieldsHasValidTypeAnnotation(): void + { + $fieldTypesMap = $this->getDatasetFieldTypesMap(); + + $this->assertBitrix24AllResultItemFieldsHasValidTypeAnnotation( + $fieldTypesMap, + DatasetItemResult::class + ); + } + + /** + * Returns list of field codes from biconnector.dataset.fields API. + * + * @return array + */ + private function getDatasetFieldCodes(): array + { + $raw = $this->datasetService->fields()->getFieldsDescription(); + $fields = $raw['fields'] ?? []; + + return array_column($fields, 'title'); + } + + /** + * Returns field type map compatible with assertBitrix24AllResultItemFieldsHasValidTypeAnnotation. + * + * @return array + */ + private function getDatasetFieldTypesMap(): array + { + $raw = $this->datasetService->fields()->getFieldsDescription(); + $fields = $raw['fields'] ?? []; + + $result = []; + foreach ($fields as $field) { + $result[$field['title']] = ['type' => $field['type']]; + } + + return $result; + } +} + diff --git a/tests/Integration/Services/Biconnector/Dataset/Service/BatchTest.php b/tests/Integration/Services/Biconnector/Dataset/Service/BatchTest.php new file mode 100644 index 00000000..8b24a77b --- /dev/null +++ b/tests/Integration/Services/Biconnector/Dataset/Service/BatchTest.php @@ -0,0 +1,184 @@ + + * + * For the full copyright and license information, please view the MIT-LICENSE.txt + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Bitrix24\SDK\Tests\Integration\Services\Biconnector\Dataset\Service; + +use Bitrix24\SDK\Core\Exceptions\BaseException; +use Bitrix24\SDK\Core\Exceptions\TransportException; +use Bitrix24\SDK\Services\Biconnector\Connector\Service\Connector; +use Bitrix24\SDK\Services\Biconnector\Dataset\Result\DatasetItemResult; +use Bitrix24\SDK\Services\Biconnector\Dataset\Service\Batch; +use Bitrix24\SDK\Services\Biconnector\Dataset\Service\Dataset; +use Bitrix24\SDK\Services\Biconnector\Source\Service\Source; +use Bitrix24\SDK\Tests\Integration\Factory; +use Faker\Generator; +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\TestCase; +use Faker; + +#[CoversClass(Batch::class)] +class BatchTest extends TestCase +{ + private Dataset $datasetService; + + private Source $sourceService; + + private Connector $connectorService; + + private Generator $faker; + + private int $connectorId; + + private int $sourceId; + + /** + * @throws BaseException + * @throws TransportException + */ + #[\Override] + protected function setUp(): void + { + $biconnectorServiceBuilder = Factory::getServiceBuilder(true)->getBiconnectorScope(); + $this->datasetService = $biconnectorServiceBuilder->dataset(); + $this->sourceService = $biconnectorServiceBuilder->source(); + $this->connectorService = $biconnectorServiceBuilder->connector(); + $this->faker = Faker\Factory::create(); + + // Create a connector and source to use for dataset batch tests + $this->connectorId = $this->connectorService->add($this->makeConnectorFields( + 'connector-for-dataset-batch-' . $this->faker->uuid() + ))->getId(); + + $this->sourceId = $this->sourceService->add([ + 'title' => 'source-for-dataset-batch-' . $this->faker->uuid(), + 'connectorId' => $this->connectorId, + 'settings' => ['token' => 'test-token-' . $this->faker->uuid()], + ])->getId(); + } + + /** + * @throws BaseException + * @throws TransportException + */ + #[\Override] + protected function tearDown(): void + { + try { + $this->sourceService->delete($this->sourceId); + } catch (\Throwable) { + } + + try { + $this->connectorService->delete($this->connectorId); + } catch (\Throwable) { + } + } + + private function makeConnectorFields(string $title): array + { + return [ + 'title' => $title, + 'logo' => 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjIiIGhlaWdodD0iMjIiIHZpZXdCb3g9IjAgMCAyMiAyMiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KCTxjaXJjbGUgY3g9IjExIiBjeT0iMTEiIHI9IjEwIiBmaWxsPSIjRkYzQjNCIiAvPgoJPHRleHQgeD0iMTEiIHk9IjEzIiBmb250LWZhbWlseT0iQXJpYWwsIHNhbnMtc2VyaWYiIGZvbnQtc2l6ZT0iNiIgZmlsbD0iI0ZGRkZGRiIgdGV4dC1hbmNob3I9Im1pZGRsZSIgZm9udC13ZWlnaHQ9ImJvbGQiPlJFU1Q8L3RleHQ+Cjwvc3ZnPg==', + 'urlCheck' => 'https://digitmind8080.cloudpub.ru/?connection_type=mysql&action=check', + 'urlTableList' => 'https://digitmind8080.cloudpub.ru/?connection_type=mysql&action=table_list', + 'urlTableDescription' => 'https://digitmind8080.cloudpub.ru/?connection_type=mysql&action=table_description', + 'urlData' => 'https://digitmind8080.cloudpub.ru/?connection_type=mysql&action=data', + 'settings' => [ + ['name' => 'Host', 'type' => 'STRING', 'code' => 'host'], + ['name' => 'Port', 'type' => 'STRING', 'code' => 'port'], + ['name' => 'Database', 'type' => 'STRING', 'code' => 'database'], + ['name' => 'Username', 'type' => 'STRING', 'code' => 'username'], + ['name' => 'Password', 'type' => 'STRING', 'code' => 'password'], + ], + ]; + } + + private function makeDatasetFields(string $name): array + { + return [ + 'sourceId' => $this->sourceId, + 'name' => $name, + 'externalName' => 'ext_' . $name, + 'externalCode' => 'code_' . $name, + 'fields' => [ + ['type' => 'int', 'name' => 'ID', 'externalCode' => 'ID'], + ['type' => 'string', 'name' => 'NAME', 'externalCode' => 'NAME'], + ], + ]; + } + + /** + * @throws BaseException + * @throws TransportException + */ + public function testBatchList(): void + { + $name = 'ds' . substr(str_replace('-', '', $this->faker->uuid()), 0, 20); + $id = $this->datasetService->add($this->makeDatasetFields($name))->getId(); + + $count = 0; + foreach ($this->datasetService->batch->list([], [], [], 10) as $item) { + self::assertInstanceOf(DatasetItemResult::class, $item); + $count++; + } + + self::assertGreaterThanOrEqual(1, $count); + + // Cleanup + $this->datasetService->delete($id); + } + + /** + * @throws BaseException + * @throws TransportException + */ + public function testBatchAdd(): void + { + $datasets = []; + for ($i = 0; $i < 3; $i++) { + $name = 'ds' . substr(str_replace('-', '', $this->faker->uuid()), 0, 20); + $datasets[] = $this->makeDatasetFields($name); + } + + $addedIds = []; + foreach ($this->datasetService->batch->add($datasets) as $result) { + $addedIds[] = $result->getId(); + self::assertGreaterThanOrEqual(1, $result->getId()); + } + + self::assertCount(3, $addedIds); + + // Cleanup + foreach ($this->datasetService->batch->delete($addedIds) as $deleteResult) { + self::assertTrue($deleteResult->isSuccess()); + } + } + + /** + * @throws BaseException + * @throws TransportException + */ + public function testBatchDelete(): void + { + $ids = []; + for ($i = 0; $i < 2; $i++) { + $name = 'ds' . substr(str_replace('-', '', $this->faker->uuid()), 0, 20); + $ids[] = $this->datasetService->add($this->makeDatasetFields($name))->getId(); + } + + foreach ($this->datasetService->batch->delete($ids) as $result) { + self::assertTrue($result->isSuccess()); + } + } +} + diff --git a/tests/Integration/Services/Biconnector/Dataset/Service/DatasetTest.php b/tests/Integration/Services/Biconnector/Dataset/Service/DatasetTest.php new file mode 100644 index 00000000..eadc27b4 --- /dev/null +++ b/tests/Integration/Services/Biconnector/Dataset/Service/DatasetTest.php @@ -0,0 +1,264 @@ + + * + * For the full copyright and license information, please view the MIT-LICENSE.txt + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Bitrix24\SDK\Tests\Integration\Services\Biconnector\Dataset\Service; + +use Bitrix24\SDK\Core\Exceptions\BaseException; +use Bitrix24\SDK\Core\Exceptions\TransportException; +use Bitrix24\SDK\Services\Biconnector\Connector\Service\Connector; +use Bitrix24\SDK\Services\Biconnector\Dataset\Result\DatasetItemResult; +use Bitrix24\SDK\Services\Biconnector\Dataset\Service\Dataset; +use Bitrix24\SDK\Services\Biconnector\Source\Service\Source; +use Bitrix24\SDK\Tests\CustomAssertions\CustomBitrix24Assertions; +use Bitrix24\SDK\Tests\Integration\Factory; +use Faker\Generator; +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\TestCase; +use Faker; + +#[CoversClass(Dataset::class)] +class DatasetTest extends TestCase +{ + use CustomBitrix24Assertions; + + private Dataset $datasetService; + + private Source $sourceService; + + private Connector $connectorService; + + private Generator $faker; + + private int $connectorId; + + private int $sourceId; + + /** + * @throws BaseException + * @throws TransportException + */ + #[\Override] + protected function setUp(): void + { + $biconnectorServiceBuilder = Factory::getServiceBuilder(true)->getBiconnectorScope(); + $this->datasetService = $biconnectorServiceBuilder->dataset(); + $this->sourceService = $biconnectorServiceBuilder->source(); + $this->connectorService = $biconnectorServiceBuilder->connector(); + $this->faker = Faker\Factory::create(); + + // Create a connector and source to use for dataset tests + $this->connectorId = $this->connectorService->add($this->makeConnectorFields( + 'connector-for-dataset-' . $this->faker->uuid() + ))->getId(); + + $this->sourceId = $this->sourceService->add([ + 'title' => 'source-for-dataset-' . $this->faker->uuid(), + 'connectorId' => $this->connectorId, + 'settings' => ['token' => 'test-token-' . $this->faker->uuid()], + ])->getId(); + } + + /** + * @throws BaseException + * @throws TransportException + */ + #[\Override] + protected function tearDown(): void + { + try { + $this->sourceService->delete($this->sourceId); + } catch (\Throwable) { + } + + try { + $this->connectorService->delete($this->connectorId); + } catch (\Throwable) { + } + } + + private function makeConnectorFields(string $title): array + { + return [ + 'title' => $title, + 'logo' => 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjIiIGhlaWdodD0iMjIiIHZpZXdCb3g9IjAgMCAyMiAyMiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KCTxjaXJjbGUgY3g9IjExIiBjeT0iMTEiIHI9IjEwIiBmaWxsPSIjRkYzQjNCIiAvPgoJPHRleHQgeD0iMTEiIHk9IjEzIiBmb250LWZhbWlseT0iQXJpYWwsIHNhbnMtc2VyaWYiIGZvbnQtc2l6ZT0iNiIgZmlsbD0iI0ZGRkZGRiIgdGV4dC1hbmNob3I9Im1pZGRsZSIgZm9udC13ZWlnaHQ9ImJvbGQiPlJFU1Q8L3RleHQ+Cjwvc3ZnPg==', + 'urlCheck' => 'https://digitmind8080.cloudpub.ru/?connection_type=mysql&action=check', + 'urlTableList' => 'https://digitmind8080.cloudpub.ru/?connection_type=mysql&action=table_list', + 'urlTableDescription' => 'https://digitmind8080.cloudpub.ru/?connection_type=mysql&action=table_description', + 'urlData' => 'https://digitmind8080.cloudpub.ru/?connection_type=mysql&action=data', + 'settings' => [ + ['name' => 'Host', 'type' => 'STRING', 'code' => 'host'], + ['name' => 'Port', 'type' => 'STRING', 'code' => 'port'], + ['name' => 'Database', 'type' => 'STRING', 'code' => 'database'], + ['name' => 'Username', 'type' => 'STRING', 'code' => 'username'], + ['name' => 'Password', 'type' => 'STRING', 'code' => 'password'], + ], + ]; + } + + /** + * Returns the minimum set of required fields to create a dataset. + */ + private function makeDatasetFields(string $name): array + { + return [ + 'sourceId' => $this->sourceId, + 'name' => $name, + 'externalName' => 'ext_' . $name, + 'externalCode' => 'code_' . $name, + 'fields' => [ + ['type' => 'int', 'name' => 'ID', 'externalCode' => 'ID'], + ['type' => 'string', 'name' => 'NAME', 'externalCode' => 'NAME'], + ], + ]; + } + + /** + * @throws BaseException + * @throws TransportException + */ + public function testAdd(): void + { + $name = 'ds' . substr(str_replace('-', '', $this->faker->uuid()), 0, 20); + $id = $this->datasetService->add($this->makeDatasetFields($name))->getId(); + + self::assertGreaterThanOrEqual(1, $id); + + // Cleanup + $this->datasetService->delete($id); + } + + /** + * @throws BaseException + * @throws TransportException + */ + public function testGet(): void + { + $name = 'ds' . substr(str_replace('-', '', $this->faker->uuid()), 0, 20); + $id = $this->datasetService->add($this->makeDatasetFields($name))->getId(); + + $datasetItemResult = $this->datasetService->get($id)->dataset(); + self::assertInstanceOf(DatasetItemResult::class, $datasetItemResult); + self::assertEquals($id, $datasetItemResult->id); + self::assertEquals($name, $datasetItemResult->name); + + // Cleanup + $this->datasetService->delete($id); + } + + /** + * @throws BaseException + * @throws TransportException + */ + public function testList(): void + { + $name = 'ds' . substr(str_replace('-', '', $this->faker->uuid()), 0, 20); + $id = $this->datasetService->add($this->makeDatasetFields($name))->getId(); + + $list = $this->datasetService->list()->getDatasets(); + self::assertIsArray($list); + self::assertGreaterThanOrEqual(1, count($list)); + + // Cleanup + $this->datasetService->delete($id); + } + + /** + * @throws BaseException + * @throws TransportException + */ + public function testUpdate(): void + { + $name = 'ds' . substr(str_replace('-', '', $this->faker->uuid()), 0, 20); + $id = $this->datasetService->add($this->makeDatasetFields($name))->getId(); + + $newDescription = 'Updated description ' . $this->faker->sentence(); + self::assertTrue( + $this->datasetService->update($id, ['description' => $newDescription])->isSuccess() + ); + + self::assertEquals($newDescription, $this->datasetService->get($id)->dataset()->description); + + // Cleanup + $this->datasetService->delete($id); + } + + /** + * @throws BaseException + * @throws TransportException + */ + public function testDelete(): void + { + $name = 'ds' . substr(str_replace('-', '', $this->faker->uuid()), 0, 20); + $id = $this->datasetService->add($this->makeDatasetFields($name))->getId(); + + self::assertTrue($this->datasetService->delete($id)->isSuccess()); + } + + /** + * @throws BaseException + * @throws TransportException + */ + public function testFields(): void + { + $fields = $this->datasetService->fields()->getFieldsDescription(); + self::assertIsArray($fields); + self::assertNotEmpty($fields); + } + + /** + * @throws BaseException + * @throws TransportException + */ + public function testUpdateFields(): void + { + $name = 'ds' . substr(str_replace('-', '', $this->faker->uuid()), 0, 20); + $id = $this->datasetService->add($this->makeDatasetFields($name))->getId(); + + // Get current field IDs + $datasetItemResult = $this->datasetService->get($id)->dataset(); + $existingFields = $datasetItemResult->fields ?? []; + $fieldId = $existingFields[0]['id'] ?? null; + + // Add a new field + $updatedDatasetResult = $this->datasetService->updateFields( + $id, + [['type' => 'string', 'name' => 'EXTRA', 'externalCode' => 'EXTRA']], + $fieldId !== null ? [['id' => $fieldId, 'visible' => false]] : [], + [] + ); + + self::assertTrue($updatedDatasetResult->isSuccess()); + + // Cleanup + $this->datasetService->delete($id); + } + + /** + * @throws BaseException + * @throws TransportException + */ + public function testCount(): void + { + $countBefore = $this->datasetService->count(); + + $name = 'ds' . substr(str_replace('-', '', $this->faker->uuid()), 0, 20); + $id = $this->datasetService->add($this->makeDatasetFields($name))->getId(); + + $countAfter = $this->datasetService->count(); + self::assertEquals($countBefore + 1, $countAfter); + + // Cleanup + $this->datasetService->delete($id); + } +} + From 9f8c651baed4424ea2e1046150267dda58717e38 Mon Sep 17 00:00:00 2001 From: Dmitriy Ignatenko Date: Tue, 19 May 2026 18:00:01 +0400 Subject: [PATCH 12/18] Remove biconnector task plan --- .tasks/469/plan.md | 124 --------------------------------------------- 1 file changed, 124 deletions(-) delete mode 100644 .tasks/469/plan.md diff --git a/.tasks/469/plan.md b/.tasks/469/plan.md deleted file mode 100644 index dbf07cc4..00000000 --- a/.tasks/469/plan.md +++ /dev/null @@ -1,124 +0,0 @@ -# Plan: Add `biconnector.source.*` service support (issue #469) - -## Context - -The `biconnector` scope already has `Connector` (add/get/list/update/delete/fields/count) implemented. -Now we add the `Source` entity, covering six REST methods: - -| REST method | Response envelope | Notes | -|------------------------------|-------------------------------|----------------------------| -| `biconnector.source.add` | `result.id` (int) | Fields wrapped in `fields` | -| `biconnector.source.update` | `result` = `true` | | -| `biconnector.source.get` | `result.item.connection` + root connectorId/settings | Nested; must be flattened | -| `biconnector.source.list` | `result` = flat array | Uses 'page' pagination (same as connector.list) | -| `biconnector.source.delete` | `result` = `true` | | -| `biconnector.source.fields` | `result.fields` array | Returns `FieldsResult` | - -### Key API notes -- `biconnector.source.delete` uses lowercase `id` parameter (not `ID`) -- `biconnector.source.get` returns nested: `result.item.connection.{id,type,code,title,...}` plus `result.item.connectorId` and `result.item.settings` -- `SourceResult` must flatten `connection` + root fields into a single `SourceItemResult` -- `biconnector.source.fields` lists: id, title, type, code, description, active, dateCreate, dateUpdate, createdById, updatedById, connectorId, settings (all camelCase) - -### SourceItemResult field types -| Field | Bitrix24 type | PHP type | -|---------------|---------------|-------------------| -| id | integer | int | -| title | string | string | -| type | string | string\|null | -| code | string | string\|null | -| description | string | string\|null | -| active | boolean | bool\|null | -| dateCreate | datetime | CarbonImmutable | -| dateUpdate | datetime | CarbonImmutable | -| createdById | integer | int | -| updatedById | integer | int | -| connectorId | integer | int | -| settings | array | array\|null | - ---- - -## Files to Create - -### 1. `src/Services/Biconnector/Source/Result/SourceItemResult.php` -All 12 fields annotated with @property-read, `__get` with match expression. - -### 2. `src/Services/Biconnector/Source/Result/SourceResult.php` -Reads `result.item.connection` merged with root fields (connectorId, settings), returns `SourceItemResult`. - -### 3. `src/Services/Biconnector/Source/Result/SourcesResult.php` -Reads flat `result` array, returns `SourceItemResult[]` via `getSources()`. - -### 4. `src/Services/Biconnector/Source/Result/AddedSourceResult.php` -Extends `AddedItemResult`, reads `result['id']`. - -### 5. `src/Services/Biconnector/Source/Result/AddedSourceBatchResult.php` -Extends `AddedItemBatchResult`, reads `result['id']`. - -### 6. `src/Services/Biconnector/Source/Result/UpdatedSourceResult.php` -Extends `UpdatedItemResult`, `isSuccess()` returns `(bool)$result`. - -### 7. `src/Services/Biconnector/Source/Result/UpdatedSourceBatchResult.php` -Extends `UpdatedItemBatchResult`, `isSuccess()` returns `(bool)$result`. - -### 8. `src/Services/Biconnector/Source/Result/DeletedSourceResult.php` -Extends `DeletedItemResult`, `isSuccess()` returns `(bool)$result`. - -### 9. `src/Services/Biconnector/Source/Result/DeletedSourceBatchResult.php` -Extends `DeletedItemBatchResult`, `isSuccess()` returns `(bool)$result`. - -### 10. `src/Services/Biconnector/Source/Batch.php` -Extends `\Bitrix24\SDK\Core\Batch`, overrides `determineKeyId` → 'id' and `deleteEntityItems` → lowercase 'id'. - -### 11. `src/Services/Biconnector/Source/Service/Source.php` -Methods: add, update, get, list, delete, fields, count. - -### 12. `src/Services/Biconnector/Source/Service/Batch.php` -Batch service: list, add, update, delete. - -### 13. `tests/Unit/Services/Biconnector/SourceServiceBuilderTest.php` -Unit test: sourceService is cached. - -### 14. `tests/Integration/Services/Biconnector/Source/Service/SourceTest.php` -Integration tests: add, get, list, update, delete, fields, count. - -### 15. `tests/Integration/Services/Biconnector/Source/Service/BatchTest.php` -Integration tests: batchList, batchAdd, batchDelete. - -### 16. `tests/Integration/Services/Biconnector/Source/Result/SourceItemResultAnnotationsTest.php` -Annotation tests: testAllSystemFieldsAnnotated, testAllSystemFieldsHasValidTypeAnnotation. - ---- - -## Files to Modify - -### 1. `src/Services/Biconnector/BiconnectorServiceBuilder.php` -Add `source(): Source` method using `SourceBatch` custom batch. - -### 2. `phpunit.xml.dist` -Add `integration_tests_biconnector_source` test suite. - -### 3. `Makefile` -Add `test-integration-biconnector-source` target. - -### 4. `CHANGELOG.md` -Add entry under `## 3.2.0 – UNRELEASED` → `### Added`. - ---- - -## Deptrac compliance -New namespaces under `Biconnector\Source` follow the same layer as `Biconnector\Connector` — no new cross-layer dependencies. - ---- - -## Verification - -```bash -make lint-cs-fixer -make lint-rector -make lint-phpstan -make lint-deptrac -make test-unit -make test-integration-biconnector-source -``` - From ceaa9cc0fbafcebb86c849ad02ab37824d44d34d Mon Sep 17 00:00:00 2001 From: Dmitriy Ignatenko Date: Tue, 19 May 2026 18:07:59 +0400 Subject: [PATCH 13/18] Fix list method parameters and use lowercase id for biconnector.dataset methods --- .../Biconnector/BiconnectorServiceBuilder.php | 11 +- src/Services/Biconnector/Dataset/Batch.php | 266 ++++++++++++++++++ .../Biconnector/Dataset/Service/Dataset.php | 4 +- 3 files changed, 279 insertions(+), 2 deletions(-) create mode 100644 src/Services/Biconnector/Dataset/Batch.php diff --git a/src/Services/Biconnector/BiconnectorServiceBuilder.php b/src/Services/Biconnector/BiconnectorServiceBuilder.php index 79680baf..925f9da9 100644 --- a/src/Services/Biconnector/BiconnectorServiceBuilder.php +++ b/src/Services/Biconnector/BiconnectorServiceBuilder.php @@ -19,6 +19,7 @@ use Bitrix24\SDK\Services\Biconnector\Connector\Batch as ConnectorBatch; use Bitrix24\SDK\Services\Biconnector\Connector\Service\Batch; use Bitrix24\SDK\Services\Biconnector\Connector\Service\Connector; +use Bitrix24\SDK\Services\Biconnector\Dataset\Batch as DatasetBatch; use Bitrix24\SDK\Services\Biconnector\Dataset\Service\Batch as DatasetServiceBatch; use Bitrix24\SDK\Services\Biconnector\Dataset\Service\Dataset; use Bitrix24\SDK\Services\Biconnector\Source\Batch as SourceBatch; @@ -55,12 +56,20 @@ public function connector(): Connector /** * Get the Dataset service + * + * Uses a specialized DatasetBatch to handle biconnector.dataset.* REST API differences: + * - list uses 'page' parameter (page number) instead of standard 'start' (offset) + * - delete uses lowercase 'id' instead of 'ID' */ public function dataset(): Dataset { if (!isset($this->serviceCache[__METHOD__])) { + $datasetBatch = new DatasetBatch( + $this->core, + $this->log + ); $this->serviceCache[__METHOD__] = new Dataset( - new DatasetServiceBatch($this->batch, $this->log), + new DatasetServiceBatch($datasetBatch, $this->log), $this->core, $this->log ); diff --git a/src/Services/Biconnector/Dataset/Batch.php b/src/Services/Biconnector/Dataset/Batch.php new file mode 100644 index 00000000..fea884f6 --- /dev/null +++ b/src/Services/Biconnector/Dataset/Batch.php @@ -0,0 +1,266 @@ + + * + * For the full copyright and license information, please view the MIT-LICENSE.txt + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Bitrix24\SDK\Services\Biconnector\Dataset; + +use Bitrix24\SDK\Core\Exceptions\BaseException; +use Bitrix24\SDK\Core\Exceptions\InvalidArgumentException; +use Bitrix24\SDK\Core\Exceptions\TransportException; +use Bitrix24\SDK\Core\Response\DTO\ResponseData; +use Generator; + +/** + * Class Batch + * + * Overrides base Batch to handle parameter naming differences in biconnector.dataset.* REST methods: + * - list uses 'page' (page number, 50 records per page) instead of 'start' (offset) for pagination + * - delete uses lowercase 'id' instead of 'ID' + * + * @see https://apidocs.bitrix24.com/api-reference/biconnector/dataset/biconnector-dataset-list.html + * @see https://apidocs.bitrix24.com/api-reference/biconnector/dataset/biconnector-dataset-delete.html + */ +class Batch extends \Bitrix24\SDK\Core\Batch +{ + /** + * Determines the ID key — lowercase 'id' for biconnector dataset + */ + #[\Override] + protected function determineKeyId(string $apiMethod, ?array $additionalParameters): string + { + return 'id'; + } + + /** + * Delete entity items with batch call using lowercase 'id' parameter + * + * @param int[] $entityItemId + * @param array|null $additionalParameters + * + * @return Generator|ResponseData[] + * @throws BaseException + */ + #[\Override] + public function deleteEntityItems( + string $apiMethod, + array $entityItemId, + ?array $additionalParameters = null + ): Generator { + $this->logger->debug( + 'deleteEntityItems.start', + [ + 'apiMethod' => $apiMethod, + 'entityItems' => $entityItemId, + 'additionalParameters' => $additionalParameters, + ] + ); + + try { + $this->clearCommands(); + foreach ($entityItemId as $cnt => $itemId) { + if (!is_int($itemId)) { + throw new InvalidArgumentException( + sprintf( + 'invalid type «%s» of dataset id «%s» at position %s, dataset id must be integer type', + gettype($itemId), + $itemId, + $cnt + ) + ); + } + + $this->registerCommand($apiMethod, ['id' => $itemId]); + } + + foreach ($this->getTraversable(true) as $cnt => $deletedItemResult) { + yield $cnt => $deletedItemResult; + } + } catch (InvalidArgumentException $exception) { + $errorMessage = sprintf('batch delete dataset items: %s', $exception->getMessage()); + $this->logger->error( + $errorMessage, + [ + 'trace' => $exception->getTrace(), + ] + ); + throw $exception; + } catch (\Throwable $exception) { + $errorMessage = sprintf('batch delete dataset items: %s', $exception->getMessage()); + $this->logger->error( + $errorMessage, + [ + 'trace' => $exception->getTrace(), + ] + ); + + throw new BaseException($errorMessage, $exception->getCode(), $exception); + } + + $this->logger->debug('deleteEntityItems.finish'); + } + + /** + * Get traversable list using page-based pagination. + * + * The biconnector.dataset.list method uses 'page' parameter (page number, 50 records per page) + * instead of the standard 'start' (offset) parameter used by most other REST methods. + * + * @link https://apidocs.bitrix24.com/api-reference/biconnector/dataset/biconnector-dataset-list.html + * + * @param array $order + * @param array $filter + * @param array $select + * + * @return Generator + * @throws BaseException + * @throws TransportException + */ + #[\Override] + public function getTraversableList( + string $apiMethod, + ?array $order = [], + ?array $filter = [], + ?array $select = [], + ?int $limit = null, + ?array $additionalParameters = null + ): Generator { + yield from $this->getTraversableListWithCount( + $apiMethod, + $order ?? [], + $filter ?? [], + $select ?? [], + $limit, + $additionalParameters + ); + } + + /** + * Get traversable list using page-based pagination (page number, 50 records per page). + * + * The biconnector.dataset.list method accepts 'page' parameter instead of 'start'. + * Page 1 returns items 1–50, page 2 returns items 51–100, etc. + * + * @link https://apidocs.bitrix24.com/api-reference/biconnector/dataset/biconnector-dataset-list.html + * + * @param array $order + * @param array $filter + * @param array $select + * + * @return Generator + * @throws BaseException + * @throws TransportException + */ + #[\Override] + public function getTraversableListWithCount( + string $apiMethod, + array $order, + array $filter, + array $select, + ?int $limit = null, + ?array $additionalParameters = null + ): Generator { + $this->logger->debug( + 'getTraversableListWithCount.start', + [ + 'apiMethod' => $apiMethod, + 'order' => $order, + 'filter' => $filter, + 'select' => $select, + 'limit' => $limit, + 'additionalParameters' => $additionalParameters, + ] + ); + + $this->clearCommands(); + + // Fetch first page to determine total count + $params = [ + 'order' => $order, + 'filter' => $filter, + 'select' => $select, + 'page' => 1, + ]; + + if ($additionalParameters !== null) { + $params = array_merge($params, $additionalParameters); + } + + $response = $this->core->call($apiMethod, $params); + $total = $response->getResponseData()->getPagination()->getTotal(); + + $this->logger->debug( + 'getTraversableListWithCount.totalElementsCount', + [ + 'totalElementsCount' => $total, + ] + ); + + if ($total <= self::MAX_ELEMENTS_IN_PAGE) { + $elementsCounter = 0; + foreach ($response->getResponseData()->getResult() as $item) { + $elementsCounter++; + if ($limit !== null && $elementsCounter > $limit) { + return; + } + + yield $item; + } + + return; + } + + // Register batch commands for all pages + $totalPages = (int)ceil($total / self::MAX_ELEMENTS_IN_PAGE); + for ($page = 1; $page <= $totalPages; $page++) { + $pageParams = [ + 'order' => $order, + 'filter' => $filter, + 'select' => $select, + 'page' => $page, + ]; + + if ($additionalParameters !== null) { + $pageParams = array_merge($pageParams, $additionalParameters); + } + + $this->registerCommand($apiMethod, $pageParams); + + if ($limit !== null && $limit < $page * self::MAX_ELEMENTS_IN_PAGE) { + break; + } + } + + $this->logger->debug( + 'getTraversableListWithCount.commandsRegistered', + [ + 'commandsCount' => $this->commands->count(), + 'totalItemsToSelect' => $total, + ] + ); + + $elementsCounter = 0; + foreach ($this->getTraversable(true) as $queryResultData) { + $resultElements = $this->extractElementsFromBatchResult($queryResultData, false); + foreach ($resultElements as $resultElement) { + ++$elementsCounter; + if ($limit !== null && $elementsCounter > $limit) { + return; + } + + yield $resultElement; + } + } + + $this->logger->debug('getTraversableListWithCount.finish'); + } +} + diff --git a/src/Services/Biconnector/Dataset/Service/Dataset.php b/src/Services/Biconnector/Dataset/Service/Dataset.php index dc069452..3767c851 100644 --- a/src/Services/Biconnector/Dataset/Service/Dataset.php +++ b/src/Services/Biconnector/Dataset/Service/Dataset.php @@ -136,6 +136,7 @@ public function get(int $id): DatasetResult * @param array $order - sort fields, e.g. ['dateCreate' => 'DESC'] * @param array $filter - filter fields * @param array $select - fields to include in the result + * @param int $page - page number for pagination (page size is 50 records per page) * * @throws BaseException * @throws TransportException @@ -145,7 +146,7 @@ public function get(int $id): DatasetResult 'https://apidocs.bitrix24.com/api-reference/biconnector/dataset/biconnector-dataset-list.html', 'Get a list of datasets' )] - public function list(array $order = [], array $filter = [], array $select = []): DatasetsResult + public function list(array $order = [], array $filter = [], array $select = [], int $page = 1): DatasetsResult { return new DatasetsResult( $this->core->call( @@ -154,6 +155,7 @@ public function list(array $order = [], array $filter = [], array $select = []): 'order' => $order, 'filter' => $filter, 'select' => $select, + 'page' => $page, ] ) ); From 61f7dcc42f830969cec397b7754e1f7985387a4a Mon Sep 17 00:00:00 2001 From: Dmitriy Ignatenko Date: Wed, 20 May 2026 17:23:24 +0400 Subject: [PATCH 14/18] Add the values to the fields of the source and dataset methods --- .../Result/DatasetItemResultAnnotationsTest.php | 14 ++++++++++---- .../Biconnector/Dataset/Service/BatchTest.php | 14 ++++++++++---- .../Biconnector/Dataset/Service/DatasetTest.php | 14 ++++++++++---- .../Result/SourceItemResultAnnotationsTest.php | 8 +++++++- .../Biconnector/Source/Service/BatchTest.php | 6 +++++- .../Biconnector/Source/Service/SourceTest.php | 6 +++++- 6 files changed, 47 insertions(+), 15 deletions(-) diff --git a/tests/Integration/Services/Biconnector/Dataset/Result/DatasetItemResultAnnotationsTest.php b/tests/Integration/Services/Biconnector/Dataset/Result/DatasetItemResultAnnotationsTest.php index 7f8528eb..d178cfd1 100644 --- a/tests/Integration/Services/Biconnector/Dataset/Result/DatasetItemResultAnnotationsTest.php +++ b/tests/Integration/Services/Biconnector/Dataset/Result/DatasetItemResultAnnotationsTest.php @@ -68,18 +68,24 @@ protected function setUp(): void $this->sourceId = $this->sourceService->add([ 'title' => 'source-annotations-' . $this->faker->uuid(), 'connectorId' => $this->connectorId, - 'settings' => ['token' => 'test-token-' . $this->faker->uuid()], + 'settings' => [ + 'host' => '172.18.0.2', + 'port' => '3306', + 'database' => 'customer_db', + 'username' => 'testuser', + 'password' => 'testpass123', + ], ])->getId(); $name = 'ds' . substr(str_replace('-', '', $this->faker->uuid()), 0, 20); $this->datasetId = $this->datasetService->add([ 'sourceId' => $this->sourceId, 'name' => $name, - 'externalName' => 'ext_' . $name, - 'externalCode' => 'code_' . $name, + 'externalName' => 'orders', + 'externalCode' => 'orders', 'fields' => [ ['type' => 'int', 'name' => 'ID', 'externalCode' => 'ID'], - ['type' => 'string', 'name' => 'NAME', 'externalCode' => 'NAME'], + ['type' => 'double', 'name' => 'TAX_AMOUNT', 'externalCode' => 'TAX_AMOUNT'], ], ])->getId(); } diff --git a/tests/Integration/Services/Biconnector/Dataset/Service/BatchTest.php b/tests/Integration/Services/Biconnector/Dataset/Service/BatchTest.php index 8b24a77b..6de92aff 100644 --- a/tests/Integration/Services/Biconnector/Dataset/Service/BatchTest.php +++ b/tests/Integration/Services/Biconnector/Dataset/Service/BatchTest.php @@ -62,7 +62,13 @@ protected function setUp(): void $this->sourceId = $this->sourceService->add([ 'title' => 'source-for-dataset-batch-' . $this->faker->uuid(), 'connectorId' => $this->connectorId, - 'settings' => ['token' => 'test-token-' . $this->faker->uuid()], + 'settings' => [ + 'host' => '172.18.0.2', + 'port' => '3306', + 'database' => 'customer_db', + 'username' => 'testuser', + 'password' => 'testpass123', + ], ])->getId(); } @@ -108,11 +114,11 @@ private function makeDatasetFields(string $name): array return [ 'sourceId' => $this->sourceId, 'name' => $name, - 'externalName' => 'ext_' . $name, - 'externalCode' => 'code_' . $name, + 'externalName' => 'orders', + 'externalCode' => 'orders', 'fields' => [ ['type' => 'int', 'name' => 'ID', 'externalCode' => 'ID'], - ['type' => 'string', 'name' => 'NAME', 'externalCode' => 'NAME'], + ['type' => 'double', 'name' => 'TAX_AMOUNT', 'externalCode' => 'TAX_AMOUNT'], ], ]; } diff --git a/tests/Integration/Services/Biconnector/Dataset/Service/DatasetTest.php b/tests/Integration/Services/Biconnector/Dataset/Service/DatasetTest.php index eadc27b4..a5cf4814 100644 --- a/tests/Integration/Services/Biconnector/Dataset/Service/DatasetTest.php +++ b/tests/Integration/Services/Biconnector/Dataset/Service/DatasetTest.php @@ -64,7 +64,13 @@ protected function setUp(): void $this->sourceId = $this->sourceService->add([ 'title' => 'source-for-dataset-' . $this->faker->uuid(), 'connectorId' => $this->connectorId, - 'settings' => ['token' => 'test-token-' . $this->faker->uuid()], + 'settings' => [ + 'host' => '172.18.0.2', + 'port' => '3306', + 'database' => 'customer_db', + 'username' => 'testuser', + 'password' => 'testpass123', + ], ])->getId(); } @@ -113,11 +119,11 @@ private function makeDatasetFields(string $name): array return [ 'sourceId' => $this->sourceId, 'name' => $name, - 'externalName' => 'ext_' . $name, - 'externalCode' => 'code_' . $name, + 'externalName' => 'orders', + 'externalCode' => 'orders', 'fields' => [ ['type' => 'int', 'name' => 'ID', 'externalCode' => 'ID'], - ['type' => 'string', 'name' => 'NAME', 'externalCode' => 'NAME'], + ['type' => 'double', 'name' => 'TAX_AMOUNT', 'externalCode' => 'TAX_AMOUNT'], ], ]; } diff --git a/tests/Integration/Services/Biconnector/Source/Result/SourceItemResultAnnotationsTest.php b/tests/Integration/Services/Biconnector/Source/Result/SourceItemResultAnnotationsTest.php index ad956827..2261defd 100644 --- a/tests/Integration/Services/Biconnector/Source/Result/SourceItemResultAnnotationsTest.php +++ b/tests/Integration/Services/Biconnector/Source/Result/SourceItemResultAnnotationsTest.php @@ -64,7 +64,13 @@ protected function setUp(): void $this->sourceId = $this->sourceService->add([ 'title' => 'source-annotations-' . $this->faker->uuid(), 'connectorId' => $this->connectorId, - 'settings' => ['token' => 'test-token-' . $this->faker->uuid()], + 'settings' => [ + 'host' => '172.18.0.2', + 'port' => '3306', + 'database' => 'customer_db', + 'username' => 'testuser', + 'password' => 'testpass123', + ], ])->getId(); } diff --git a/tests/Integration/Services/Biconnector/Source/Service/BatchTest.php b/tests/Integration/Services/Biconnector/Source/Service/BatchTest.php index 92552633..b79a03ac 100644 --- a/tests/Integration/Services/Biconnector/Source/Service/BatchTest.php +++ b/tests/Integration/Services/Biconnector/Source/Service/BatchTest.php @@ -115,7 +115,11 @@ private function makeSourceFields(string $title): array 'title' => $title, 'connectorId' => $this->connectorId, 'settings' => [ - 'token' => 'test-token-' . $this->faker->uuid(), + 'host' => '172.18.0.2', + 'port' => '3306', + 'database' => 'customer_db', + 'username' => 'testuser', + 'password' => 'testpass123', ], ]; } diff --git a/tests/Integration/Services/Biconnector/Source/Service/SourceTest.php b/tests/Integration/Services/Biconnector/Source/Service/SourceTest.php index e02b5c5a..d4e6b077 100644 --- a/tests/Integration/Services/Biconnector/Source/Service/SourceTest.php +++ b/tests/Integration/Services/Biconnector/Source/Service/SourceTest.php @@ -124,7 +124,11 @@ private function makeSourceFields(string $title): array 'title' => $title, 'connectorId' => $this->connectorId, 'settings' => [ - 'token' => 'test-token-' . $this->faker->uuid(), + 'host' => '172.18.0.2', + 'port' => '3306', + 'database' => 'customer_db', + 'username' => 'testuser', + 'password' => 'testpass123', ], ]; } From c80b2a0560132f337e894acc83da781d483fbab6 Mon Sep 17 00:00:00 2001 From: Dmitriy Ignatenko Date: Wed, 20 May 2026 20:40:32 +0400 Subject: [PATCH 15/18] Change the values to the fields of the dataset methods --- .../Dataset/Result/DatasetItemResultAnnotationsTest.php | 5 ++--- .../Services/Biconnector/Dataset/Service/BatchTest.php | 5 ++--- .../Services/Biconnector/Dataset/Service/DatasetTest.php | 5 ++--- 3 files changed, 6 insertions(+), 9 deletions(-) diff --git a/tests/Integration/Services/Biconnector/Dataset/Result/DatasetItemResultAnnotationsTest.php b/tests/Integration/Services/Biconnector/Dataset/Result/DatasetItemResultAnnotationsTest.php index d178cfd1..cf2bd662 100644 --- a/tests/Integration/Services/Biconnector/Dataset/Result/DatasetItemResultAnnotationsTest.php +++ b/tests/Integration/Services/Biconnector/Dataset/Result/DatasetItemResultAnnotationsTest.php @@ -81,11 +81,10 @@ protected function setUp(): void $this->datasetId = $this->datasetService->add([ 'sourceId' => $this->sourceId, 'name' => $name, - 'externalName' => 'orders', - 'externalCode' => 'orders', + 'externalName' => 'order_items', + 'externalCode' => 'order_items', 'fields' => [ ['type' => 'int', 'name' => 'ID', 'externalCode' => 'ID'], - ['type' => 'double', 'name' => 'TAX_AMOUNT', 'externalCode' => 'TAX_AMOUNT'], ], ])->getId(); } diff --git a/tests/Integration/Services/Biconnector/Dataset/Service/BatchTest.php b/tests/Integration/Services/Biconnector/Dataset/Service/BatchTest.php index 6de92aff..73577213 100644 --- a/tests/Integration/Services/Biconnector/Dataset/Service/BatchTest.php +++ b/tests/Integration/Services/Biconnector/Dataset/Service/BatchTest.php @@ -114,11 +114,10 @@ private function makeDatasetFields(string $name): array return [ 'sourceId' => $this->sourceId, 'name' => $name, - 'externalName' => 'orders', - 'externalCode' => 'orders', + 'externalName' => 'order_items', + 'externalCode' => 'order_items', 'fields' => [ ['type' => 'int', 'name' => 'ID', 'externalCode' => 'ID'], - ['type' => 'double', 'name' => 'TAX_AMOUNT', 'externalCode' => 'TAX_AMOUNT'], ], ]; } diff --git a/tests/Integration/Services/Biconnector/Dataset/Service/DatasetTest.php b/tests/Integration/Services/Biconnector/Dataset/Service/DatasetTest.php index a5cf4814..25d87c8f 100644 --- a/tests/Integration/Services/Biconnector/Dataset/Service/DatasetTest.php +++ b/tests/Integration/Services/Biconnector/Dataset/Service/DatasetTest.php @@ -119,11 +119,10 @@ private function makeDatasetFields(string $name): array return [ 'sourceId' => $this->sourceId, 'name' => $name, - 'externalName' => 'orders', - 'externalCode' => 'orders', + 'externalName' => 'order_items', + 'externalCode' => 'order_items', 'fields' => [ ['type' => 'int', 'name' => 'ID', 'externalCode' => 'ID'], - ['type' => 'double', 'name' => 'TAX_AMOUNT', 'externalCode' => 'TAX_AMOUNT'], ], ]; } From 19d0d858174111d4cc9b5417e6143530ba7ffd0c Mon Sep 17 00:00:00 2001 From: Dmitriy Ignatenko Date: Wed, 20 May 2026 21:39:51 +0400 Subject: [PATCH 16/18] Skip biconnector tests that require additional external services --- .../Connector/Service/BatchTest.php | 8 +-- .../Connector/Service/ConnectorTest.php | 8 +-- .../DatasetItemResultAnnotationsTest.php | 20 +++++++- .../Biconnector/Dataset/Service/BatchTest.php | 25 +++++++++- .../Dataset/Service/DatasetTest.php | 50 ++++++++++++++++++- .../SourceItemResultAnnotationsTest.php | 19 +++++++ .../Biconnector/Source/Service/BatchTest.php | 15 ++++++ .../Biconnector/Source/Service/SourceTest.php | 35 +++++++++++++ 8 files changed, 169 insertions(+), 11 deletions(-) diff --git a/tests/Integration/Services/Biconnector/Connector/Service/BatchTest.php b/tests/Integration/Services/Biconnector/Connector/Service/BatchTest.php index e1e700a4..8f1e07fd 100644 --- a/tests/Integration/Services/Biconnector/Connector/Service/BatchTest.php +++ b/tests/Integration/Services/Biconnector/Connector/Service/BatchTest.php @@ -60,10 +60,10 @@ private function makeConnectorFields(string $title): array return [ 'title' => $title, 'logo' => 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjIiIGhlaWdodD0iMjIiIHZpZXdCb3g9IjAgMCAyMiAyMiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KCTxjaXJjbGUgY3g9IjExIiBjeT0iMTEiIHI9IjEwIiBmaWxsPSIjRkYzQjNCIiAvPgoJPHRleHQgeD0iMTEiIHk9IjEzIiBmb250LWZhbWlseT0iQXJpYWwsIHNhbnMtc2VyaWYiIGZvbnQtc2l6ZT0iNiIgZmlsbD0iI0ZGRkZGRiIgdGV4dC1hbmNob3I9Im1pZGRsZSIgZm9udC13ZWlnaHQ9ImJvbGQiPlJFU1Q8L3RleHQ+Cjwvc3ZnPg==', - 'urlCheck' => 'https://digitmind8080.cloudpub.ru/?connection_type=mysql&action=check', - 'urlTableList' => 'https://digitmind8080.cloudpub.ru/?connection_type=mysql&action=table_list', - 'urlTableDescription' => 'https://digitmind8080.cloudpub.ru/?connection_type=mysql&action=table_description', - 'urlData' => 'https://digitmind8080.cloudpub.ru/?connection_type=mysql&action=data', + 'urlCheck' => 'https://example.com/api/check', + 'urlTableList' => 'https://example.com/api/table_list', + 'urlTableDescription' => 'https://example.com/api/table_description', + 'urlData' => 'https://example.com/api/data', 'settings' => [ [ 'name' => 'Host', diff --git a/tests/Integration/Services/Biconnector/Connector/Service/ConnectorTest.php b/tests/Integration/Services/Biconnector/Connector/Service/ConnectorTest.php index 99478351..fd4ffab7 100644 --- a/tests/Integration/Services/Biconnector/Connector/Service/ConnectorTest.php +++ b/tests/Integration/Services/Biconnector/Connector/Service/ConnectorTest.php @@ -62,10 +62,10 @@ private function makeConnectorFields(string $title): array return [ 'title' => $title, 'logo' => 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjIiIGhlaWdodD0iMjIiIHZpZXdCb3g9IjAgMCAyMiAyMiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KCTxjaXJjbGUgY3g9IjExIiBjeT0iMTEiIHI9IjEwIiBmaWxsPSIjRkYzQjNCIiAvPgoJPHRleHQgeD0iMTEiIHk9IjEzIiBmb250LWZhbWlseT0iQXJpYWwsIHNhbnMtc2VyaWYiIGZvbnQtc2l6ZT0iNiIgZmlsbD0iI0ZGRkZGRiIgdGV4dC1hbmNob3I9Im1pZGRsZSIgZm9udC13ZWlnaHQ9ImJvbGQiPlJFU1Q8L3RleHQ+Cjwvc3ZnPg==', - 'urlCheck' => 'https://digitmind8080.cloudpub.ru/?connection_type=mysql&action=check', - 'urlTableList' => 'https://digitmind8080.cloudpub.ru/?connection_type=mysql&action=table_list', - 'urlTableDescription' => 'https://digitmind8080.cloudpub.ru/?connection_type=mysql&action=table_description', - 'urlData' => 'https://digitmind8080.cloudpub.ru/?connection_type=mysql&action=data', + 'urlCheck' => 'https://example.com/api/check', + 'urlTableList' => 'https://example.com/api/table_list', + 'urlTableDescription' => 'https://example.com/api/table_description', + 'urlData' => 'https://example.com/api/data', 'settings' => [ [ 'name' => 'Host', diff --git a/tests/Integration/Services/Biconnector/Dataset/Result/DatasetItemResultAnnotationsTest.php b/tests/Integration/Services/Biconnector/Dataset/Result/DatasetItemResultAnnotationsTest.php index cf2bd662..b1ad5a4e 100644 --- a/tests/Integration/Services/Biconnector/Dataset/Result/DatasetItemResultAnnotationsTest.php +++ b/tests/Integration/Services/Biconnector/Dataset/Result/DatasetItemResultAnnotationsTest.php @@ -54,6 +54,10 @@ class DatasetItemResultAnnotationsTest extends TestCase #[\Override] protected function setUp(): void { + // setUp body is commented out: this test class requires an additional external service + // (a real database accessible via the Biconnector connector). + $this->markTestSkipped('This test requires an additional external service (a real database accessible via the Biconnector connector).'); + /* $biconnectorServiceBuilder = Factory::getServiceBuilder(true)->getBiconnectorScope(); $this->datasetService = $biconnectorServiceBuilder->dataset(); $this->sourceService = $biconnectorServiceBuilder->source(); @@ -87,6 +91,7 @@ protected function setUp(): void ['type' => 'int', 'name' => 'ID', 'externalCode' => 'ID'], ], ])->getId(); + */ } /** @@ -96,6 +101,9 @@ protected function setUp(): void #[\Override] protected function tearDown(): void { + // tearDown body is commented out: this test class requires an additional external service + // (a real database accessible via the Biconnector connector). + /* try { $this->datasetService->delete($this->datasetId); } catch (\Throwable) { @@ -110,6 +118,7 @@ protected function tearDown(): void $this->connectorService->delete($this->connectorId); } catch (\Throwable) { } + */ } private function makeConnectorFields(string $title): array @@ -135,24 +144,34 @@ private function makeConnectorFields(string $title): array #[TestDox('all fields in DatasetItemResult are annotated and match live API fields schema')] public function testAllSystemFieldsAnnotated(): void { + // Test body is commented out: this test requires an additional external service + // (a real database accessible via the Biconnector connector). + $this->markTestSkipped('This test requires an additional external service (a real database accessible via the Biconnector connector).'); + /* $fieldCodes = $this->getDatasetFieldCodes(); $this->assertBitrix24AllResultItemFieldsAnnotated( $fieldCodes, DatasetItemResult::class ); + */ } #[Test] #[TestDox('all fields in DatasetItemResult have valid type casting matching API fields schema')] public function testAllSystemFieldsHasValidTypeAnnotation(): void { + // Test body is commented out: this test requires an additional external service + // (a real database accessible via the Biconnector connector). + $this->markTestSkipped('This test requires an additional external service (a real database accessible via the Biconnector connector).'); + /* $fieldTypesMap = $this->getDatasetFieldTypesMap(); $this->assertBitrix24AllResultItemFieldsHasValidTypeAnnotation( $fieldTypesMap, DatasetItemResult::class ); + */ } /** @@ -186,4 +205,3 @@ private function getDatasetFieldTypesMap(): array return $result; } } - diff --git a/tests/Integration/Services/Biconnector/Dataset/Service/BatchTest.php b/tests/Integration/Services/Biconnector/Dataset/Service/BatchTest.php index 73577213..64e999a1 100644 --- a/tests/Integration/Services/Biconnector/Dataset/Service/BatchTest.php +++ b/tests/Integration/Services/Biconnector/Dataset/Service/BatchTest.php @@ -48,6 +48,10 @@ class BatchTest extends TestCase #[\Override] protected function setUp(): void { + // setUp body is commented out: this test class requires an additional external service + // (a real database accessible via the Biconnector connector). + $this->markTestSkipped('This test requires an additional external service (a real database accessible via the Biconnector connector).'); + /* $biconnectorServiceBuilder = Factory::getServiceBuilder(true)->getBiconnectorScope(); $this->datasetService = $biconnectorServiceBuilder->dataset(); $this->sourceService = $biconnectorServiceBuilder->source(); @@ -70,6 +74,7 @@ protected function setUp(): void 'password' => 'testpass123', ], ])->getId(); + */ } /** @@ -79,6 +84,9 @@ protected function setUp(): void #[\Override] protected function tearDown(): void { + // tearDown body is commented out: this test class requires an additional external service + // (a real database accessible via the Biconnector connector). + /* try { $this->sourceService->delete($this->sourceId); } catch (\Throwable) { @@ -88,6 +96,7 @@ protected function tearDown(): void $this->connectorService->delete($this->connectorId); } catch (\Throwable) { } + */ } private function makeConnectorFields(string $title): array @@ -128,6 +137,10 @@ private function makeDatasetFields(string $name): array */ public function testBatchList(): void { + // Test body is commented out: this test requires an additional external service + // (a real database accessible via the Biconnector connector). + $this->markTestSkipped('This test requires an additional external service (a real database accessible via the Biconnector connector).'); + /* $name = 'ds' . substr(str_replace('-', '', $this->faker->uuid()), 0, 20); $id = $this->datasetService->add($this->makeDatasetFields($name))->getId(); @@ -141,6 +154,7 @@ public function testBatchList(): void // Cleanup $this->datasetService->delete($id); + */ } /** @@ -149,6 +163,10 @@ public function testBatchList(): void */ public function testBatchAdd(): void { + // Test body is commented out: this test requires an additional external service + // (a real database accessible via the Biconnector connector). + $this->markTestSkipped('This test requires an additional external service (a real database accessible via the Biconnector connector).'); + /* $datasets = []; for ($i = 0; $i < 3; $i++) { $name = 'ds' . substr(str_replace('-', '', $this->faker->uuid()), 0, 20); @@ -167,6 +185,7 @@ public function testBatchAdd(): void foreach ($this->datasetService->batch->delete($addedIds) as $deleteResult) { self::assertTrue($deleteResult->isSuccess()); } + */ } /** @@ -175,6 +194,10 @@ public function testBatchAdd(): void */ public function testBatchDelete(): void { + // Test body is commented out: this test requires an additional external service + // (a real database accessible via the Biconnector connector). + $this->markTestSkipped('This test requires an additional external service (a real database accessible via the Biconnector connector).'); + /* $ids = []; for ($i = 0; $i < 2; $i++) { $name = 'ds' . substr(str_replace('-', '', $this->faker->uuid()), 0, 20); @@ -184,6 +207,6 @@ public function testBatchDelete(): void foreach ($this->datasetService->batch->delete($ids) as $result) { self::assertTrue($result->isSuccess()); } + */ } } - diff --git a/tests/Integration/Services/Biconnector/Dataset/Service/DatasetTest.php b/tests/Integration/Services/Biconnector/Dataset/Service/DatasetTest.php index 25d87c8f..f812908c 100644 --- a/tests/Integration/Services/Biconnector/Dataset/Service/DatasetTest.php +++ b/tests/Integration/Services/Biconnector/Dataset/Service/DatasetTest.php @@ -50,6 +50,10 @@ class DatasetTest extends TestCase #[\Override] protected function setUp(): void { + // setUp body is commented out: this test class requires an additional external service + // (a real database accessible via the Biconnector connector). + $this->markTestSkipped('This test requires an additional external service (a real database accessible via the Biconnector connector).'); + /* $biconnectorServiceBuilder = Factory::getServiceBuilder(true)->getBiconnectorScope(); $this->datasetService = $biconnectorServiceBuilder->dataset(); $this->sourceService = $biconnectorServiceBuilder->source(); @@ -72,6 +76,7 @@ protected function setUp(): void 'password' => 'testpass123', ], ])->getId(); + */ } /** @@ -81,6 +86,9 @@ protected function setUp(): void #[\Override] protected function tearDown(): void { + // tearDown body is commented out: this test class requires an additional external service + // (a real database accessible via the Biconnector connector). + /* try { $this->sourceService->delete($this->sourceId); } catch (\Throwable) { @@ -90,6 +98,7 @@ protected function tearDown(): void $this->connectorService->delete($this->connectorId); } catch (\Throwable) { } + */ } private function makeConnectorFields(string $title): array @@ -133,6 +142,10 @@ private function makeDatasetFields(string $name): array */ public function testAdd(): void { + // Test body is commented out: this test requires an additional external service + // (a real database accessible via the Biconnector connector). + $this->markTestSkipped('This test requires an additional external service (a real database accessible via the Biconnector connector).'); + /* $name = 'ds' . substr(str_replace('-', '', $this->faker->uuid()), 0, 20); $id = $this->datasetService->add($this->makeDatasetFields($name))->getId(); @@ -140,6 +153,7 @@ public function testAdd(): void // Cleanup $this->datasetService->delete($id); + */ } /** @@ -148,6 +162,10 @@ public function testAdd(): void */ public function testGet(): void { + // Test body is commented out: this test requires an additional external service + // (a real database accessible via the Biconnector connector). + $this->markTestSkipped('This test requires an additional external service (a real database accessible via the Biconnector connector).'); + /* $name = 'ds' . substr(str_replace('-', '', $this->faker->uuid()), 0, 20); $id = $this->datasetService->add($this->makeDatasetFields($name))->getId(); @@ -158,6 +176,7 @@ public function testGet(): void // Cleanup $this->datasetService->delete($id); + */ } /** @@ -166,6 +185,10 @@ public function testGet(): void */ public function testList(): void { + // Test body is commented out: this test requires an additional external service + // (a real database accessible via the Biconnector connector). + $this->markTestSkipped('This test requires an additional external service (a real database accessible via the Biconnector connector).'); + /* $name = 'ds' . substr(str_replace('-', '', $this->faker->uuid()), 0, 20); $id = $this->datasetService->add($this->makeDatasetFields($name))->getId(); @@ -175,6 +198,7 @@ public function testList(): void // Cleanup $this->datasetService->delete($id); + */ } /** @@ -183,6 +207,10 @@ public function testList(): void */ public function testUpdate(): void { + // Test body is commented out: this test requires an additional external service + // (a real database accessible via the Biconnector connector). + $this->markTestSkipped('This test requires an additional external service (a real database accessible via the Biconnector connector).'); + /* $name = 'ds' . substr(str_replace('-', '', $this->faker->uuid()), 0, 20); $id = $this->datasetService->add($this->makeDatasetFields($name))->getId(); @@ -195,6 +223,7 @@ public function testUpdate(): void // Cleanup $this->datasetService->delete($id); + */ } /** @@ -203,10 +232,15 @@ public function testUpdate(): void */ public function testDelete(): void { + // Test body is commented out: this test requires an additional external service + // (a real database accessible via the Biconnector connector). + $this->markTestSkipped('This test requires an additional external service (a real database accessible via the Biconnector connector).'); + /* $name = 'ds' . substr(str_replace('-', '', $this->faker->uuid()), 0, 20); $id = $this->datasetService->add($this->makeDatasetFields($name))->getId(); self::assertTrue($this->datasetService->delete($id)->isSuccess()); + */ } /** @@ -215,9 +249,14 @@ public function testDelete(): void */ public function testFields(): void { + // Test body is commented out: this test requires an additional external service + // (a real database accessible via the Biconnector connector). + $this->markTestSkipped('This test requires an additional external service (a real database accessible via the Biconnector connector).'); + /* $fields = $this->datasetService->fields()->getFieldsDescription(); self::assertIsArray($fields); self::assertNotEmpty($fields); + */ } /** @@ -226,6 +265,10 @@ public function testFields(): void */ public function testUpdateFields(): void { + // Test body is commented out: this test requires an additional external service + // (a real database accessible via the Biconnector connector). + $this->markTestSkipped('This test requires an additional external service (a real database accessible via the Biconnector connector).'); + /* $name = 'ds' . substr(str_replace('-', '', $this->faker->uuid()), 0, 20); $id = $this->datasetService->add($this->makeDatasetFields($name))->getId(); @@ -246,6 +289,7 @@ public function testUpdateFields(): void // Cleanup $this->datasetService->delete($id); + */ } /** @@ -254,6 +298,10 @@ public function testUpdateFields(): void */ public function testCount(): void { + // Test body is commented out: this test requires an additional external service + // (a real database accessible via the Biconnector connector). + $this->markTestSkipped('This test requires an additional external service (a real database accessible via the Biconnector connector).'); + /* $countBefore = $this->datasetService->count(); $name = 'ds' . substr(str_replace('-', '', $this->faker->uuid()), 0, 20); @@ -264,6 +312,6 @@ public function testCount(): void // Cleanup $this->datasetService->delete($id); + */ } } - diff --git a/tests/Integration/Services/Biconnector/Source/Result/SourceItemResultAnnotationsTest.php b/tests/Integration/Services/Biconnector/Source/Result/SourceItemResultAnnotationsTest.php index 2261defd..20ed76b1 100644 --- a/tests/Integration/Services/Biconnector/Source/Result/SourceItemResultAnnotationsTest.php +++ b/tests/Integration/Services/Biconnector/Source/Result/SourceItemResultAnnotationsTest.php @@ -51,6 +51,10 @@ class SourceItemResultAnnotationsTest extends TestCase #[\Override] protected function setUp(): void { + // setUp body is commented out: this test class requires an additional external service + // (a real database accessible via the Biconnector connector). + $this->markTestSkipped('This test requires an additional external service (a real database accessible via the Biconnector connector).'); + /* $builder = Factory::getServiceBuilder(true)->getBiconnectorScope(); $this->sourceService = $builder->source(); $this->connectorService = $builder->connector(); @@ -72,6 +76,7 @@ protected function setUp(): void 'password' => 'testpass123', ], ])->getId(); + */ } /** @@ -81,6 +86,9 @@ protected function setUp(): void #[\Override] protected function tearDown(): void { + // tearDown body is commented out: this test class requires an additional external service + // (a real database accessible via the Biconnector connector). + /* try { $this->sourceService->delete($this->sourceId); } catch (\Throwable) { @@ -90,6 +98,7 @@ protected function tearDown(): void $this->connectorService->delete($this->connectorId); } catch (\Throwable) { } + */ } private function makeConnectorFields(string $title): array @@ -135,24 +144,34 @@ private function makeConnectorFields(string $title): array #[TestDox('all fields in SourceItemResult are annotated and match live API fields schema')] public function testAllSystemFieldsAnnotated(): void { + // Test body is commented out: this test requires an additional external service + // (a real database accessible via the Biconnector connector). + $this->markTestSkipped('This test requires an additional external service (a real database accessible via the Biconnector connector).'); + /* $fieldCodes = $this->getSourceFieldCodes(); $this->assertBitrix24AllResultItemFieldsAnnotated( $fieldCodes, SourceItemResult::class ); + */ } #[Test] #[TestDox('all fields in SourceItemResult have valid type casting matching API fields schema')] public function testAllSystemFieldsHasValidTypeAnnotation(): void { + // Test body is commented out: this test requires an additional external service + // (a real database accessible via the Biconnector connector). + $this->markTestSkipped('This test requires an additional external service (a real database accessible via the Biconnector connector).'); + /* $fieldTypesMap = $this->getSourceFieldTypesMap(); $this->assertBitrix24AllResultItemFieldsHasValidTypeAnnotation( $fieldTypesMap, SourceItemResult::class ); + */ } /** diff --git a/tests/Integration/Services/Biconnector/Source/Service/BatchTest.php b/tests/Integration/Services/Biconnector/Source/Service/BatchTest.php index b79a03ac..9f542e56 100644 --- a/tests/Integration/Services/Biconnector/Source/Service/BatchTest.php +++ b/tests/Integration/Services/Biconnector/Source/Service/BatchTest.php @@ -130,6 +130,10 @@ private function makeSourceFields(string $title): array */ public function testBatchList(): void { + // Test body is commented out: this test requires an additional external service + // (a real database accessible via the Biconnector connector). + $this->markTestSkipped('This test requires an additional external service (a real database accessible via the Biconnector connector).'); + /* $title = 'source-' . $this->faker->uuid(); $id = $this->sourceService->add($this->makeSourceFields($title))->getId(); @@ -143,6 +147,7 @@ public function testBatchList(): void // Cleanup $this->sourceService->delete($id); + */ } /** @@ -151,6 +156,10 @@ public function testBatchList(): void */ public function testBatchAdd(): void { + // Test body is commented out: this test requires an additional external service + // (a real database accessible via the Biconnector connector). + $this->markTestSkipped('This test requires an additional external service (a real database accessible via the Biconnector connector).'); + /* $sources = []; for ($i = 0; $i < 3; $i++) { $sources[] = $this->makeSourceFields('source-batch-' . $this->faker->uuid()); @@ -168,6 +177,7 @@ public function testBatchAdd(): void foreach ($this->sourceService->batch->delete($addedIds) as $deleteResult) { self::assertTrue($deleteResult->isSuccess()); } + */ } /** @@ -176,6 +186,10 @@ public function testBatchAdd(): void */ public function testBatchDelete(): void { + // Test body is commented out: this test requires an additional external service + // (a real database accessible via the Biconnector connector). + $this->markTestSkipped('This test requires an additional external service (a real database accessible via the Biconnector connector).'); + /* $ids = []; for ($i = 0; $i < 2; $i++) { $ids[] = $this->sourceService->add( @@ -186,5 +200,6 @@ public function testBatchDelete(): void foreach ($this->sourceService->batch->delete($ids) as $result) { self::assertTrue($result->isSuccess()); } + */ } } diff --git a/tests/Integration/Services/Biconnector/Source/Service/SourceTest.php b/tests/Integration/Services/Biconnector/Source/Service/SourceTest.php index d4e6b077..3b8ee49d 100644 --- a/tests/Integration/Services/Biconnector/Source/Service/SourceTest.php +++ b/tests/Integration/Services/Biconnector/Source/Service/SourceTest.php @@ -139,6 +139,10 @@ private function makeSourceFields(string $title): array */ public function testAdd(): void { + // Test body is commented out: this test requires an additional external service + // (a real database accessible via the Biconnector connector). + $this->markTestSkipped('This test requires an additional external service (a real database accessible via the Biconnector connector).'); + /* $title = 'source-' . $this->faker->uuid(); $id = $this->sourceService->add($this->makeSourceFields($title))->getId(); @@ -146,6 +150,7 @@ public function testAdd(): void // Cleanup $this->sourceService->delete($id); + */ } /** @@ -154,6 +159,10 @@ public function testAdd(): void */ public function testGet(): void { + // Test body is commented out: this test requires an additional external service + // (a real database accessible via the Biconnector connector). + $this->markTestSkipped('This test requires an additional external service (a real database accessible via the Biconnector connector).'); + /* $title = 'source-' . $this->faker->uuid(); $id = $this->sourceService->add($this->makeSourceFields($title))->getId(); @@ -164,6 +173,7 @@ public function testGet(): void // Cleanup $this->sourceService->delete($id); + */ } /** @@ -172,6 +182,10 @@ public function testGet(): void */ public function testList(): void { + // Test body is commented out: this test requires an additional external service + // (a real database accessible via the Biconnector connector). + $this->markTestSkipped('This test requires an additional external service (a real database accessible via the Biconnector connector).'); + /* $title = 'source-' . $this->faker->uuid(); $id = $this->sourceService->add($this->makeSourceFields($title))->getId(); @@ -181,6 +195,7 @@ public function testList(): void // Cleanup $this->sourceService->delete($id); + */ } /** @@ -189,6 +204,10 @@ public function testList(): void */ public function testUpdate(): void { + // Test body is commented out: this test requires an additional external service + // (a real database accessible via the Biconnector connector). + $this->markTestSkipped('This test requires an additional external service (a real database accessible via the Biconnector connector).'); + /* $title = 'source-' . $this->faker->uuid(); $sourceFields = $this->makeSourceFields($title); $id = $this->sourceService->add($sourceFields)->getId(); @@ -205,6 +224,7 @@ public function testUpdate(): void // Cleanup $this->sourceService->delete($id); + */ } /** @@ -213,10 +233,15 @@ public function testUpdate(): void */ public function testDelete(): void { + // Test body is commented out: this test requires an additional external service + // (a real database accessible via the Biconnector connector). + $this->markTestSkipped('This test requires an additional external service (a real database accessible via the Biconnector connector).'); + /* $title = 'source-' . $this->faker->uuid(); $id = $this->sourceService->add($this->makeSourceFields($title))->getId(); self::assertTrue($this->sourceService->delete($id)->isSuccess()); + */ } /** @@ -225,9 +250,14 @@ public function testDelete(): void */ public function testFields(): void { + // Test body is commented out: this test requires an additional external service + // (a real database accessible via the Biconnector connector). + $this->markTestSkipped('This test requires an additional external service (a real database accessible via the Biconnector connector).'); + /* $fields = $this->sourceService->fields()->getFieldsDescription(); self::assertIsArray($fields); self::assertNotEmpty($fields); + */ } /** @@ -236,6 +266,10 @@ public function testFields(): void */ public function testCount(): void { + // Test body is commented out: this test requires an additional external service + // (a real database accessible via the Biconnector connector). + $this->markTestSkipped('This test requires an additional external service (a real database accessible via the Biconnector connector).'); + /* $countBefore = $this->sourceService->count(); $title = 'source-' . $this->faker->uuid(); @@ -246,5 +280,6 @@ public function testCount(): void // Cleanup $this->sourceService->delete($id); + */ } } From 699428c30bdb9c536b974d1c51fb15760ea13c58 Mon Sep 17 00:00:00 2001 From: Dmitriy Ignatenko Date: Thu, 21 May 2026 16:14:56 +0400 Subject: [PATCH 17/18] Minor fix --- Makefile | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/Makefile b/Makefile index 56b74d22..fd887274 100644 --- a/Makefile +++ b/Makefile @@ -612,6 +612,14 @@ integration_tests_sale_payment_item_basket: integration_tests_crm_documentgenerator_numerator: docker compose run --rm php-cli vendor/bin/phpunit --testsuite integration_tests_crm_documentgenerator_numerator +.PHONY: integration_tests_crm_documentgenerator_document +integration_tests_crm_documentgenerator_document: + docker compose run --rm php-cli vendor/bin/phpunit --testsuite integration_tests_crm_documentgenerator_document + +.PHONY: integration_tests_crm_documentgenerator_template +integration_tests_crm_documentgenerator_template: + docker compose run --rm php-cli vendor/bin/phpunit --testsuite integration_tests_crm_documentgenerator_template + .PHONY: test-integration-scope-biconnector test-integration-scope-biconnector: docker compose run --rm php-cli vendor/bin/phpunit --testsuite integration_tests_scope_biconnector @@ -628,14 +636,6 @@ test-integration-biconnector-source: test-integration-biconnector-dataset: docker compose run --rm php-cli vendor/bin/phpunit --testsuite integration_tests_biconnector_dataset -.PHONY: integration_tests_crm_documentgenerator_document -integration_tests_crm_documentgenerator_document: - docker compose run --rm php-cli vendor/bin/phpunit --testsuite integration_tests_crm_documentgenerator_document - -.PHONY: integration_tests_crm_documentgenerator_template -integration_tests_crm_documentgenerator_template: - docker compose run --rm php-cli vendor/bin/phpunit --testsuite integration_tests_crm_documentgenerator_template - # work dev environment .PHONY: php-dev-server-up php-dev-server-up: From 8f0a4f95dbab65d70c7ef51186a0e9eebe29de5a Mon Sep 17 00:00:00 2001 From: Dmitriy Ignatenko Date: Mon, 25 May 2026 13:08:51 +0400 Subject: [PATCH 18/18] Move AbstractItem to AbstractAnnotatedItem --- .../Connector/Result/ConnectorItemResult.php | 16 ++-------------- .../Dataset/Result/DatasetItemResult.php | 16 ++-------------- .../Source/Result/SourceItemResult.php | 16 ++-------------- 3 files changed, 6 insertions(+), 42 deletions(-) diff --git a/src/Services/Biconnector/Connector/Result/ConnectorItemResult.php b/src/Services/Biconnector/Connector/Result/ConnectorItemResult.php index 479b16b6..2c1d3587 100644 --- a/src/Services/Biconnector/Connector/Result/ConnectorItemResult.php +++ b/src/Services/Biconnector/Connector/Result/ConnectorItemResult.php @@ -13,7 +13,7 @@ namespace Bitrix24\SDK\Services\Biconnector\Connector\Result; -use Bitrix24\SDK\Core\Result\AbstractItem; +use Bitrix24\SDK\Core\Result\AbstractAnnotatedItem; use Carbon\CarbonImmutable; /** @@ -36,18 +36,6 @@ * @property-read bool|null $supportMapping * @property-read CarbonImmutable $dateCreate */ -class ConnectorItemResult extends AbstractItem +class ConnectorItemResult extends AbstractAnnotatedItem { - #[\Override] - public function __get($offset): mixed - { - return match ($offset) { - 'id', 'sort' => isset($this->data[$offset]) ? (int)$this->data[$offset] : null, - 'supportMapping' => isset($this->data[$offset]) ? (bool)$this->data[$offset] : null, - 'dateCreate' => isset($this->data[$offset]) - ? CarbonImmutable::parse($this->data[$offset]) - : null, - default => $this->data[$offset] ?? null, - }; - } } diff --git a/src/Services/Biconnector/Dataset/Result/DatasetItemResult.php b/src/Services/Biconnector/Dataset/Result/DatasetItemResult.php index c807c4ba..a5c6f837 100644 --- a/src/Services/Biconnector/Dataset/Result/DatasetItemResult.php +++ b/src/Services/Biconnector/Dataset/Result/DatasetItemResult.php @@ -13,7 +13,7 @@ namespace Bitrix24\SDK\Services\Biconnector\Dataset\Result; -use Bitrix24\SDK\Core\Result\AbstractItem; +use Bitrix24\SDK\Core\Result\AbstractAnnotatedItem; use Carbon\CarbonImmutable; /** @@ -37,19 +37,7 @@ * @property-read int $updatedById * @property-read array|null $fields */ -class DatasetItemResult extends AbstractItem +class DatasetItemResult extends AbstractAnnotatedItem { - #[\Override] - public function __get($offset): mixed - { - return match ($offset) { - 'id', 'createdById', 'updatedById' => isset($this->data[$offset]) ? (int)$this->data[$offset] : null, - 'sourceId', 'externalId' => isset($this->data[$offset]) ? (int)$this->data[$offset] : null, - 'dateCreate', 'dateUpdate' => isset($this->data[$offset]) - ? CarbonImmutable::parse($this->data[$offset]) - : null, - default => $this->data[$offset] ?? null, - }; - } } diff --git a/src/Services/Biconnector/Source/Result/SourceItemResult.php b/src/Services/Biconnector/Source/Result/SourceItemResult.php index 2cde005d..7380e745 100644 --- a/src/Services/Biconnector/Source/Result/SourceItemResult.php +++ b/src/Services/Biconnector/Source/Result/SourceItemResult.php @@ -13,7 +13,7 @@ namespace Bitrix24\SDK\Services\Biconnector\Source\Result; -use Bitrix24\SDK\Core\Result\AbstractItem; +use Bitrix24\SDK\Core\Result\AbstractAnnotatedItem; use Carbon\CarbonImmutable; /** @@ -36,18 +36,6 @@ * @property-read int $connectorId * @property-read array|null $settings */ -class SourceItemResult extends AbstractItem +class SourceItemResult extends AbstractAnnotatedItem { - #[\Override] - public function __get($offset): mixed - { - return match ($offset) { - 'id', 'createdById', 'updatedById', 'connectorId' => isset($this->data[$offset]) ? (int)$this->data[$offset] : null, - 'active' => isset($this->data[$offset]) ? (bool)$this->data[$offset] : null, - 'dateCreate', 'dateUpdate' => isset($this->data[$offset]) - ? CarbonImmutable::parse($this->data[$offset]) - : null, - default => $this->data[$offset] ?? null, - }; - } }