Skip to content

Commit 211ee14

Browse files
committed
Add Disk search and folder sharing wrappers (#659)
1 parent 2b49190 commit 211ee14

17 files changed

Lines changed: 828 additions & 0 deletions

File tree

‎.tasks/659/plan.md‎

Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
# Plan: Add missing legacy REST methods in disk scope (issue #659)
2+
3+
Status: implemented and locally verified; live sharing awaits an explicitly configured recipient.
4+
Target: SDK 3.x, `v3-dev`, explicitly selected by the owner.
5+
Branch: `codex/659-disk-methods`.
6+
Base: `origin/v3-dev` at `2b491902449c4dbf6bc198c96c945632d7f349ce`.
7+
8+
## Context and evidence
9+
10+
Issue: https://github.com/bitrix24/b24phpsdk/issues/659
11+
12+
These are legacy REST v1 endpoints in SDK 3.x; the SDK release line does not change the REST version.
13+
`make oa-schema-build` succeeded before research. None of the four candidates appears in the resulting OpenAPI snapshot; legacy documentation and controlled live requests are needed.
14+
15+
Research on 2026-09-30:
16+
17+
| Candidate | Evidence | Decision |
18+
| --- | --- | --- |
19+
| `disk.file.search` | Official MCP and English documentation found; live request with a unique nonexistent query and `TYPE=all` returned `[]`. | Implement; successful nonempty fixtures remain to be verified. |
20+
| `disk.folder.shareToUser` | Official MCP resolves the camel-case name; English documentation found. | Implement after disposable-fixture verification; no live mutation performed during planning. |
21+
| `disk.file.listallowedoperations` | Catalog entry; official MCP lookup and official-domain searches found no contract. Read-only invalid-ID probe requires `userId`, then returns `ERROR_NOT_FOUND`. | Exclude from public SDK additions pending public contract evidence. Not proven internal, unsupported or deprecated. |
22+
| `disk.folder.listallowedoperations` | Same evidence boundary as file operations. | Same exclusion; retain as an unresolved gap. |
23+
24+
Sources:
25+
- https://apidocs.bitrix24.com/api-reference/disk/file/disk-file-search.html
26+
- https://apidocs.bitrix24.com/api-reference/disk/folder/disk-folder-share-to-user.html
27+
28+
Both documented endpoints require the `disk` scope. Neither documentation page marks the method deprecated. Search is permission-filtered. Record the actual permission behavior for sharing from the official page and the live disposable fixture before publishing support claims.
29+
30+
The existing coverage Make target reports exactly these four missing Disk endpoints and zero SDK-only Disk methods. Its global totals changed from 747/1160 (64.40%) to 760/1160 (65.52%) between runs in the shared primary checkout; do not attribute that difference to this task. Capture the authoritative before/after pair in the isolated worktree. The first noninteractive run aborted at its menu; the subsequent interactive run completed successfully.
31+
32+
## Design and alternatives
33+
34+
Recommended: extend the existing File and Folder services for the two documented contracts. Return a dedicated mixed search-item type and reuse `UpdatedItemResult` for sharing. Record the two unresolved methods without speculative wrappers.
35+
36+
Alternatives considered:
37+
1. Implement all four from observed responses: rejected because catalog membership and error messages do not establish a public supported contract.
38+
2. Block the entire issue until all four are documented: unnecessary for the two documented methods; keep unresolved items explicit instead.
39+
40+
The existing `FileItemResult` is file-only and extends `AbstractItem`; `FolderChildrenResult` labels every item as `FolderItemResult`. Reusing either for mixed search results would misrepresent the contract. A new search item avoids changing existing public result semantics. `FolderOperationResult::isSuccess()` expects an `ID` field and cannot represent the documented boolean sharing response. `UpdatedItemResult` reads the core-normalized scalar boolean at index zero and fits that contract. Verify both true and false in unit tests. `AddedItemResult` is not applicable.
41+
42+
## Files to create
43+
44+
All PHP files use strict types and repository headers/import conventions.
45+
46+
### `src/Services/Disk/File/Result/FileSearchResult.php`
47+
Namespace `Bitrix24\SDK\Services\Disk\File\Result`.
48+
Extends `Core\Result\AbstractResult`.
49+
Public `items(): array` with `@return FileSearchItemResult[]`, mapping the flat `result` array. Empty results return `[]`. Retain pagination through the inherited core response (`getResponseData()->getPagination()->getNextItem()`).
50+
51+
### `src/Services/Disk/File/Result/FileSearchItemResult.php`
52+
Same namespace; extends `Core\Result\AbstractAnnotatedItem` and imports `Carbon\CarbonImmutable`.
53+
Use the documented union of file/folder fields, including search-specific links and folder identity, with nullable annotations for fields omitted on one object kind. Keep `TYPE` as the discriminator. Confirm scalar/date casts and all actual keys against controlled live results and both field metadata methods. Do not add an OpenAPI entity attribute for an entity absent from the schema.
54+
55+
Generator-first step, before writing the item:
56+
```bash
57+
php bin/console b24-dev:result-item-generator disk.file.search --stage=all
58+
```
59+
Inspect generated artifacts before adopting them. Current generator inspection reveals three constraints: the default target is the existing `FileItemResult.php`, sample parameters are empty (search needs QUERY), and the default documentation object selector is `result-item`. Run the generator in a disposable staging copy under the task directory to avoid overwriting the released item. Record the exact outcome here. If it cannot generate this mixed item, document the concrete failure before creating it manually; do not broaden this issue into generator development.
60+
61+
### Unit tests
62+
- `tests/Unit/Services/Disk/File/Service/FileTest.php`: request method and casing, default/explicit arguments, empty/nonempty file/folder/mixed responses, paging offset, core exceptions.
63+
- `tests/Unit/Services/Disk/File/Result/FileSearchResultTest.php`: item classes and typed access, omitted optional fields, dates, nulls, empty results, preserved pagination.
64+
- `tests/Unit/Services/Disk/Folder/Service/FolderTest.php`: share request mapping, true/false normalized result, exception propagation.
65+
66+
Use `PHPUnit\Framework\TestCase`, the existing core mocks, and explicit `CoversClass` attributes. Run each new service test red before implementation.
67+
68+
### Integration tests
69+
- `tests/Integration/Services/Disk/File/Service/FileSearchTest.php`: disposable folder and uploaded text file with unique searchable names; file, folder, mixed and scoped searches; empty results. Use bounded polling for indexing, fail with a clear timeout, and always clean up in `finally`. Do not silently pass an empty result for a successful-search scenario.
70+
- `tests/Integration/Services/Disk/Folder/Service/FolderShareToUserTest.php`: share only a newly created private disposable folder to an explicitly configured active test recipient. Verify success and, where test credentials support it, recipient access. Report absence of recipient credentials separately from a passing access verification. Clean up in `finally`; never grant access to existing folders or create/invite users.
71+
- `tests/Integration/Services/Disk/File/Result/FileSearchItemResultAnnotationsTest.php`: separate shared-assertion checks named `testAllSystemFieldsAnnotated...` and `testAllSystemFieldsHasValidTypeAnnotation...` against File and Folder `getFields()->getFieldsDescription()`, normalize metadata keys as existing Disk tests do. Add actual raw-search field completeness and magic-getter casting checks against a nonempty fixture.
72+
73+
## Files to modify
74+
75+
### `src/Services/Disk/File/Service/File.php`
76+
Import `FileSearchResult` and add:
77+
```php
78+
public function search(string $query, string $type = 'file', array $filter = [], int $start = 0): FileSearchResult
79+
```
80+
Document `$type` as `file|folder|all` and `$filter` as optional integer `STORAGE_ID`/`FOLDER_ID` keys. Map to `QUERY`, `TYPE`, `FILTER` and lowercase `start`. Omit an empty FILTER so it is not encoded as a JSON list. Leave API validation and errors to the core; do not invent client-side truncation or normalization. Add `ApiEndpointMetadata` with canonical lowercase endpoint and the English documentation URL.
81+
82+
### `src/Services/Disk/Folder/Service/Folder.php`
83+
Import `Core\Result\UpdatedItemResult` and add:
84+
```php
85+
public function shareToUser(int $id, int $userId, string $taskName): UpdatedItemResult
86+
```
87+
Map `id`, `userId`, `taskName` exactly. Document the four supported `disk_access_*` values. Register canonical lowercase `disk.folder.sharetouser` metadata and English documentation URL. Keep ordinary SDK exception propagation.
88+
89+
### `phpunit.xml.dist` and `Makefile`
90+
Existing Disk/file suites already cover the proposed files. Add the missing focused folder suite and target:
91+
```xml
92+
<testsuite name="integration_tests_disk_folder">
93+
<directory>./tests/Integration/Services/Disk/Folder/</directory>
94+
</testsuite>
95+
```
96+
```make
97+
.PHONY: test-integration-disk-folder
98+
test-integration-disk-folder:
99+
docker compose run --rm php-cli $(PHPUNIT) --testsuite integration_tests_disk_folder
100+
```
101+
Use this suite or an explicit filter to limit live mutations to disposable fixtures for this task.
102+
103+
### `CHANGELOG.md`
104+
Under the existing top-level Unreleased / Added section:
105+
`- Added Disk file/folder search and folder sharing to users; undocumented allowed-operation endpoints remain unresolved ([#659](https://github.com/bitrix24/b24phpsdk/issues/659)).`
106+
Do not rename the repository's existing `## Unreleased` heading. No agent or skill configuration changes are planned.
107+
108+
### Task evidence
109+
Add `.tasks/659/research.md` with sanitized live findings, generator outcome, public-contract exclusions, before/after coverage, test commands and limitations. Do not commit credentials, raw portal URLs containing webhook secrets, download links, or user data.
110+
111+
## Deptrac compliance
112+
113+
Dependencies remain Services -> Core/Attributes and existing external Carbon/PSR contracts. Tests depend on the existing factory/custom assertions. No new cross-scope dependency or skipped violation is necessary. Existing DiskServiceBuilder accessors already expose both services; no builder edit is needed.
114+
115+
## Execution and verification
116+
117+
1. Install dependencies in the isolated worktree, refresh schema there with `make -s oa-schema-build` while suppressing secret-bearing command echoes, and capture clean baseline unit results and coverage.
118+
2. Verify public contracts on disposable fixtures. Stop implementing any endpoint whose observed response contradicts its documentation; record the evidence and resolve the discrepancy first.
119+
3. Write failing unit tests, run generator in staging, implement the two wrappers/results, then add live tests and changelog.
120+
4. Run the light gates sequentially; fix failures before starting integrations:
121+
```bash
122+
make lint-cs-fixer
123+
make lint-rector
124+
make lint-phpstan
125+
make lint-deptrac
126+
make test-unit
127+
```
128+
5. Run new targeted Disk integration tests and annotation checks. Run broader Disk suites only after reviewing their existing fixture safety. Report skipped/unavailable scenarios explicitly.
129+
6. Re-run `make -s sdk-coverage-v1-show` interactively (missing methods -> disk -> exit) on the same isolated checkout, and `make -s sdk-coverage-v3-show`. Expect two resolved Disk gaps; the two unverified allowed-operation methods remain. Legacy additions must not be misreported as REST v3 coverage.
130+
7. Run `git diff --check`, inspect tracked/untracked files, review the complete diff and evidence. Follow the repository PR template; any push to a PR requires polling CI to terminal status. Do not merge or claim a release/live authorization scenario without its evidence.
131+
132+
## Plan review
133+
134+
- Unambiguity: selected SDK branch, exact API mappings, result responsibilities, test locations and exclusion policy are explicit.
135+
- Non-contradiction: REST v1 remains on the owner-selected SDK 3.x branch; mixed records use a dedicated result and boolean sharing uses the compatible core wrapper.
136+
- No gaps: every candidate has an evidence-backed disposition or explicit unresolved status; generators, metadata, fixtures, annotation tests, changelog, coverage and quality gates are included.
137+
138+
## Generator execution evidence
139+
140+
The required CLI was attempted before manual result-item creation in a disposable Docker staging directory. It failed with `Unable to determine the current git branch`; the repository PHP Docker image has no `git` executable. Host PHP is unavailable. Source inspection additionally confirms that the URL resolver requires an existing endpoint metadata attribute, default sample parameters omit required QUERY, and the target would overwrite the existing file-only FileItemResult. Manual creation of the dedicated mixed FileSearchItemResult is therefore used without changing generator infrastructure.
141+
142+
## Implementation adjustments
143+
144+
- Added `tests/Integration/Services/Disk/TemporaryDiskFixture.php` to own disposable personal-drive objects and checked cleanup, shared by the new integration tests.
145+
- Added optional `BITRIX24_DISK_TEST_RECIPIENT_ID` to `tests/.env`; no user is selected implicitly.
146+
- The shared completeness assertion compares exact field sets. The mixed search-item tests therefore compare the union of live file/folder metadata and observed search fields, and separately the union of observed mixed-search fields. Type validation still checks both metadata contracts independently.
147+
- No recipient credentials were supplied: the integration scenario for assigning permissions is explicitly skipped, and recipient-side authorization is unverified. This does not weaken the request/boolean/error unit tests.
148+
- See `research.md` for measured results and generator limitations. The two public wrappers are implemented; the undocumented operations remain unresolved, not classified as deprecated/internal based on missing documentation alone.

‎.tasks/659/research.md‎

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
# Issue #659: Disk contract and verification evidence
2+
3+
Date: 2026-09-30. SDK target: 3.x (`v3-dev`), REST methods: legacy v1.
4+
Base commit: `2b491902449c4dbf6bc198c96c945632d7f349ce`.
5+
6+
## Public contracts
7+
8+
Official sources retrieved through Bitrix24 documentation MCP and English documentation:
9+
- https://apidocs.bitrix24.com/api-reference/disk/file/disk-file-search.html
10+
- https://apidocs.bitrix24.com/api-reference/disk/folder/disk-folder-share-to-user.html
11+
12+
`disk.file.search`: scope `disk`, readable objects only. Confirmed with a newly created personal-drive folder, nested folder and text file. The response is a flat array containing both `TYPE=file` and `TYPE=folder`. Search-specific links and kind-specific fields are represented by a dedicated annotated item. A unique missing query returned an empty array. File/folder filtering, combined storage/folder scoping and offset 1 passed live tests. Search indexing is bounded by a 30-second retry window.
13+
14+
`disk.folder.shareToUser`: documented scope `disk`, requires the caller to have Share permission and forbids granting a higher level than the caller holds. Not marked deprecated. The documented scalar boolean fits `Core\Result\UpdatedItemResult`; unit tests cover true/false, exact request names and exception propagation. No live success or recipient-side authorization claim: `BITRIX24_DISK_TEST_RECIPIENT_ID` was not supplied, so its live test skipped before creating objects. It requires an explicitly configured active recipient different from the fixture owner, grants only read access and deletes the disposable parent tree in `finally`.
15+
16+
All created research/search fixtures were removed through `disk.folder.deletetree` and returned true. Tests fail when cleanup reports false. Setup-failure cleanup preserves the original error as the exception cause.
17+
18+
## Unresolved methods
19+
20+
`disk.file.listallowedoperations` and `disk.folder.listallowedoperations` remain excluded from SDK public wrappers under the approved plan. Official MCP exact-name/broad searches and official-domain web searches did not resolve public documentation. Neither absence of documentation nor catalog presence proves deprecation, internal status or supported integration availability.
21+
22+
Controlled probes on our disposable objects, with the current user ID, both returned an array of operation names: `disk_add`, `disk_create_wf`, `disk_delete`, `disk_destroy`, `disk_edit`, `disk_read`, `disk_restore`, `disk_rights`, `disk_settings`, `disk_sharing`, `disk_start_bp`. Invalid-ID calls established a required `userId` and returned `ERROR_NOT_FOUND` once supplied. These observations do not establish cross-user permissions, stable public guarantees or deprecation status. Public support evidence is still required before exposing these APIs.
23+
24+
## Generator
25+
26+
Attempted `php bin/console b24-dev:result-item-generator disk.file.search --stage=all` in a disposable Docker staging directory, before writing the new item. Failed with `Unable to determine the current git branch`: the PHP image does not contain git; host PHP is absent. An attempted staging git initialization reported `git: not found`. No installed packages or generator source were changed to work around this.
27+
28+
Source inspection identified further constraints: only feature/bugfix branch names are parsed; the documentation URL resolver discovers existing SDK metadata, the default sample lacks required QUERY, and the inferred output would overwrite the existing file-only FileItemResult. The approved dedicated mixed item was written manually after recording these limitations in the plan. Existing FileItemResult remains unchanged.
29+
30+
## Measured coverage
31+
32+
Measured using repository Make targets, not hand-counting implementations.
33+
34+
| Metric | Before | After |
35+
| --- | ---: | ---: |
36+
| Live legacy REST v1 portal methods | 1160 | 1160 |
37+
| Covered legacy methods | 760 | 762 |
38+
| Uncovered legacy methods | 400 | 398 |
39+
| Legacy coverage | 65.52% | 65.69% |
40+
| SDK-only legacy inventory | 441 | 441 |
41+
| Missing Disk methods | 4 | 2 |
42+
43+
`make -s sdk-coverage-v1-show` ran interactively in the isolated worktree. The final Disk missing-method list contains exactly the two allowed-operations methods. Disk SDK-only inventory is zero; endpoint casing is normalized by the coverage utility.
44+
45+
`make -s sdk-coverage-v3-show`: 314 OpenAPI methods, 98 covered, 216 uncovered, 4 SDK-only, 31.21%. Disk REST v3: 18 methods, zero covered. The legacy additions are not presented as REST v3 implementations. The refreshed OpenAPI snapshot has no entry for any of the four legacy candidates.
46+
47+
## Verification
48+
49+
- Fresh dependencies: `make -s composer-install` succeeded; PHP 8.4.21 / PHPUnit 12.5.37.
50+
- `make -s oa-schema-build` succeeded after copying existing ignored local test settings into the isolated worktree. The first attempt had an empty webhook; no source change was required.
51+
- Baseline `make test-unit`: 1857 tests / 6029 assertions, 11 existing deprecations, exit 0.
52+
- TDD red: 7 new unit scenarios failed because the methods/results did not exist. Green: 7 tests / 45 assertions.
53+
- `make lint-cs-fixer`: passed first run.
54+
- `make lint-rector`: passed, including after test adjustments.
55+
- `make lint-phpstan`: passed, including after test adjustments.
56+
- `make lint-deptrac`: zero violations.
57+
- `make test-unit`: 1864 tests / 6074 assertions, same 11 existing deprecations, exit 0.
58+
- Targeted integration command below: 5 tests / 70 assertions, 4 passed, 1 explicitly skipped (sharing recipient missing). Initially two annotation tests failed because the shared helper requires equality; fixed the mixed-entity test to use actual field unions, then reran successfully.
59+
- No broad legacy Disk CRUD suite was run: this task uses only reviewed disposable fixtures.
60+
- Independent read-only review: no production blockers; cleanup diagnostic suggestion implemented.
61+
62+
```bash
63+
make test-file path='tests/Integration/Services/Disk/File/Service/FileSearchTest.php tests/Integration/Services/Disk/File/Result/FileSearchItemResultAnnotationsTest.php tests/Integration/Services/Disk/Folder/Service/FolderShareToUserTest.php'
64+
```
65+
66+
## Remaining validation
67+
68+
Configure `BITRIX24_DISK_TEST_RECIPIENT_ID` locally and rerun the sharing test. Verify recipient access separately with appropriate recipient credentials. Obtain a public contract for both allowed-operation endpoints before adding SDK wrappers. No release, merge or full four-endpoint support is claimed.

0 commit comments

Comments
 (0)