docs(13): add phase 13 planning artifacts — virtual local cloud operations
Plans 01–11, CONTEXT, PATTERNS, RESEARCH, REVIEW-FIX, and updated SUMMARY and CONTEXT for the virtual-local-cloud-operations phase. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 4.6
parent
73328eee0b
commit
b67e77dd69
@@ -0,0 +1,559 @@
|
||||
# Phase 13: Virtual-Local Cloud Operations - Pattern Map
|
||||
|
||||
**Mapped:** 2026-06-22
|
||||
**Files analyzed:** 33 likely new/modified files
|
||||
**Analogs found:** 33 / 33 (6 are composite/no-exact)
|
||||
|
||||
## Inherited phase rules that stay locked
|
||||
|
||||
- Keep `StorageBrowser.vue` as the only file browser. Cloud behavior extends emitted events and props; it does not fork layout. See [AGENTS.md], Phase 12 patterns, and [frontend/src/components/storage/StorageBrowser.vue].
|
||||
- Keep cloud routing owner-scoped by connection UUID plus opaque `provider_item_id`; never parse provider refs on the client. Primary analogs: [backend/api/cloud/browse.py:183], [frontend/src/views/CloudFolderView.vue:42], [frontend/src/components/cloud/CloudFolderTreeItem.vue:42].
|
||||
- Keep metadata reconciliation centralized in `reconcile_cloud_listing` / `apply_listing_and_finalize`; provider adapters never write `cloud_items` rows directly. Primary analogs: [backend/services/cloud_items.py:163], [backend/services/cloud_items.py:271].
|
||||
- Keep service-layer exception discipline: services raise domain errors / `ValueError`, routers translate to `HTTPException`. Primary analogs: [AGENTS.md], [backend/services/cloud_items.py:1].
|
||||
- Keep audit writes metadata-only and in the caller’s transaction. Primary analog: [backend/services/audit.py:27].
|
||||
|
||||
## File Classification
|
||||
|
||||
| Likely file | Role | Data flow | Closest analog | Match |
|
||||
|---|---|---|---|---|
|
||||
| `backend/api/cloud/operations.py` | route | request-response + file-I/O + CRUD | `backend/api/cloud/browse.py`, `backend/api/documents/content.py`, `backend/api/documents/upload.py`, `backend/api/folders.py` | composite |
|
||||
| `backend/api/cloud/connections.py` | route | request-response | itself + `backend/tasks/cloud_tasks.py` | exact |
|
||||
| `backend/api/cloud/schemas.py` | schema | transform | itself | exact |
|
||||
| `backend/services/cloud_operations.py` | service | CRUD + transform | `backend/services/cloud_items.py` + `backend/services/audit.py` | composite |
|
||||
| `backend/services/cloud_items.py` | service | CRUD + batch reconcile | itself | exact |
|
||||
| `backend/storage/cloud_base.py` | contract | transform | itself | exact |
|
||||
| `backend/storage/cloud_backend_factory.py` | factory | request-response | itself | exact |
|
||||
| `backend/storage/google_drive_backend.py` | provider | file-I/O + CRUD | itself | exact |
|
||||
| `backend/storage/onedrive_backend.py` | provider | file-I/O + CRUD | itself | exact |
|
||||
| `backend/storage/webdav_backend.py` | provider | file-I/O + CRUD | itself | exact |
|
||||
| `backend/storage/nextcloud_backend.py` | provider | file-I/O | itself + `webdav_backend.py` | exact |
|
||||
| `backend/tasks/cloud_tasks.py` | task | event-driven + batch | itself | exact |
|
||||
| `frontend/src/api/cloud.js` | API client | request-response | itself + `frontend/src/api/documents.js` | exact |
|
||||
| `frontend/src/stores/cloudConnections.js` | store | state transform | itself | exact |
|
||||
| `frontend/src/views/CloudFolderView.vue` | thin view | request-response + queue orchestration | itself + `frontend/src/views/FileManagerView.vue` | exact |
|
||||
| `frontend/src/components/storage/StorageBrowser.vue` | smart component | CRUD UI + queue UI | itself | exact |
|
||||
| `frontend/src/components/settings/SettingsCloudTab.vue` | smart component | request-response | itself | exact |
|
||||
| `frontend/src/components/cloud/CloudCredentialModal.vue` | modal/form | request-response | itself | exact |
|
||||
| `backend/tests/test_cloud.py` | integration test | request-response | itself | exact |
|
||||
| `backend/tests/test_cloud_security.py` | security-negative test | request-response | itself | exact |
|
||||
| `backend/tests/test_cloud_backends.py` | provider unit/contract test | file-I/O | itself | exact |
|
||||
| `backend/tests/test_cloud_provider_contract.py` | provider contract test | transform | itself | exact |
|
||||
| `backend/tests/test_cloud_items.py` | service/task test | CRUD + batch | itself | exact |
|
||||
| `backend/tests/test_cloud_mutations.py` | integration/contract test | CRUD + file-I/O | `test_cloud.py` + `test_cloud_security.py` + `test_cloud_items.py` | composite |
|
||||
| `backend/tests/test_cloud_reconnect.py` | integration/security test | request-response + task | `test_cloud.py` + `test_cloud_items.py` | composite |
|
||||
| `backend/tests/test_cloud_audit.py` | integration test | transform | `backend/tests/test_audit.py` + `backend/tests/test_cloud.py` | partial |
|
||||
| `frontend/src/views/__tests__/CloudFolderView.test.js` | view unit test | request-response | itself | exact |
|
||||
| `frontend/src/views/__tests__/CloudFolderRenderedFlow.test.js` | rendered-flow test | request-response | itself | exact |
|
||||
| `frontend/src/components/storage/__tests__/StorageBrowser.capabilities.test.js` | component unit test | CRUD UI | itself | exact |
|
||||
| `frontend/src/components/storage/__tests__/StorageBrowser.cloud-queue.test.js` | component unit test | queue UI | `StorageBrowser.capabilities.test.js` + `FileManagerView.vue` upload flow | composite |
|
||||
| `frontend/src/views/__tests__/CloudFolderOpenPreview.test.js` | rendered-flow test | request-response + preview | `CloudFolderRenderedFlow.test.js` + document content helpers | composite |
|
||||
| `frontend/src/stores/__tests__/cloudConnections.test.js` | store unit test | state transform | itself | exact |
|
||||
| `frontend/src/components/settings/__tests__/SettingsCloudTab.test.js` | component unit test | request-response | itself | exact |
|
||||
| `frontend/src/components/settings/__tests__/SettingsCloudTab.health.test.js` | component unit test | health/reconnect UI | `SettingsCloudTab.test.js` + store freshness tests | partial |
|
||||
|
||||
## Pattern Assignments
|
||||
|
||||
### `backend/api/cloud/operations.py`
|
||||
|
||||
**Primary analogs**
|
||||
|
||||
- Route/dependency skeleton: [backend/api/cloud/browse.py:19-42], [backend/api/cloud/browse.py:183-214]
|
||||
- Connection settings/test/update/disconnect patterns: [backend/api/cloud/connections.py:368-434], [backend/api/cloud/connections.py:492-579], [backend/api/cloud/connections.py:604-635]
|
||||
- Authorized byte streaming and cloud-error translation: [backend/api/documents/content.py:50-103]
|
||||
- Multipart upload proxy pattern: [backend/api/documents/upload.py:122-198]
|
||||
- Local create/rename/delete/move handler shape: [backend/api/folders.py:122-160], [backend/api/folders.py:232-265], [backend/api/folders.py:268-351], [backend/api/folders.py:357-375]
|
||||
|
||||
**Copy these conventions**
|
||||
|
||||
- Use `get_regular_user`, `get_db`, `account_limiter`, and `request.state.current_user = current_user` exactly like cloud browse/connections.
|
||||
- Resolve ownership through `resolve_owned_connection(...)` before any provider action.
|
||||
- Translate provider/service failures to controlled `HTTPException` messages like document content/upload already do; do not surface raw provider text.
|
||||
- For open/preview/download, copy the `StreamingResponse` + `CloudConnectionError` mapping from `content.py`, but key authorization off cloud connection/item ownership instead of `Document`.
|
||||
- For upload/create/rename/move/delete, keep router bodies thin: validate request shape, call service, return whitelisted schema, write no inline reconciliation logic.
|
||||
|
||||
**No exact analog**
|
||||
|
||||
- There is no existing single cloud route file that combines owner-scoped connection actions with content proxying and normalized mutation results. Keep the file shaped like `browse.py`, but borrow handler bodies from the document/folder routers above.
|
||||
|
||||
### `backend/api/cloud/connections.py`
|
||||
|
||||
**Primary analogs**
|
||||
|
||||
- WebDAV connect and health-check flow: [backend/api/cloud/connections.py:368-434]
|
||||
- Credential update flow: [backend/api/cloud/connections.py:492-579]
|
||||
- Display-name rename: [backend/api/cloud/connections.py:582-601]
|
||||
- Disconnect confirmation target: [backend/api/cloud/connections.py:604-635]
|
||||
|
||||
**Copy these conventions**
|
||||
|
||||
- Keep `_get_owned_connection(...)` for owner checks.
|
||||
- Keep URL validation before backend construction.
|
||||
- Keep audit writes adjacent to the successful state change.
|
||||
- If reconnect/test routes are added, model them after `connect_webdav` + `update_webdav_credentials`: validate, health-check, persist, audit, commit.
|
||||
|
||||
**Phase 13-specific caution**
|
||||
|
||||
- The current OAuth callback always inserts a new row (`_upsert_cloud_connection = _insert_cloud_connection`) at [backend/api/cloud/connections.py:128-129]. Do not copy that behavior for reconnect; Phase 13 needs connection-row preservation.
|
||||
|
||||
### `backend/api/cloud/schemas.py`
|
||||
|
||||
**Primary analog**
|
||||
|
||||
- Current cloud whitelist schemas: [backend/api/cloud/schemas.py:17-86]
|
||||
|
||||
**Copy these conventions**
|
||||
|
||||
- Add only explicit allowlist response/request models.
|
||||
- Keep validators at schema level for user-submitted fields, as in `ConnectionRenameRequest`.
|
||||
- Keep credentials, provider URLs, and raw provider payloads out of every schema.
|
||||
|
||||
**No exact analog**
|
||||
|
||||
- There is no current normalized mutation-result schema. Add new result types beside `CloudBrowseResponse`, not in routers.
|
||||
|
||||
### `backend/services/cloud_operations.py`
|
||||
|
||||
**Primary analogs**
|
||||
|
||||
- Owner resolution / stable identity / folder freshness: [backend/services/cloud_items.py:38-62], [backend/services/cloud_items.py:97-159], [backend/services/cloud_items.py:271-342]
|
||||
- Metadata-only audit helper: [backend/services/audit.py:27-58]
|
||||
- Background refresh orchestration: [backend/tasks/cloud_tasks.py:50-163]
|
||||
|
||||
**Copy these conventions**
|
||||
|
||||
- Accept UUIDs/strings the same way `cloud_items.py` does.
|
||||
- Raise domain errors or `ValueError`, never `HTTPException`.
|
||||
- Reconcile and finalize folder state before returning success.
|
||||
- Write audit rows through `write_audit_log(...)`, letting the caller own the transaction.
|
||||
|
||||
**No exact analog**
|
||||
|
||||
- There is no existing service that turns provider mutation outcomes into normalized API results. Treat this as the new orchestration seam; do not push that logic into routers or providers.
|
||||
|
||||
### `backend/services/cloud_items.py`
|
||||
|
||||
**Primary analog**
|
||||
|
||||
- Keep using the current service itself.
|
||||
|
||||
**Copy these conventions**
|
||||
|
||||
- Owner resolution: [backend/services/cloud_items.py:38-62]
|
||||
- Stable upsert-by-`(connection_id, provider_item_id)`: [backend/services/cloud_items.py:97-159]
|
||||
- Complete-vs-incomplete reconciliation: [backend/services/cloud_items.py:163-213]
|
||||
- Single freshness gate: [backend/services/cloud_items.py:271-342]
|
||||
- Controlled folder-state updates: [backend/services/cloud_items.py:345-384]
|
||||
|
||||
**Planner note**
|
||||
|
||||
- Any mutation service should feed its authoritative provider result back through these primitives instead of adding direct ORM writes elsewhere.
|
||||
|
||||
### `backend/storage/cloud_base.py`
|
||||
|
||||
**Primary analog**
|
||||
|
||||
- Keep extending the current contract file itself: [backend/storage/cloud_base.py:27-245]
|
||||
|
||||
**Copy these conventions**
|
||||
|
||||
- Stable action vocabulary and reason codes live here, not in routers or Vue.
|
||||
- Immutable normalized dataclasses (`CloudCapability`, `CloudResource`, `CloudListing`) are the pattern to follow for new mutation result types.
|
||||
- Keep provider-specific branching out of the shared contract.
|
||||
|
||||
**No exact analog**
|
||||
|
||||
- There is no Phase-13 mutation result/value type yet. New normalized result dataclasses belong here.
|
||||
|
||||
### `backend/storage/cloud_backend_factory.py`
|
||||
|
||||
**Primary analog**
|
||||
|
||||
- Current factory and adapter assertion: [backend/storage/cloud_backend_factory.py:7-66]
|
||||
|
||||
**Copy these conventions**
|
||||
|
||||
- Keep lazy imports.
|
||||
- Keep Nextcloud URL normalization in the factory boundary.
|
||||
- If a mutable-adapter subclass is introduced, assert the interface here instead of switching on provider names in routers.
|
||||
|
||||
### `backend/storage/google_drive_backend.py`
|
||||
|
||||
**Primary analog**
|
||||
|
||||
- Existing upload/download/delete/list/capability patterns: [backend/storage/google_drive_backend.py:140-172], [backend/storage/google_drive_backend.py:174-207], [backend/storage/google_drive_backend.py:257-272], [backend/storage/google_drive_backend.py:276-417]
|
||||
|
||||
**Copy these conventions**
|
||||
|
||||
- Wrap SDK calls in `asyncio.to_thread()`.
|
||||
- Centralize provider error classification in helper methods like `_handle_http_error(...)`.
|
||||
- Normalize provider metadata into `CloudResource` objects.
|
||||
- Keep the backend stateless: it may return refreshed credentials or typed errors, but must not write DB state directly.
|
||||
|
||||
### `backend/storage/onedrive_backend.py`
|
||||
|
||||
**Primary analog**
|
||||
|
||||
- Existing token lifecycle + upload/download/delete/list/capabilities: [backend/storage/onedrive_backend.py:83-154], [backend/storage/onedrive_backend.py:158-218], [backend/storage/onedrive_backend.py:220-245], [backend/storage/onedrive_backend.py:280-409]
|
||||
|
||||
**Copy these conventions**
|
||||
|
||||
- Refresh token on demand inside the backend boundary.
|
||||
- Keep Graph HTTP async with `httpx`.
|
||||
- Keep `CloudConnectionError` unified across providers.
|
||||
|
||||
**Gap to flag**
|
||||
|
||||
- There is no persistence pattern yet for refreshed OneDrive credentials; Phase 13 must add one above the backend, not inside it.
|
||||
|
||||
### `backend/storage/webdav_backend.py`
|
||||
|
||||
**Primary analog**
|
||||
|
||||
- Existing SSRF, PUT/GET/DELETE, list-folder, and capability patterns: [backend/storage/webdav_backend.py:66-137], [backend/storage/webdav_backend.py:139-174], [backend/storage/webdav_backend.py:222-237], [backend/storage/webdav_backend.py:241-370]
|
||||
|
||||
**Copy these conventions**
|
||||
|
||||
- Re-run `validate_cloud_url(...)` before every outbound request.
|
||||
- Keep basename/path-traversal protection when building destination names.
|
||||
- Keep WebDAV-specific overwrite/conflict semantics inside this provider file.
|
||||
|
||||
### `backend/storage/nextcloud_backend.py`
|
||||
|
||||
**Primary analog**
|
||||
|
||||
- Minimal specialization over WebDAV: [backend/storage/nextcloud_backend.py:38-90]
|
||||
|
||||
**Copy these conventions**
|
||||
|
||||
- Keep Nextcloud as a tiny specialization, not a parallel backend.
|
||||
- If Phase 13 needs Nextcloud-specific mutation quirks, implement them as narrow overrides that preserve the public contract.
|
||||
|
||||
### `backend/tasks/cloud_tasks.py`
|
||||
|
||||
**Primary analog**
|
||||
|
||||
- Current refresh worker: [backend/tasks/cloud_tasks.py:50-163], [backend/tasks/cloud_tasks.py:175-216]
|
||||
|
||||
**Copy these conventions**
|
||||
|
||||
- Revalidate ownership inside the worker.
|
||||
- Decrypt credentials inside the worker, never in broker payload.
|
||||
- Use sentinel exceptions to separate retryable provider failures from terminal auth failures.
|
||||
- Reuse this task model if reconnect or stale-metadata recovery needs post-success refresh.
|
||||
|
||||
### `frontend/src/api/cloud.js`
|
||||
|
||||
**Primary analogs**
|
||||
|
||||
- Existing connection browse/config/update methods: [frontend/src/api/cloud.js:11-18], [frontend/src/api/cloud.js:36-50], [frontend/src/api/cloud.js:73-88]
|
||||
- Cloud upload helper shape: [frontend/src/api/documents.js:48-54]
|
||||
|
||||
**Copy these conventions**
|
||||
|
||||
- Keep all Phase 13 cloud endpoints behind this domain module.
|
||||
- Keep connection UUID and encoded `parent_ref` handling here.
|
||||
- Add new `open/preview/upload/create/rename/move/delete/test/reconnect` helpers here instead of calling raw URLs from views/components.
|
||||
|
||||
### `frontend/src/stores/cloudConnections.js`
|
||||
|
||||
**Primary analog**
|
||||
|
||||
- Current store state and reset/mapping helpers: [frontend/src/stores/cloudConnections.js:33-138]
|
||||
|
||||
**Copy these conventions**
|
||||
|
||||
- Keep server state translation centralized in the store.
|
||||
- Keep sessionStorage limited to folder refs only.
|
||||
- Reset browse state on connection switch.
|
||||
|
||||
**No exact analog**
|
||||
|
||||
- There is no queue/conflict/resume state machine yet. Add it here only if it is shared across cloud views/settings; otherwise keep queue orchestration in `CloudFolderView`.
|
||||
|
||||
### `frontend/src/views/CloudFolderView.vue`
|
||||
|
||||
**Primary analogs**
|
||||
|
||||
- Existing cloud thin-view contract: [frontend/src/views/CloudFolderView.vue:1-20], [frontend/src/views/CloudFolderView.vue:125-170]
|
||||
- Local upload / create / rename / move / delete orchestration: [frontend/src/views/FileManagerView.vue:1-30], [frontend/src/views/FileManagerView.vue:103-182]
|
||||
|
||||
**Copy these conventions**
|
||||
|
||||
- Keep the view thin: store/router/API orchestration only.
|
||||
- Pass everything into `StorageBrowser`; do not add layout or grid logic here.
|
||||
- Reuse the local upload queue orchestration style from `FileManagerView`, but Phase 13 needs sequential pause/resume instead of the current all-at-once `Promise.allSettled(...)` placeholder at [frontend/src/views/CloudFolderView.vue:172-181].
|
||||
- Use toasts and success/error handling the same way `FileManagerView` does.
|
||||
|
||||
### `frontend/src/components/storage/StorageBrowser.vue`
|
||||
|
||||
**Primary analog**
|
||||
|
||||
- Extend the current shared component itself: [frontend/src/components/storage/StorageBrowser.vue:140-275], [frontend/src/components/storage/StorageBrowser.vue:424-681]
|
||||
|
||||
**Copy these conventions**
|
||||
|
||||
- Props/events are the contract; keep new behavior behind emitted events and prop-driven state.
|
||||
- Capability-aware buttons and notices stay in this component: [frontend/src/components/storage/StorageBrowser.vue:360-418], [frontend/src/components/storage/StorageBrowser.vue:495-530].
|
||||
- New folder / rename inline patterns already exist here: [frontend/src/components/storage/StorageBrowser.vue:118-135], [frontend/src/components/storage/StorageBrowser.vue:157-166], [frontend/src/components/storage/StorageBrowser.vue:543-593].
|
||||
- File move picker and drag-to-move patterns already exist here: [frontend/src/components/storage/StorageBrowser.vue:253-344], [frontend/src/components/storage/StorageBrowser.vue:595-621].
|
||||
|
||||
**Gap to flag**
|
||||
|
||||
- There is no existing conflict dialog / paused upload queue UI. That is a real no-analog area inside the shared browser and should be added here rather than in a cloud-only component.
|
||||
|
||||
### `frontend/src/components/settings/SettingsCloudTab.vue`
|
||||
|
||||
**Primary analog**
|
||||
|
||||
- Existing settings actions and confirm flows: [frontend/src/components/settings/SettingsCloudTab.vue:21-102], [frontend/src/components/settings/SettingsCloudTab.vue:104-170], [frontend/src/components/settings/SettingsCloudTab.vue:293-359]
|
||||
|
||||
**Copy these conventions**
|
||||
|
||||
- Keep provider rows, inline status badges, and destructive confirms in one shared settings tab.
|
||||
- Add Test/Reconnect controls beside existing Connect/Edit/Remove actions; do not create a parallel health screen.
|
||||
- Keep OAuth initiation in the component and store mutation calls behind the store/API layer.
|
||||
|
||||
### `frontend/src/components/cloud/CloudCredentialModal.vue`
|
||||
|
||||
**Primary analog**
|
||||
|
||||
- Existing submit branching for create vs edit: [frontend/src/components/cloud/CloudCredentialModal.vue:301-323]
|
||||
|
||||
**Copy these conventions**
|
||||
|
||||
- Keep credential edits inside the modal for WebDAV/Nextcloud.
|
||||
- If Phase 13 adds explicit “Test connection” before save, hang it off the same API module and modal state machine instead of duplicating form state elsewhere.
|
||||
|
||||
### `backend/tests/test_cloud.py`
|
||||
|
||||
**Primary analog**
|
||||
|
||||
- Current integration coverage for connect/upload/status/browse/rename/refresh: [backend/tests/test_cloud.py:30-65], [backend/tests/test_cloud.py:473-527], [backend/tests/test_cloud.py:532-655], [backend/tests/test_cloud.py:998-1315]
|
||||
|
||||
**Copy these conventions**
|
||||
|
||||
- Use the shared auth helper pattern and `async_client`.
|
||||
- Patch provider/network boundaries, not route internals, where possible.
|
||||
- Assert both HTTP contract and DB side effects.
|
||||
|
||||
### `backend/tests/test_cloud_security.py`
|
||||
|
||||
**Primary analog**
|
||||
|
||||
- Current security-negative suite: [backend/tests/test_cloud_security.py:37-117], [backend/tests/test_cloud_security.py:122-340]
|
||||
|
||||
**Copy these conventions**
|
||||
|
||||
- Add Phase 13 wrong-owner/admin/credential/no-byte/cross-connection negatives here.
|
||||
- Keep raw-provider-error sanitization assertions here, not only in happy-path integration tests.
|
||||
|
||||
### `backend/tests/test_cloud_backends.py`
|
||||
|
||||
**Primary analog**
|
||||
|
||||
- Provider backend behavior tests: [backend/tests/test_cloud_backends.py:334-420] and the rest of the file’s provider sections
|
||||
|
||||
**Copy these conventions**
|
||||
|
||||
- Provider-specific fixtures, normalized assertions.
|
||||
- Explicit “never download/mutate while browsing” checks.
|
||||
- Keep backend tests provider-neutral where possible and provider-specific only where semantics differ.
|
||||
|
||||
### `backend/tests/test_cloud_provider_contract.py`
|
||||
|
||||
**Primary analog**
|
||||
|
||||
- Canonical signature/identity/pagination/no-byte contract: [backend/tests/test_cloud_provider_contract.py:201-276], [backend/tests/test_cloud_provider_contract.py:376-536], [backend/tests/test_cloud_provider_contract.py:659-727]
|
||||
|
||||
**Copy these conventions**
|
||||
|
||||
- Any new mutable adapter methods should get the same style of canonical contract tests: signature, normalized result, trusted caller identity, no forbidden side effects.
|
||||
|
||||
### `backend/tests/test_cloud_items.py`
|
||||
|
||||
**Primary analog**
|
||||
|
||||
- Owner-scoped service and folder-state tests: [backend/tests/test_cloud_items.py:368-529], [backend/tests/test_cloud_items.py:545-859]
|
||||
|
||||
**Copy these conventions**
|
||||
|
||||
- Mutation reconciliation tests belong here: stable UUID across rename/move, complete/incomplete behavior, freshness truth, no quota mutation.
|
||||
|
||||
### `backend/tests/test_cloud_mutations.py`
|
||||
|
||||
**Closest analogs**
|
||||
|
||||
- API behavior: [backend/tests/test_cloud.py:473-527], [backend/tests/test_cloud.py:1112-1249]
|
||||
- Security negatives: [backend/tests/test_cloud_security.py:122-340]
|
||||
- Reconciliation truth: [backend/tests/test_cloud_items.py:411-499], [backend/tests/test_cloud_items.py:743-859]
|
||||
|
||||
**No exact analog**
|
||||
|
||||
- There is no existing end-to-end mutation suite for cloud items. Build it as a new file, but follow the helper/fixture style of `test_cloud.py`.
|
||||
|
||||
### `backend/tests/test_cloud_reconnect.py`
|
||||
|
||||
**Closest analogs**
|
||||
|
||||
- Connection lifecycle/status: [backend/tests/test_cloud.py:532-655]
|
||||
- Worker/freshness behavior: [backend/tests/test_cloud.py:1251-1315], [backend/tests/test_cloud_items.py:545-609]
|
||||
|
||||
**No exact analog**
|
||||
|
||||
- There is no dedicated reconnect/token-persistence suite today.
|
||||
|
||||
### `backend/tests/test_cloud_audit.py`
|
||||
|
||||
**Closest analogs**
|
||||
|
||||
- Audit helper behavior: [backend/services/audit.py:27-58]
|
||||
- Inline audit assertions in cloud routes: [backend/api/cloud/connections.py:351-359], [backend/api/cloud/connections.py:422-430], [backend/api/cloud/connections.py:567-575], [backend/api/cloud/connections.py:624-632]
|
||||
|
||||
**No exact analog**
|
||||
|
||||
- There is no cloud-mutation audit suite today; create one that asserts metadata-only payloads and same-transaction persistence.
|
||||
|
||||
### `frontend/src/views/__tests__/CloudFolderView.test.js`
|
||||
|
||||
**Primary analog**
|
||||
|
||||
- Existing thin-view cloud tests: [frontend/src/views/__tests__/CloudFolderView.test.js:73-294]
|
||||
|
||||
**Copy these conventions**
|
||||
|
||||
- Stub `StorageBrowser` when the test is about view orchestration only.
|
||||
- Assert route params, API calls, and props passed to the browser.
|
||||
|
||||
### `frontend/src/views/__tests__/CloudFolderRenderedFlow.test.js`
|
||||
|
||||
**Primary analog**
|
||||
|
||||
- Existing real-browser rendered flow: [frontend/src/views/__tests__/CloudFolderRenderedFlow.test.js:1-260]
|
||||
|
||||
**Copy these conventions**
|
||||
|
||||
- Render the real `StorageBrowser` when testing end-to-end view/browser interactions.
|
||||
- Keep only router/API boundaries mocked.
|
||||
|
||||
### `frontend/src/components/storage/__tests__/StorageBrowser.capabilities.test.js`
|
||||
|
||||
**Primary analog**
|
||||
|
||||
- Current accessibility and unsupported-capability suite: [frontend/src/components/storage/__tests__/StorageBrowser.capabilities.test.js:47-257]
|
||||
|
||||
**Copy these conventions**
|
||||
|
||||
- Assert emitted events, `aria-disabled`, notices, and touch-target classes from the real component.
|
||||
- Extend this file for new capability buttons only; do not hide cloud actions based on mode.
|
||||
|
||||
### `frontend/src/components/storage/__tests__/StorageBrowser.cloud-queue.test.js`
|
||||
|
||||
**Closest analogs**
|
||||
|
||||
- Capability interaction assertions: [frontend/src/components/storage/__tests__/StorageBrowser.capabilities.test.js:89-190]
|
||||
- Local upload queue behavior source: [frontend/src/views/FileManagerView.vue:103-128]
|
||||
|
||||
**No exact analog**
|
||||
|
||||
- There is no existing paused sequential queue test file. Create it as a dedicated component suite around the shared browser’s new queue/conflict UI.
|
||||
|
||||
### `frontend/src/views/__tests__/CloudFolderOpenPreview.test.js`
|
||||
|
||||
**Closest analogs**
|
||||
|
||||
- Rendered cloud flow: [frontend/src/views/__tests__/CloudFolderRenderedFlow.test.js:170-260]
|
||||
- Authorized document content fetch helper: [frontend/src/api/documents.js:60-81]
|
||||
|
||||
**No exact analog**
|
||||
|
||||
- There is no cloud open/preview rendered-flow test today.
|
||||
|
||||
### `frontend/src/stores/__tests__/cloudConnections.test.js`
|
||||
|
||||
**Primary analog**
|
||||
|
||||
- Existing store tests for selection, freshness, session state: [frontend/src/stores/__tests__/cloudConnections.test.js:28-237]
|
||||
|
||||
**Copy these conventions**
|
||||
|
||||
- Keep pure store tests focused on mapping and reset behavior.
|
||||
- If queue state or health-state translation lives in the store, test it here once.
|
||||
|
||||
### `frontend/src/components/settings/__tests__/SettingsCloudTab.test.js`
|
||||
|
||||
**Primary analog**
|
||||
|
||||
- Existing settings-tab tests: [frontend/src/components/settings/__tests__/SettingsCloudTab.test.js:1-132]
|
||||
|
||||
**Copy these conventions**
|
||||
|
||||
- Mock the store and API module, not network.
|
||||
- Keep component tests structural and action-trigger oriented.
|
||||
|
||||
### `frontend/src/components/settings/__tests__/SettingsCloudTab.health.test.js`
|
||||
|
||||
**Closest analogs**
|
||||
|
||||
- Existing settings tab structure: [frontend/src/components/settings/__tests__/SettingsCloudTab.test.js:51-132]
|
||||
- Store freshness/health state assertions: [frontend/src/stores/__tests__/cloudConnections.test.js:160-237]
|
||||
|
||||
**No exact analog**
|
||||
|
||||
- There is no dedicated health/test/reconnect settings suite yet.
|
||||
|
||||
## Shared Patterns to Reuse Everywhere
|
||||
|
||||
### Authentication / ownership
|
||||
|
||||
- Regular-user-only dependency: [backend/api/cloud/browse.py:29-31], [backend/api/cloud/connections.py:25-27], [backend/api/documents/content.py:24-26]
|
||||
- Owner resolution before resource work: [backend/api/cloud/browse.py:205-214], [backend/api/cloud/connections.py:132-141], [backend/services/cloud_items.py:38-62]
|
||||
|
||||
### Router shape
|
||||
|
||||
- Thin routers with typed bodies + `account_limiter`: [backend/api/cloud/connections.py:281-304], [backend/api/cloud/browse.py:183-200], [backend/api/folders.py:122-149]
|
||||
- Response shaping through explicit Pydantic schemas or dict helpers, never raw ORM/provider payloads: [backend/api/cloud/browse.py:64-97], [backend/api/cloud/schemas.py:17-67]
|
||||
|
||||
### Error translation
|
||||
|
||||
- `CloudConnectionError` becomes controlled reconnect guidance: [backend/api/documents/upload.py:160-175], [backend/api/documents/content.py:61-76]
|
||||
- Incomplete/stale provider state becomes controlled warning, not fake success: [backend/services/cloud_items.py:319-342], [backend/api/cloud/browse.py:296-307]
|
||||
|
||||
### Audit
|
||||
|
||||
- Use `write_audit_log(...)` after successful state change, inside caller-owned transaction: [backend/services/audit.py:27-58]
|
||||
|
||||
### Provider boundary
|
||||
|
||||
- Providers normalize data and keep SDK/HTTP details private: [backend/storage/google_drive_backend.py:276-362], [backend/storage/onedrive_backend.py:300-386], [backend/storage/webdav_backend.py:241-332]
|
||||
- Factories own provider construction and URL normalization: [backend/storage/cloud_backend_factory.py:7-66]
|
||||
|
||||
### Frontend architecture
|
||||
|
||||
- Thin view -> shared browser: [frontend/src/views/CloudFolderView.vue:1-20], [frontend/src/views/FileManagerView.vue:1-30]
|
||||
- Shared browser owns interaction/layout/state: [frontend/src/components/storage/StorageBrowser.vue:424-681]
|
||||
- Settings tab owns connection controls, not provider-specific subviews: [frontend/src/components/settings/SettingsCloudTab.vue:21-214]
|
||||
|
||||
### Testing
|
||||
|
||||
- Integration tests assert API contract plus DB side effect: [backend/tests/test_cloud.py:532-655], [backend/tests/test_cloud.py:1112-1249]
|
||||
- Security tests assert absence of leaks and forbidden side effects: [backend/tests/test_cloud_security.py:197-301]
|
||||
- View tests stub the browser for orchestration-only checks; rendered-flow tests use the real browser: [frontend/src/views/__tests__/CloudFolderView.test.js:51-101], [frontend/src/views/__tests__/CloudFolderRenderedFlow.test.js:170-205]
|
||||
|
||||
## No-Analog / planner watchlist
|
||||
|
||||
1. Provider-neutral mutation result dataclasses do not exist yet. Add them in `backend/storage/cloud_base.py`; do not leak provider payloads.
|
||||
2. OAuth reconnect that patches an existing connection row does not exist yet. Current callback flow inserts a new row.
|
||||
3. Shared paused upload queue UI for cloud conflicts/errors does not exist yet. Add it in `StorageBrowser.vue`, not a cloud-only component.
|
||||
4. Authorized cloud-item preview/open endpoints do not exist yet. Reuse document content streaming patterns, but this is a new endpoint family.
|
||||
5. Dedicated reconnect/audit/open-preview test suites do not exist yet; create them instead of overloading unrelated files.
|
||||
|
||||
## Recommended implementation order from pattern proximity
|
||||
|
||||
1. Extend `cloud_base.py` + provider backends + factory.
|
||||
2. Add `cloud_operations.py` and keep all reconciliation through `cloud_items.py`.
|
||||
3. Add `api/cloud/operations.py`, then minimally extend `connections.py` / `schemas.py`.
|
||||
4. Extend `api/cloud.js` + `cloudConnections.js` + `CloudFolderView.vue`.
|
||||
5. Extend `StorageBrowser.vue` and `SettingsCloudTab.vue`.
|
||||
6. Add the missing dedicated backend/frontend test files before broad polish.
|
||||
|
||||
## PATTERN MAPPING COMPLETE
|
||||
Reference in New Issue
Block a user