Commit c87fa70
authored
feat(aurora-portal): implement create and delete opetations bucket (#806)
* feat(ceph): extend backend to return full container metadata
Align Ceph container data structure with Swift ContainerSummary:
- Add count, bytes, last_modified fields to containerSchema
- Update list procedure to fetch metadata via ListObjectsV2Command
- Add deleteAll procedure for emptying buckets
- Remove getDetails procedure (replaced by enhanced list)
This enables UI parity between Swift and Ceph containers view.
* feat(ceph): add Swift-like UI with ListToolbar, virtualization, and bulk operations
Frontend refactor to achieve UI parity with Swift containers:
- Add ListToolbar with sorting (name, count, bytes, last_modified) and search
- Implement URL-persisted state for sort/search (survives navigation)
- Create ContainerTableView with @tanstack/react-virtual for performance
- Add checkbox selection for bulk operations
- Implement EmptyBucketModal and EmptyBucketsModal
- Update toast notifications to Swift standard (title/description structure)
- Support "Empty All" bulk operation with progress tracking
The UI is now fully consistent with Swift containers view.
* refactor(ceph): remove Ceph-specific routes, use shared $provider route
Remove duplicate routes at /storage/ceph/* to consolidate all storage
providers under the shared /storage/$provider/* route structure.
This ensures Ceph uses the same routing as Swift with proper provider
switching support.
* refactor(ceph): improve region resolution and add JSDoc comments
- Auto-resolve Ceph region from OpenStack service catalog
- Add special case for qa-de-1 region (uses ec prefix for historical reasons)
- Remove CEPH_REGION environment variable (no longer needed)
- Remove debug console.log statements
- Add comprehensive JSDoc comments to resolveS3Config and bucket operations
- Extract magic strings to constants for better maintainability
* refactor: code styles
* refactor: ceph doc
* refactor: ceph doc
* test(ceph): add comprehensive unit test coverage for Ceph storage
Add unit tests for previously untested Ceph storage files:
- cephProcedure.ts: S3 config resolution, middleware, and procedures (15 tests)
- s3Client.ts: Client factory with validation (16 tests)
- s3ErrorMapper.ts: Error mapping from AWS SDK to TRPC (27 tests)
Fix existing router tests by adding catalog structure to mock context:
- containerRouter.test.ts: Add catalog to token data, update expected results
- objectRouter.test.ts: Add catalog to token data
All tests follow existing patterns from router tests using Vitest.
Total: 58 new tests, all passing (4149/4153 tests pass).
* refactor(test): replace 'any' with proper types in Ceph tests
Replace all 'any' type assertions with proper TypeScript types:
- s3Client.test.ts: Use 'as unknown as string' for null/undefined tests
- cephProcedure.test.ts: Create MockS3Client type for S3Client mocks
- s3ErrorMapper.test.ts: Use ReturnType<typeof vi.spyOn> for spy type
All tests still passing (58/58). No eslint-disable comments needed.
* test(ceph): add frontend tests for container toast notifications and credential prompt
Add comprehensive test coverage for Ceph frontend components:
- ContainerToastNotifications.test.tsx (97 tests):
- getBucketCreatedToast
- getBucketCreateErrorToast
- getBucketEmptiedToast (plural, singular, zero cases)
- getBucketEmptyErrorToast
- getBucketDeletedToast
- getBucketDeleteErrorToast
- getBucketsEmptyCompleteToast (success/warning variants)
- Toast configuration validation
- CredentialPrompt.test.tsx (16 tests):
- Rendering setup prompt with EC2 credential explanation
- Button interaction with TRPC mutation
- Success handling with callback and query invalidation
- Error handling with toast display
- Layout and styling validation
All tests follow existing patterns from Swift component tests.
Total Ceph test coverage: 187 tests (58 backend + 129 frontend).
* test(ceph): add modal tests for CreateBucket and DeleteBucket
Add comprehensive test coverage for Ceph bucket modals:
- CreateBucketModal.test.tsx (48 tests):
- UI elements rendering
- Valid bucket name validation (lowercase, numbers, periods, hyphens)
- Length validation (3-63 characters)
- Character validation (no uppercase, special chars, invalid start/end)
- IP address format rejection
- Reserved prefix/suffix validation (xn--, sthree-, amzn-s3-demo-, -s3alias, --ol-s3, etc)
- Real-time validation feedback
- Bucket creation with TRPC mutation
- Success/error handling with callbacks
- DeleteBucketModal.test.tsx (35 tests):
- Visibility logic (isOpen, bucket null checks)
- UI elements for empty/non-empty buckets
- Loading state with bucket contents check
- Non-empty bucket error message (plural/singular forms)
- Objects query error handling
- Bucket name display and copy functionality
- Confirmation name validation
- Bucket deletion with TRPC mutation
- Success/error handling with query invalidation
Total Ceph test coverage: 212 tests (58 backend + 154 frontend).
* test(ceph): add modal tests for EmptyBucket and EmptyBuckets
Add comprehensive test coverage for bucket emptying modals:
- EmptyBucketModal.test.tsx (33 tests):
- Visibility logic (isOpen, bucket null checks)
- Empty bucket state (info message, Got it! button)
- Non-empty bucket UI with warning and object count (plural/singular)
- Bucket name display and copy functionality
- Confirmation name validation
- Bucket emptying mutation with TRPC
- Success/error handling with callbacks
- Modal close behavior
- EmptyBucketsModal.test.tsx (23 tests):
- Visibility with empty buckets array
- UI elements with bucket count (plural/singular)
- Bucket list display (max 20 visible, hidden count)
- Object count for each bucket (plural/singular)
- Batch emptying process with sequential mutations
- Success handling with emptiedCount and totalDeleted
- Error handling (partial failures, all failures)
- Query invalidation logic (only when some succeed)
- Modal close and state reset
Total Ceph test coverage: 268 tests (58 backend + 210 frontend).
* test(ceph): add ContainerTableView component tests
Add comprehensive test coverage for ContainerTableView component:
- Empty state rendering
- Table structure with headers (Bucket Name, Object Count, Last Modified, Total Size)
- Footer with bucket count (singular/plural forms)
- Selection functionality (select all checkbox, individual container checkboxes)
- Row navigation (click and Enter key)
- Action menu presence (PopupMenu for each container)
- Modal visibility (CreateBucketModal, EmptyBucketModal, DeleteBucketModal)
- Date formatting
Total: 24 tests
* test(ceph): add ContainerListView component tests
Add comprehensive test coverage for ContainerListView component:
- Loading state with spinner
- Error handling (NO_CEPH_CREDENTIALS, other errors)
- CredentialPrompt integration
- Empty state rendering
- Container list rendering (names, dates, delete buttons)
- Create Bucket button and modal
- Delete Bucket button and modal
- Table structure
Total: 21 tests
* fix(ceph): correct last_modified comparator for stable sorting
Fix comparator function to return 0 when both values are missing,
preventing unstable/inconsistent sorting behavior.
Previous logic:
- Returned 1 when both last_modified were missing
- Violated comparator contract (equal items should return 0)
New logic:
- Returns 0 when both are missing (equal)
- Returns 1 when only first is missing (first goes last)
- Returns -1 when only second is missing (first goes first)
- Compares timestamps when both present
This ensures stable sort order where items without last_modified
consistently appear at the end of the sorted list.
* fix(ceph): validate object keys before deletion in deleteAll
Add validation to ensure all objects have valid Key field before
attempting batch deletion. Previously, objects with undefined keys
would be included in delete requests, causing S3 API errors.
Changes:
- Filter and validate object keys before deletion
- Throw descriptive error if objects without keys are encountered
- Fail fast with clear error message instead of sending invalid requests
Tests added (5 new tests):
- Delete all objects and return count
- Handle paginated deletion with multiple batches
- Return 0 when bucket is empty
- Throw error when objects have undefined keys ✨ NEW
- Throw NOT_FOUND when bucket does not exist
This prevents invalid delete requests and provides better error
messages when S3 API returns unexpected data.
* perf(ceph): add optional metadata fetching with concurrency control
Add includeMetadata parameter to containers.list endpoint to address
performance issues when listing many buckets. Previously, every bucket
triggered a ListObjectsV2 request in parallel, causing thundering herd
problems and potential rate limit issues.
Changes:
- Add includeMetadata boolean parameter (default: false) to listContainersInputSchema
- Fast path (includeMetadata=false): Returns basic bucket info only (name, creationDate)
- Slow path (includeMetadata=true): Fetches full metadata with CONCURRENCY_LIMIT=5
- Batched concurrent requests prevent overwhelming S3 API
Performance impact:
- Without metadata: ~1 ListBuckets request (fast)
- With metadata: 1 ListBuckets + N ListObjectsV2 requests (batched in groups of 5)
This allows frontends to choose between fast listing (for initial load)
and detailed metadata (when user explicitly requests it or for table views).
Addresses Copilot feedback about thundering herd and rate limits.
* feat(ceph): use includeMetadata flag in frontend components
Update frontend components to use new includeMetadata parameter:
- ContainerTableView (index.tsx): Set includeMetadata=true for full table
with sorting by object count, size, and last modified date
- ContainerListView: Set includeMetadata=false for fast basic list view
that only shows bucket names without metadata
This optimizes performance:
- Basic list view loads instantly (1 API call)
- Table view with metadata loads progressively (batched requests)
* docs(ceph): document metadata estimation limitations
Add explicit documentation that count, bytes, and last_modified are
ESTIMATES when buckets contain >1000 objects, addressing Copilot
feedback about potential data inaccuracy.
Changes:
- Updated containerSchema JSDoc to warn about estimation limitations
- Added detailed comments in containerRouter about MaxKeys=1000 impact
- Clarified that pagination would be needed for accurate data (expensive)
Context:
ListObjectsV2 with MaxKeys=1000 means:
- count: Capped at 1000 (use KeyCount for actual count up to limit)
- bytes: Only sums first 1000 objects
- last_modified: May miss newer objects beyond first 1000
Trade-off: Performance vs accuracy. Current implementation prioritizes
fast response times over perfect accuracy for large buckets.
Alternative solutions considered:
1. Full pagination (accurate but very slow for large buckets)
2. S3 bucket metrics/inventory (requires additional setup)
3. Current approach: Fast estimates with clear documentation ✅
Addresses Copilot comment on lines +36 to +49.
* refactor(ceph): remove misleading TODO about pagination
Replace TODO with clear explanation that MaxKeys=1000 limitation is
a deliberate design decision, not something that needs fixing.
Rationale:
- Fast response time for UI list view > perfect accuracy
- Full pagination would be prohibitively expensive for large buckets
- Current approach (estimates) is the correct trade-off for this use case
- Misleading TODOs create false expectations of future "fixes"
The limitation is already well-documented in comments and type definitions.
* refactor(storage): organize routers into ceph/ and swift/ subdirectories
Move storage routers into separate subdirectories to improve organization and avoid confusion between Ceph and Swift implementations.
Changes:
- Create routers/ceph/ and routers/swift/ subdirectories
- Move containerRouter, objectRouter, ec2CredentialRouter to ceph/
- Move swiftRouter to swift/
- Add index.ts exports for each subdirectory
- Update all import paths in routers and tests
- Update main routers/index.ts to import from subdirectories
All 4397 tests passing after restructure.
* fix(i18n): remove duplicate translation message IDs
Fix duplicate i18n message IDs identified by Copilot:
- Change 'EmptyBucketsModal' to use existing '... and {hiddenCount} more' (with spaces)
- Fix 'EmptyBucketModal' Trans block formatting to keep string on one line
This ensures consistent UI copy across features and reduces translation burden.
Regenerated translation files with lingui extract and compile.
* refactor: code style
* refactor: translations
* refactor: code styles
* refactor(storage): simplify Ceph service resolution in resolveS3Config
Refactored resolveS3Config to use ctx.openstack.service() API directly instead of manually searching through the catalog, addressing PR feedback from @andypf.
Changes:
- Removed redundant token.tokenData.catalog.find() logic
- Use service.availableEndpoints() instead of accessing catalog directly
- Simplified error handling and reduced nesting
- Updated all test mocks to include availableEndpoints() method
Fixes code duplication where we were already calling ctx.openstack.service("ceph") but then manually searching the catalog again.
* refactor(storage): extract S3 MaxKeys value to constant
Extracted hardcoded MaxKeys=1000 value to S3_MAX_KEYS_PER_REQUEST constant, addressing PR feedback from @mark-karnaukh-extern-sap.
Changes:
- Created src/server/Storage/constants.ts with S3_MAX_KEYS_PER_REQUEST constant
- Updated objectRouter.ts to use the constant
- Updated containerRouter.ts to use the constant
- Updated comment in types/ceph.ts to reference the constant name
This improves maintainability - the value is now defined in one place with proper documentation.
* fix(ceph): show creationDate when last_modified is missing for empty containers
Empty containers don't have last_modified field, so we now fall back to creationDate which is always present from S3 CreationDate.
Changes:
- Updated ContainerTableView to use creationDate as fallback
- Updated test to reflect the new behavior
- formatDate handles empty string gracefully (returns N/A)
* refactor: modals
* refactor: data grid
* fix(ceph): localize delete bucket button title in ContainerListView
* fix(ceph): avoid dereferencing bucket in async mutation callbacks
Capture bucket name before async operations to prevent accessing null
bucket reference if modal closes before callbacks execute.
- EmptyBucketModal: capture bucketName in handleSubmit, pass to inline onSuccess/onError
- DeleteBucketModal: capture bucketName in handleSubmit, pass to inline onSuccess/onError
- Update tests to expect second parameter (options) in mutate() calls
- Move invalidate() to onSettled to ensure it runs in both success and error cases
* refactor: env ceph region variable
* test(ceph): update tests to use CEPH_REGION env variable
* test(ceph): add CEPH_REGION env to containerRouter and objectRouter tests1 parent f28aea4 commit c87fa70
45 files changed
Lines changed: 6878 additions & 456 deletions
File tree
- apps/aurora-portal
- docs
- src
- client
- routes/_auth/projects/$projectId/storage
- -components/Ceph/Containers
- ceph
- containers
- $containerName/objects
- locales
- de
- en
- server/Storage
- clients
- helpers
- routers
- ceph
- swift
- types
Some content is hidden
Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
2 | 2 | | |
3 | 3 | | |
4 | 4 | | |
5 | | - | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
6 | 10 | | |
7 | 11 | | |
8 | 12 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
85 | 85 | | |
86 | 86 | | |
87 | 87 | | |
88 | | - | |
89 | | - | |
90 | | - | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
91 | 97 | | |
92 | 98 | | |
93 | 99 | | |
94 | 100 | | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
95 | 114 | | |
96 | 115 | | |
97 | 116 | | |
| |||
571 | 590 | | |
572 | 591 | | |
573 | 592 | | |
574 | | - | |
| 593 | + | |
575 | 594 | | |
576 | | - | |
577 | | - | |
578 | | - | |
| 595 | + | |
| 596 | + | |
579 | 597 | | |
580 | 598 | | |
581 | 599 | | |
| |||
586 | 604 | | |
587 | 605 | | |
588 | 606 | | |
589 | | - | |
| 607 | + | |
| 608 | + | |
| 609 | + | |
| 610 | + | |
| 611 | + | |
590 | 612 | | |
591 | | - | |
| 613 | + | |
592 | 614 | | |
593 | 615 | | |
594 | 616 | | |
| |||
799 | 821 | | |
800 | 822 | | |
801 | 823 | | |
802 | | - | |
803 | | - | |
| 824 | + | |
| 825 | + | |
804 | 826 | | |
805 | 827 | | |
806 | 828 | | |
| |||
0 commit comments