Skip to content

Conversation

@anya-xcode
Copy link

Adds API-based admin namespace deletion, removing the need for manual SQL. Fixes issue #1519.

Changes

Backend

  • Added deleteNamespace() in AdminService

    • Validates namespace existence
    • Deletes related data in correct order (reviews, files, versions, extensions, memberships, namespace)
    • Removes associated storage files
    • Clears relevant caches
    • Updates search index
    • Logs admin actions
    • Uses transactions with rollback on failure
  • Added DELETE endpoints in AdminAPI

    • DELETE /admin/namespace/{namespaceName} (session-based auth)
    • DELETE /admin/api/namespace/{namespaceName}?token={token} (token-based auth)
    • Returns appropriate HTTP status codes
    • Includes OpenAPI documentation and CSRF protection

Frontend

  • Added deleteNamespace() to AdminService

  • Added “Delete Namespace” button in admin namespace detail view

    • Confirmation dialog before deletion
    • Loading state during operation
    • Redirects after successful deletion
    • Displays errors if deletion fails

Testing

  • Added 12 test cases covering:

    • Authorization and authentication
    • Deletion of empty and populated namespaces
    • Namespaces with extensions, versions, memberships, files, and reviews
    • Token-based authentication

Benefits

  • Eliminates manual SQL for namespace deletion
  • Ensures proper cascade deletion and data integrity
  • Automatically cleans storage, cache, and search index
  • Improves admin usability with UI support

Breaking Changes

None

Related Issue

Fixes #1519


If you want it even shorter (GitHub PR–style) or more technical, tell me and I’ll trim it further.

Implements admin namespace deletion through the API instead of requiring manual SQL statements.

Backend: Add deleteNamespace method with cascade deletion, two DELETE endpoints, storage cleanup, cache invalidation, and audit logging.

Frontend: Add Delete Namespace button in admin dashboard with confirmation dialog and error handling.

Testing: Add 12 comprehensive test cases covering all scenarios including edge cases.
Copilot AI review requested due to automatic review settings January 14, 2026 17:40
@anya-xcode
Copy link
Author

@netomi Please review this and let me know if any changes are needed.

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds admin namespace deletion functionality via API, eliminating the need for manual SQL operations. It implements a complete deletion flow that handles all related data (extensions, versions, files, reviews, memberships) with proper cascade, cache clearing, and search index updates.

Changes:

  • Added backend deleteNamespace() method in AdminService with transactional support and proper cleanup
  • Added two DELETE endpoints in AdminAPI for session-based and token-based authentication
  • Added frontend deleteNamespace() method and UI button with confirmation dialog in namespace detail view

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
server/src/main/java/org/eclipse/openvsx/admin/AdminService.java Core namespace deletion logic with cascade deletion and cleanup
server/src/main/java/org/eclipse/openvsx/admin/AdminAPI.java REST API endpoints for namespace deletion with authentication
server/src/test/java/org/eclipse/openvsx/admin/AdminAPITest.java Comprehensive test coverage for namespace deletion scenarios
webui/src/extension-registry-service.ts Frontend service method for namespace deletion API calls
webui/src/pages/user/user-settings-namespace-detail.tsx UI integration with delete button and confirmation dialog

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +136 to +142
<ButtonWithProgress
variant='outlined'
color='error'
working={deleting}
onClick={handleDelete}>
Delete Namespace
</ButtonWithProgress>
Copy link

Copilot AI Jan 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The ButtonWithProgress component does not accept a 'variant' prop. The component hardcodes variant='contained' on line 17 of button-with-progress.tsx. Remove the variant='outlined' prop.

Copilot uses AI. Check for mistakes.
entityManager.remove(namespace);

// Update search index
search.updateSearchEntries(extensions.toList());
Copy link

Copilot AI Jan 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After deleting extensions, updateSearchEntries should not be called with the deleted extensions. This will attempt to re-index extensions that have just been removed. Use removeSearchEntries with extension IDs instead, or call search.removeSearchEntry() for each extension before removal.

Copilot uses AI. Check for mistakes.
// Remove file resources
var resources = repositories.findFiles(version);
for (var resource : resources) {
storageUtil.removeFile(resource);
Copy link

Copilot AI Jan 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

File removal should be done asynchronously via job scheduler, similar to removeExtensionVersion() on line 243 which uses scheduler.enqueue(new RemoveFileJobRequest(resource)). This prevents blocking the transaction on potentially slow external storage operations.

Suggested change
storageUtil.removeFile(resource);
scheduler.enqueue(new RemoveFileJobRequest(resource));

Copilot uses AI. Check for mistakes.
Comment on lines +338 to +365
for (var extension : extensions) {
// Remove reviews (associated with extension, not version)
var reviews = repositories.findAllReviews(extension);
for (var review : reviews) {
entityManager.remove(review);
}

// Remove all versions and their resources
var versions = repositories.findVersions(extension);
for (var version : versions) {
// Remove file resources
var resources = repositories.findFiles(version);
for (var resource : resources) {
storageUtil.removeFile(resource);
entityManager.remove(resource);
}

// Remove the version
entityManager.remove(version);
}

// Clear cache for the extension
cache.evictExtensionJsons(extension);
cache.evictLatestExtensionVersion(extension);

// Remove the extension
entityManager.remove(extension);
}
Copy link

Copilot AI Jan 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The namespace deletion logic does not handle deprecated extension references. When an extension is deleted, other extensions that reference it as a replacement should have their replacement field cleared. See deleteExtension() on lines 217-221 which handles this with findDeprecatedExtensions().

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Support deleting a namespace by admins

1 participant