From 85609cc2a94a2aa92bfd9e505c3e13b8cb46271b Mon Sep 17 00:00:00 2001 From: mesilov Date: Sun, 19 Apr 2026 03:03:18 +0600 Subject: [PATCH 1/5] docs: add design for issue 435 --- .../2026-04-19-im-disk-folder-get-design.md | 71 +++++++++++++++++++ 1 file changed, 71 insertions(+) create mode 100644 docs/superpowers/specs/2026-04-19-im-disk-folder-get-design.md diff --git a/docs/superpowers/specs/2026-04-19-im-disk-folder-get-design.md b/docs/superpowers/specs/2026-04-19-im-disk-folder-get-design.md new file mode 100644 index 00000000..b3ee7d34 --- /dev/null +++ b/docs/superpowers/specs/2026-04-19-im-disk-folder-get-design.md @@ -0,0 +1,71 @@ +# Design: IM Disk folder getter for issue #435 + +## Goal + +Add SDK support for the Bitrix24 REST method `im.disk.folder.get` in a minimal way that matches the scope of issue `#435`. + +## Scope + +In scope: +- add a new IM Disk service class under `src/Services/IM/Disk/Service/Disk.php` +- add one public method for `im.disk.folder.get` +- expose the service through `IMServiceBuilder::disk()` +- add unit coverage for the new service and builder accessor +- add integration coverage for the IM Disk service +- add a dedicated PHPUnit suite and `make test-integration-im-disk` target +- add a `CHANGELOG.md` entry under `3.2.0 – UNRELEASED` / `### Added` + +Out of scope: +- adding any other `im.disk.*` methods +- introducing result-item abstractions beyond what is needed for the single returned folder id +- refactoring unrelated IM services + +## Design + +### Service layout + +Create `Bitrix24\SDK\Services\IM\Disk\Service\Disk` as a regular `AbstractService` subclass with: +- `#[ApiServiceMetadata(new Scope(['im']))]` +- one method `folderGet(): IntResult` + +The method calls `im.disk.folder.get` without parameters and returns `Bitrix24\SDK\Core\Result\IntResult`. + +This keeps the implementation aligned with the existing service pattern in the SDK while avoiding speculative expansion for future `im.disk.*` methods. + +### Service builder integration + +Extend `Bitrix24\SDK\Services\IM\IMServiceBuilder` with: +- import for the new IM Disk service +- cached accessor `disk(): \Bitrix24\SDK\Services\IM\Disk\Service\Disk` + +The accessor should follow the same caching behavior already used by `notify()` and `placements()`. + +### Tests + +Unit coverage: +- extend `tests/Unit/Services/IM/IMServiceBuilderTest.php` with an assertion that `disk()` returns the new service and is cached +- add `tests/Unit/Services/IM/Disk/Service/DiskTest.php` to verify the REST method name and empty parameter mapping for `folderGet()` + +Integration coverage: +- add `tests/Integration/Services/IM/Disk/Service/DiskTest.php` +- verify `Factory::getServiceBuilder()->getIMScope()->disk()->folderGet()` returns a positive folder id + +Test execution wiring: +- add a dedicated suite to `phpunit.xml.dist` for the new integration test path +- add `make test-integration-im-disk` to run only that suite + +## Error handling + +No custom error handling is needed. The new service should reuse the existing transport and API exception flow provided by `AbstractService` and `Core`. + +## Verification + +Minimum verification for the implementation phase: +- targeted unit tests for the new service and builder +- targeted integration test for `im.disk.folder.get` +- changelog entry references issue `#435` + +## Risks + +- The REST response shape must match `IntResult` expectations. If the endpoint returns a different envelope than other integer-returning methods, the integration test should surface it immediately. +- The new PHPUnit suite name and Make target must follow existing repository conventions to avoid drift in local developer workflows. From 9257aa86f79092eca184db2d68b4a7b1cad77650 Mon Sep 17 00:00:00 2001 From: mesilov Date: Sun, 19 Apr 2026 14:48:31 +0600 Subject: [PATCH 2/5] test: lock IM disk service contract --- .../Services/IM/Disk/Service/DiskTest.php | 59 +++++++++++++++++++ .../Unit/Services/IM/IMServiceBuilderTest.php | 7 +++ 2 files changed, 66 insertions(+) create mode 100644 tests/Unit/Services/IM/Disk/Service/DiskTest.php diff --git a/tests/Unit/Services/IM/Disk/Service/DiskTest.php b/tests/Unit/Services/IM/Disk/Service/DiskTest.php new file mode 100644 index 00000000..61fe671e --- /dev/null +++ b/tests/Unit/Services/IM/Disk/Service/DiskTest.php @@ -0,0 +1,59 @@ + + * + * 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\IM\Disk\Service; + +use Bitrix24\SDK\Core\ApiLevelErrorHandler; +use Bitrix24\SDK\Core\Commands\Command; +use Bitrix24\SDK\Core\Contracts\ApiVersion; +use Bitrix24\SDK\Core\Contracts\CoreInterface; +use Bitrix24\SDK\Core\Response\Response; +use Bitrix24\SDK\Services\IM\Disk\Result\FolderIdResult; +use Bitrix24\SDK\Services\IM\Disk\Service\Disk; +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\TestCase; +use Psr\Log\NullLogger; +use Symfony\Component\HttpClient\Response\MockResponse; + +#[CoversClass(Disk::class)] +final class DiskTest extends TestCase +{ + public function testGetFolderIdMapsChatIdAndDialogId(): void + { + $core = $this->createMock(CoreInterface::class); + $core + ->expects($this->once()) + ->method('call') + ->with( + 'im.disk.folder.get', + [ + 'CHAT_ID' => 17, + 'DIALOG_ID' => 'chat17', + ], + ApiVersion::v1 + ) + ->willReturn( + new Response( + new MockResponse(json_encode(['result' => ['ID' => 5153], 'time' => []], JSON_THROW_ON_ERROR)), + new Command('im.disk.folder.get', []), + new ApiLevelErrorHandler(new NullLogger()), + new NullLogger() + ) + ); + + $result = (new Disk($core, new NullLogger()))->getFolderId(17, 'chat17'); + + $this->assertInstanceOf(FolderIdResult::class, $result); + $this->assertSame(5153, $result->getId()); + } +} diff --git a/tests/Unit/Services/IM/IMServiceBuilderTest.php b/tests/Unit/Services/IM/IMServiceBuilderTest.php index c718b769..3071379a 100644 --- a/tests/Unit/Services/IM/IMServiceBuilderTest.php +++ b/tests/Unit/Services/IM/IMServiceBuilderTest.php @@ -13,6 +13,7 @@ namespace Bitrix24\SDK\Tests\Unit\Services\IM; +use Bitrix24\SDK\Services\IM\Disk\Service\Disk; use Bitrix24\SDK\Services\IM\IMServiceBuilder; use Bitrix24\SDK\Services\IM\Placements\Placements; use Bitrix24\SDK\Services\ServiceBuilder; @@ -33,6 +34,12 @@ public function testGetIMService(): void $this::assertSame($this->serviceBuilder->notify(), $this->serviceBuilder->notify()); } + public function testGetDiskService(): void + { + $this->assertInstanceOf(Disk::class, $this->serviceBuilder->disk()); + $this->assertSame($this->serviceBuilder->disk(), $this->serviceBuilder->disk()); + } + public function testGetPlacementsService(): void { $this->assertInstanceOf(Placements::class, $this->serviceBuilder->placements()); From e9f4c659e514341346b3c36f1806fe8736ed5af7 Mon Sep 17 00:00:00 2001 From: mesilov Date: Sun, 19 Apr 2026 14:50:36 +0600 Subject: [PATCH 3/5] feat: add IM disk folder getter --- .../IM/Disk/Result/FolderIdResult.php | 24 +++++++++ src/Services/IM/Disk/Service/Disk.php | 50 +++++++++++++++++++ src/Services/IM/IMServiceBuilder.php | 10 ++++ .../Services/IM/Disk/Service/DiskTest.php | 20 ++++---- 4 files changed, 93 insertions(+), 11 deletions(-) create mode 100644 src/Services/IM/Disk/Result/FolderIdResult.php create mode 100644 src/Services/IM/Disk/Service/Disk.php diff --git a/src/Services/IM/Disk/Result/FolderIdResult.php b/src/Services/IM/Disk/Result/FolderIdResult.php new file mode 100644 index 00000000..1b66c7d1 --- /dev/null +++ b/src/Services/IM/Disk/Result/FolderIdResult.php @@ -0,0 +1,24 @@ + + * + * 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\IM\Disk\Result; + +use Bitrix24\SDK\Core\Result\AbstractResult; + +class FolderIdResult extends AbstractResult +{ + public function getId(): int + { + return (int)$this->getCoreResponse()->getResponseData()->getResult()['ID']; + } +} diff --git a/src/Services/IM/Disk/Service/Disk.php b/src/Services/IM/Disk/Service/Disk.php new file mode 100644 index 00000000..6b8f64f0 --- /dev/null +++ b/src/Services/IM/Disk/Service/Disk.php @@ -0,0 +1,50 @@ + + * + * 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\IM\Disk\Service; + +use Bitrix24\SDK\Attributes\ApiEndpointMetadata; +use Bitrix24\SDK\Attributes\ApiServiceMetadata; +use Bitrix24\SDK\Core\Credentials\Scope; +use Bitrix24\SDK\Core\Exceptions\BaseException; +use Bitrix24\SDK\Core\Exceptions\TransportException; +use Bitrix24\SDK\Services\AbstractService; +use Bitrix24\SDK\Services\IM\Disk\Result\FolderIdResult; + +#[ApiServiceMetadata(new Scope(['im']))] +class Disk extends AbstractService +{ + /** + * @throws BaseException + * @throws TransportException + */ + #[ApiEndpointMetadata( + 'im.disk.folder.get', + 'https://apidocs.bitrix24.ru/api-reference/chats/files/im-disk-folder-get.html', + 'Get the identifier of the folder where chat files are stored' + )] + public function getFolderId(?int $chatId = null, ?string $dialogId = null): FolderIdResult + { + $params = []; + + if ($chatId !== null) { + $params['CHAT_ID'] = $chatId; + } + + if ($dialogId !== null) { + $params['DIALOG_ID'] = $dialogId; + } + + return new FolderIdResult($this->core->call('im.disk.folder.get', $params)); + } +} diff --git a/src/Services/IM/IMServiceBuilder.php b/src/Services/IM/IMServiceBuilder.php index bb5ab9cc..cf123b3d 100644 --- a/src/Services/IM/IMServiceBuilder.php +++ b/src/Services/IM/IMServiceBuilder.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\IM\Disk\Service\Disk; use Bitrix24\SDK\Services\IM\Notify\Service\Notify; use Bitrix24\SDK\Services\IM\Placements\PlacementLocationCodes; use Bitrix24\SDK\Services\IM\Placements\Placements; @@ -24,6 +25,15 @@ #[ApiServiceBuilderMetadata(new Scope(['im']))] class IMServiceBuilder extends AbstractServiceBuilder { + public function disk(): Disk + { + if (!isset($this->serviceCache[__METHOD__])) { + $this->serviceCache[__METHOD__] = new Disk($this->core, $this->log); + } + + return $this->serviceCache[__METHOD__]; + } + public function notify(): Notify { if (!isset($this->serviceCache[__METHOD__])) { diff --git a/tests/Unit/Services/IM/Disk/Service/DiskTest.php b/tests/Unit/Services/IM/Disk/Service/DiskTest.php index 61fe671e..52990a01 100644 --- a/tests/Unit/Services/IM/Disk/Service/DiskTest.php +++ b/tests/Unit/Services/IM/Disk/Service/DiskTest.php @@ -13,23 +13,28 @@ namespace Bitrix24\SDK\Tests\Unit\Services\IM\Disk\Service; -use Bitrix24\SDK\Core\ApiLevelErrorHandler; -use Bitrix24\SDK\Core\Commands\Command; use Bitrix24\SDK\Core\Contracts\ApiVersion; use Bitrix24\SDK\Core\Contracts\CoreInterface; use Bitrix24\SDK\Core\Response\Response; +use Bitrix24\SDK\Core\Response\DTO\Pagination; +use Bitrix24\SDK\Core\Response\DTO\ResponseData; +use Bitrix24\SDK\Core\Response\DTO\Time; use Bitrix24\SDK\Services\IM\Disk\Result\FolderIdResult; use Bitrix24\SDK\Services\IM\Disk\Service\Disk; use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\TestCase; use Psr\Log\NullLogger; -use Symfony\Component\HttpClient\Response\MockResponse; #[CoversClass(Disk::class)] final class DiskTest extends TestCase { public function testGetFolderIdMapsChatIdAndDialogId(): void { + $response = $this->createStub(Response::class); + $response + ->method('getResponseData') + ->willReturn(new ResponseData(['ID' => 5153], Time::initWithZeroValues(), new Pagination())); + $core = $this->createMock(CoreInterface::class); $core ->expects($this->once()) @@ -42,14 +47,7 @@ public function testGetFolderIdMapsChatIdAndDialogId(): void ], ApiVersion::v1 ) - ->willReturn( - new Response( - new MockResponse(json_encode(['result' => ['ID' => 5153], 'time' => []], JSON_THROW_ON_ERROR)), - new Command('im.disk.folder.get', []), - new ApiLevelErrorHandler(new NullLogger()), - new NullLogger() - ) - ); + ->willReturn($response); $result = (new Disk($core, new NullLogger()))->getFolderId(17, 'chat17'); From c4eacd5495ffe37afcb47fa807fd94db743fd7cc Mon Sep 17 00:00:00 2001 From: mesilov Date: Sun, 19 Apr 2026 20:03:44 +0600 Subject: [PATCH 4/5] test: cover IM disk folder getter --- CHANGELOG.md | 1 + Makefile | 5 ++ phpunit.xml.dist | 3 + .../Services/IM/Disk/Service/DiskTest.php | 58 +++++++++++++++++++ 4 files changed, 67 insertions(+) create mode 100644 tests/Integration/Services/IM/Disk/Service/DiskTest.php diff --git a/CHANGELOG.md b/CHANGELOG.md index 09328787..f968350b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ### Added +- Added `Bitrix24\SDK\Services\IM\Disk\Service\Disk` with `getFolderId(?int $chatId = null, ?string $dialogId = null)` for `im.disk.folder.get`, plus dedicated `FolderIdResult`, IM builder registration, and focused unit/integration coverage ([#435](https://github.com/bitrix24/b24phpsdk/issues/435)) - Added `Bitrix24\SDK\Services\IM\Placements\PlacementLocationCodes` with constants `IM_TEXTAREA`, `IM_SIDEBAR`, `IM_CONTEXT_MENU`, `IM_NAVIGATION`, and `IM_SMILES_SELECTOR` (deprecated since `im 25.1600.0`) for IM widget placement codes ([#437](https://github.com/bitrix24/b24phpsdk/issues/437)) - Added `PlacementOptionsInterface` and fluent option builders `TextareaPlacementOptions`, `SidebarPlacementOptions`, `ContextMenuPlacementOptions` under `Bitrix24\SDK\Services\IM\Placements` namespace, backed by `ChatContext`, `PlacementColor` (IM-specific) and shared `Role`, `ExtranetAvailability` string-backed enums under `Bitrix24\SDK\Services\Placement` ([#437](https://github.com/bitrix24/b24phpsdk/issues/437)) - Added integration test `PlacementLocationCodesTest` that asserts (via reflection) every `IM_`-prefixed code returned by `placement.list` is declared as a constant in `PlacementLocationCodes` ([#437](https://github.com/bitrix24/b24phpsdk/issues/437)) diff --git a/Makefile b/Makefile index f54dcabb..3dc4f142 100644 --- a/Makefile +++ b/Makefile @@ -60,6 +60,7 @@ help: @echo "test-integration-calendar-event - run Calendar Event integration tests" @echo "test-integration-calendar-resource - run Calendar Resource integration tests" @echo "test-integration-sale-basket-property - run BasketProperty integration tests" + @echo "test-integration-im-disk - run IM Disk integration tests" @echo "test-integration-sale-cashbox-handler - run CashboxHandler integration tests" @echo "test-integration-sale-cashbox - run Cashbox integration tests" @echo "test-integration-sale-delivery - run Delivery integration tests" @@ -200,6 +201,10 @@ test-integration-scope-workflows: test-integration-scope-im: docker compose run --rm php-cli vendor/bin/phpunit --testsuite integration_tests_scope_im +.PHONY: test-integration-im-disk +test-integration-im-disk: + docker compose run --rm php-cli vendor/bin/phpunit --testsuite integration_tests_im_disk + .PHONY: test-integration-scope-placement test-integration-scope-placement: docker compose run --rm php-cli vendor/bin/phpunit --testsuite integration_tests_scope_placement diff --git a/phpunit.xml.dist b/phpunit.xml.dist index d0400826..772d730d 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -22,6 +22,9 @@ ./tests/Integration/Services/IM/ + + ./tests/Integration/Services/IM/Disk/ + ./tests/Integration/Services/IMOpenLines/ diff --git a/tests/Integration/Services/IM/Disk/Service/DiskTest.php b/tests/Integration/Services/IM/Disk/Service/DiskTest.php new file mode 100644 index 00000000..b4f169ff --- /dev/null +++ b/tests/Integration/Services/IM/Disk/Service/DiskTest.php @@ -0,0 +1,58 @@ + + * + * 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\IM\Disk\Service; + +use Bitrix24\SDK\Core\Exceptions\BaseException; +use Bitrix24\SDK\Core\Exceptions\TransportException; +use Bitrix24\SDK\Services\IM\Disk\Service\Disk; +use Bitrix24\SDK\Tests\Integration\Factory; +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\TestCase; + +#[CoversClass(Disk::class)] +class DiskTest extends TestCase +{ + private Disk $diskService; + + #[\Override] + protected function setUp(): void + { + $this->diskService = Factory::getServiceBuilder()->getIMScope()->disk(); + } + + /** + * @throws BaseException + * @throws TransportException + */ + public function testGetFolderId(): void + { + $userId = (int)$this->diskService->core->call('PROFILE')->getResponseData()->getResult()['ID']; + $chatId = (int)$this->diskService->core->call( + 'im.chat.add', + [ + 'USERS' => [$userId], + 'TYPE' => 'CHAT', + 'TITLE' => sprintf('IM Disk Test %s', time()), + ] + )->getResponseData()->getResult()[0]; + + try { + $folderId = $this->diskService->getFolderId(dialogId: 'chat' . $chatId)->getId(); + + $this->assertGreaterThan(0, $folderId); + } finally { + $this->diskService->core->call('im.chat.leave', ['CHAT_ID' => $chatId]); + } + } +} From 9b851ae6ef4ca7f4c00aa2343371d0974343dcb0 Mon Sep 17 00:00:00 2001 From: mesilov Date: Sun, 19 Apr 2026 20:03:56 +0600 Subject: [PATCH 5/5] docs: align IM disk design and plan --- .../plans/2026-04-19-im-disk-folder-get.md | 360 ++++++++++++++++++ .../2026-04-19-im-disk-folder-get-design.md | 18 +- 2 files changed, 373 insertions(+), 5 deletions(-) create mode 100644 docs/superpowers/plans/2026-04-19-im-disk-folder-get.md diff --git a/docs/superpowers/plans/2026-04-19-im-disk-folder-get.md b/docs/superpowers/plans/2026-04-19-im-disk-folder-get.md new file mode 100644 index 00000000..7eecd993 --- /dev/null +++ b/docs/superpowers/plans/2026-04-19-im-disk-folder-get.md @@ -0,0 +1,360 @@ +# IM Disk Folder Get Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** Add SDK support for `im.disk.folder.get` through a dedicated IM Disk service, builder accessor, result wrapper, tests, and dedicated integration wiring. + +**Architecture:** Follow the existing service pattern used across the SDK: a focused service class under `src/Services/IM/Disk/Service`, a tiny custom result object for the `result.ID` envelope, builder registration in `IMServiceBuilder`, and targeted unit/integration tests. Keep the scope narrow to `im.disk.folder.get`; do not introduce broader `im.disk.*` abstractions in this task. + +**Tech Stack:** PHP 8.4, PHPUnit 12, Bitrix24 PHP SDK service/result patterns, Docker-based `make` test targets + +--- + +### Task 1: Lock the contract with failing unit tests + +**Files:** +- Create: `tests/Unit/Services/IM/Disk/Service/DiskTest.php` +- Modify: `tests/Unit/Services/IM/IMServiceBuilderTest.php` +- Read: `src/Services/IM/IMServiceBuilder.php` +- Read: `src/Services/IMOpenLines/Session/Service/Session.php` + +- [ ] **Step 1: Write the failing builder test** + +Add a new assertion to `tests/Unit/Services/IM/IMServiceBuilderTest.php`: + +```php +use Bitrix24\SDK\Services\IM\Disk\Service\Disk; + +public function testGetDiskService(): void +{ + $this->assertInstanceOf(Disk::class, $this->serviceBuilder->disk()); + $this->assertSame($this->serviceBuilder->disk(), $this->serviceBuilder->disk()); +} +``` + +- [ ] **Step 2: Write the failing service contract test** + +Create `tests/Unit/Services/IM/Disk/Service/DiskTest.php`: + +```php +createMock(CoreInterface::class); + $core + ->expects($this->once()) + ->method('call') + ->with( + 'im.disk.folder.get', + [ + 'CHAT_ID' => 17, + 'DIALOG_ID' => 'chat17', + ], + ApiVersion::v1 + ) + ->willReturn( + new Response( + new MockResponse(json_encode(['result' => ['ID' => 5153]], JSON_THROW_ON_ERROR)), + new Command('im.disk.folder.get', []), + new ApiLevelErrorHandler(new NullLogger()), + new NullLogger() + ) + ); + + $result = (new Disk($core, new NullLogger()))->getFolderId(17, 'chat17'); + + $this->assertInstanceOf(FolderIdResult::class, $result); + $this->assertSame(5153, $result->getId()); + } +} +``` + +- [ ] **Step 3: Run the targeted unit tests to verify they fail** + +Run: + +```bash +docker compose run --rm php-cli vendor/bin/phpunit tests/Unit/Services/IM/IMServiceBuilderTest.php tests/Unit/Services/IM/Disk/Service/DiskTest.php --display-warnings +``` + +Expected: +- `IMServiceBuilderTest` fails because `disk()` does not exist yet +- `DiskTest` fails because the new service/result classes do not exist yet + +- [ ] **Step 4: Commit the red tests** + +```bash +git add tests/Unit/Services/IM/IMServiceBuilderTest.php tests/Unit/Services/IM/Disk/Service/DiskTest.php +git commit -m "test: lock IM disk service contract" +``` + +### Task 2: Implement the minimal IM Disk service and make unit tests pass + +**Files:** +- Create: `src/Services/IM/Disk/Result/FolderIdResult.php` +- Create: `src/Services/IM/Disk/Service/Disk.php` +- Modify: `src/Services/IM/IMServiceBuilder.php` +- Test: `tests/Unit/Services/IM/Disk/Service/DiskTest.php` +- Test: `tests/Unit/Services/IM/IMServiceBuilderTest.php` + +- [ ] **Step 1: Add the dedicated result wrapper** + +Create `src/Services/IM/Disk/Result/FolderIdResult.php`: + +```php +getCoreResponse()->getResponseData()->getResult()['ID']; + } +} +``` + +- [ ] **Step 2: Add the IM Disk service** + +Create `src/Services/IM/Disk/Service/Disk.php`: + +```php +core->call('im.disk.folder.get', $params)); + } +} +``` + +- [ ] **Step 3: Register the service in the IM builder** + +Update `src/Services/IM/IMServiceBuilder.php`: + +```php +use Bitrix24\SDK\Services\IM\Disk\Service\Disk; + +public function disk(): Disk +{ + if (!isset($this->serviceCache[__METHOD__])) { + $this->serviceCache[__METHOD__] = new Disk($this->core, $this->log); + } + + return $this->serviceCache[__METHOD__]; +} +``` + +- [ ] **Step 4: Re-run the targeted unit tests to verify green** + +Run: + +```bash +docker compose run --rm php-cli vendor/bin/phpunit tests/Unit/Services/IM/IMServiceBuilderTest.php tests/Unit/Services/IM/Disk/Service/DiskTest.php --display-warnings +``` + +Expected: +- both files pass +- no warnings or risky tests + +- [ ] **Step 5: Run the full unit suite to catch regressions** + +Run: + +```bash +make test-unit +``` + +Expected: +- `OK` +- the total test count increases from the current baseline + +- [ ] **Step 6: Commit the implementation** + +```bash +git add src/Services/IM/Disk/Result/FolderIdResult.php src/Services/IM/Disk/Service/Disk.php src/Services/IM/IMServiceBuilder.php +git add tests/Unit/Services/IM/IMServiceBuilderTest.php tests/Unit/Services/IM/Disk/Service/DiskTest.php +git commit -m "feat: add IM disk folder getter" +``` + +### Task 3: Add integration coverage, suite wiring, and changelog entry + +**Files:** +- Create: `tests/Integration/Services/IM/Disk/Service/DiskTest.php` +- Modify: `phpunit.xml.dist` +- Modify: `Makefile` +- Modify: `CHANGELOG.md` +- Test: `tests/Integration/Services/IM/Disk/Service/DiskTest.php` + +- [ ] **Step 1: Write the failing integration test** + +Create `tests/Integration/Services/IM/Disk/Service/DiskTest.php`: + +```php +diskService = Factory::getServiceBuilder()->getIMScope()->disk(); + } + + /** + * @throws BaseException + * @throws TransportException + */ + public function testGetFolderId(): void + { + $userId = (int)$this->diskService->core->call('PROFILE')->getResponseData()->getResult()['ID']; + $chatId = (int)$this->diskService->core->call( + 'im.chat.add', + [ + 'USERS' => [$userId], + 'TYPE' => 'CHAT', + 'TITLE' => sprintf('IM Disk Test %s', time()), + ] + )->getResponseData()->getResult()[0]; + + try { + $folderId = $this->diskService->getFolderId(dialogId: 'chat' . $chatId)->getId(); + + $this->assertGreaterThan(0, $folderId); + } finally { + $this->diskService->core->call('im.chat.leave', ['CHAT_ID' => $chatId]); + } + } +} +``` + +- [ ] **Step 2: Wire a dedicated PHPUnit suite and Make target** + +Update `phpunit.xml.dist` near the other IM suites: + +```xml + + ./tests/Integration/Services/IM/Disk/ + +``` + +Update `Makefile` help and target sections: + +```make +@echo "test-integration-im-disk - run IM Disk integration tests" + +.PHONY: test-integration-im-disk +test-integration-im-disk: + docker compose run --rm php-cli vendor/bin/phpunit --testsuite integration_tests_im_disk +``` + +- [ ] **Step 3: Add the changelog entry** + +Update `CHANGELOG.md` under `## 3.2.0 – UNRELEASED` / `### Added`: + +```markdown +- Added `Bitrix24\SDK\Services\IM\Disk\Service\Disk` with `getFolderId(?int $chatId = null, ?string $dialogId = null)` for `im.disk.folder.get`, plus dedicated `FolderIdResult`, IM builder registration, and focused unit/integration coverage ([#435](https://github.com/bitrix24/b24phpsdk/issues/435)) +``` + +- [ ] **Step 4: Run the targeted integration suite and verify it starts red, then green after wiring** + +Run: + +```bash +make test-integration-im-disk +``` + +Expected before wiring: +- `No tests executed` or unknown testsuite / missing file failure + +Expected after wiring and implementation: +- the new IM Disk integration test passes + +- [ ] **Step 5: Run the final verification set** + +Run: + +```bash +docker compose run --rm php-cli vendor/bin/phpunit tests/Unit/Services/IM/Disk/Service/DiskTest.php tests/Unit/Services/IM/IMServiceBuilderTest.php --display-warnings +make test-unit +make test-integration-im-disk +``` + +Expected: +- targeted unit tests pass +- full unit suite stays green +- IM Disk integration suite passes + +- [ ] **Step 6: Commit the integration and release wiring** + +```bash +git add tests/Integration/Services/IM/Disk/Service/DiskTest.php phpunit.xml.dist Makefile CHANGELOG.md +git commit -m "test: cover IM disk folder getter" +``` diff --git a/docs/superpowers/specs/2026-04-19-im-disk-folder-get-design.md b/docs/superpowers/specs/2026-04-19-im-disk-folder-get-design.md index b3ee7d34..7e34c49e 100644 --- a/docs/superpowers/specs/2026-04-19-im-disk-folder-get-design.md +++ b/docs/superpowers/specs/2026-04-19-im-disk-folder-get-design.md @@ -26,9 +26,14 @@ Out of scope: Create `Bitrix24\SDK\Services\IM\Disk\Service\Disk` as a regular `AbstractService` subclass with: - `#[ApiServiceMetadata(new Scope(['im']))]` -- one method `folderGet(): IntResult` +- one method `getFolderId(?int $chatId = null, ?string $dialogId = null): FolderIdResult` -The method calls `im.disk.folder.get` without parameters and returns `Bitrix24\SDK\Core\Result\IntResult`. +The method calls `im.disk.folder.get` and maps optional request parameters to `CHAT_ID` / `DIALOG_ID`. +Bitrix24 requires at least one of these parameters, but the SDK method can forward them as nullable values and leave cross-field validation to the API, matching existing service patterns. + +Because the response shape is `result.ID`, add a dedicated result wrapper instead of relying on a non-existent generic integer result class: +- `src/Services/IM/Disk/Result/FolderIdResult.php` +- `FolderIdResult::getId(): int` This keeps the implementation aligned with the existing service pattern in the SDK while avoiding speculative expansion for future `im.disk.*` methods. @@ -44,11 +49,14 @@ The accessor should follow the same caching behavior already used by `notify()` Unit coverage: - extend `tests/Unit/Services/IM/IMServiceBuilderTest.php` with an assertion that `disk()` returns the new service and is cached -- add `tests/Unit/Services/IM/Disk/Service/DiskTest.php` to verify the REST method name and empty parameter mapping for `folderGet()` +- add `tests/Unit/Services/IM/Disk/Service/DiskTest.php` to verify: + - the REST method name `im.disk.folder.get` + - `CHAT_ID` / `DIALOG_ID` parameter mapping + - the service returns `FolderIdResult` Integration coverage: - add `tests/Integration/Services/IM/Disk/Service/DiskTest.php` -- verify `Factory::getServiceBuilder()->getIMScope()->disk()->folderGet()` returns a positive folder id +- verify `Factory::getServiceBuilder()->getIMScope()->disk()->getFolderId(dialogId: 'chat...')` returns a positive folder id Test execution wiring: - add a dedicated suite to `phpunit.xml.dist` for the new integration test path @@ -67,5 +75,5 @@ Minimum verification for the implementation phase: ## Risks -- The REST response shape must match `IntResult` expectations. If the endpoint returns a different envelope than other integer-returning methods, the integration test should surface it immediately. +- The REST response shape is `result.ID`, so the new custom `FolderIdResult` must read the nested key correctly. - The new PHPUnit suite name and Make target must follow existing repository conventions to avoid drift in local developer workflows.