Skip to content

Add bulk-delete endpoints for remaining grid entities - #2569

Open
EmanueleDeRossi1 wants to merge 7 commits into
mainfrom
feature/bulk-delete-test-runs-test-sets
Open

Add bulk-delete endpoints for remaining grid entities#2569
EmanueleDeRossi1 wants to merge 7 commits into
mainfrom
feature/bulk-delete-test-runs-test-sets

Conversation

@EmanueleDeRossi1

Copy link
Copy Markdown
Collaborator

Purpose

Add bulk-select delete support to every backend entity that has a grid in the frontend. This covers the backend half of #2261 (Test Runs and Test Sets) and extends the same pattern to Endpoints, Sources, Tokens, and Tasks. Two of these entities (Test Run, Task) have an owner-only delete rule that the existing generic bulk-delete helper didn't understand, so this also extends that helper to support it safely instead of letting any org member bulk-delete rows they don't own.

What Changed

  • bulk_delete_by_ids() gained an optional owner_attr parameter: when set, only rows owned by the caller are deleted, and ids that exist but belong to someone else are reported in a new forbidden_ids field instead of being silently skipped or deleted.
  • Added DELETE /test_runs/bulk, respecting the existing creator-only delete rule (also revokes the Celery task for any active run among the ones actually deleted).
  • Added DELETE /test_sets/bulk, replacing the one-row-at-a-time delete loop the frontend used before.
  • Added DELETE /endpoints/bulk, DELETE /sources/bulk, and DELETE /tokens/bulk — none of these have an owner-only rule, so each is a direct wrapper around the existing bulk-delete helper.
  • Added DELETE /tasks/bulk, respecting the existing creator-only delete rule, same treatment as test runs.

Additional Context

Closes the backend half of #2261. Team Members and account-level User deletion were intentionally left out of this batch — Team Members isn't backed by a real data grid today, and deleting another user's account carries different risk than deleting a resource like a test or endpoint. The frontend wiring (selection UI, bulk-action bars, API client methods) is a separate PR.

Testing

Added a route-level test file per entity under tests/backend/routes/, covering the deleted/not-found split for every entity and the deleted/forbidden/not-found split for Test Run and Task specifically. Ran the full set of new tests together with the existing Tests bulk-delete suite and the object-level authorization suite to confirm no regressions; all 36 tests pass.

bulk_delete_by_ids only understood organization scope and the
visibility column, so it couldn't back an owner-only (":own") delete
rule the way single-item delete does via authorize_object. Ids that
exist but fail the new owner_attr check land in "forbidden_ids"
instead of being silently skipped or deleted.
Single-item delete already enforces that only the creator may delete
their own test run (DELETE_OWN). Wires bulk_delete_by_ids's new
owner_attr check into a DELETE /test_runs/bulk endpoint so bulk delete
enforces the same rule instead of silently dropping it, and revokes
the Celery task for any active run among the ones actually deleted.
TestSet's visibility column is the only delete gate (no owner-only
rule on top, unlike TestRun), so this ports Tests' existing
bulk_delete_by_ids pattern as-is behind a new DELETE /test_sets/bulk
endpoint, replacing the per-row delete loop the frontend used before.
No owner-only rule on endpoint delete, so this ports the existing
bulk_delete_by_ids pattern as-is behind a new DELETE /endpoints/bulk
endpoint.
No owner-only rule on source delete, so this ports the existing
bulk_delete_by_ids pattern as-is behind a new DELETE /sources/bulk
endpoint.
No owner-only rule on token delete, so this ports the existing
bulk_delete_by_ids pattern as-is behind a new DELETE /tokens/bulk
endpoint, matching the existing revoke_token naming internally.
Single-item delete already enforces that only the creator may delete
their own task (DELETE_OWN). Wires bulk_delete_by_ids's owner_attr
check into a DELETE /tasks/bulk endpoint, same treatment as test runs,
so bulk delete enforces the same rule instead of silently dropping it.

@peqy peqy Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

[Improvement] bulk_delete_by_ids(owner_attr=...) should probably guard against missing user_id (and optionally validate owner_attr exists) to avoid accidental weakening of owner-only semantics.

[Improvement] /test_runs/bulk fetches task ids using visibility filtering; consider aligning that query with the same ownership filter used for deletion to avoid extra work / subtle coupling.

[Question] /tasks/bulk doesn’t emit telemetry like the single-item delete—intentional?

Found 3 issues (0 critical, 2 improvements, 1 question).

not_found_ids = [i for i in item_ids if i not in existing_ids]
not_found_ids = [i for i in item_ids if i not in visible_ids]

forbidden_ids = None

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

[Improvement] owner_attr is meant to enforce an owner-only delete rule, but if a caller ever passes owner_attr without a user_id (it’s still optional in the signature), the query will silently treat everything as not-owned.

Fix: consider an explicit guard like if owner_attr and not user_id: raise ValueError(...) (and optionally validate hasattr(model, owner_attr)) so misuse can’t weaken the authorization semantics.

"""
from rhesis.backend.celery.core import app as celery_app

organization_id, user_id = tenant_context

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

[Improvement] Bulk delete gets task ids via get_test_run_task_ids(..., user_id=user_id) which applies .with_visibility_filter(user_id). For TestRun you’re also enforcing creator-only delete (owner_attr="user_id").

If with_visibility_filter for TestRun is broader than “creator-only” (e.g., org-visible), you’ll fetch task IDs for runs you won’t delete (forbidden). That’s not a security issue, but it’s extra work and makes the revoke logic depend on visibility semantics.

Fix: consider aligning the task-id query with the same ownership filter (either pass an owner_attr-like filter into get_test_run_task_ids, or compute task ids only from result["deleted_ids"] after the delete by re-querying those ids).

raise HTTPException(status_code=500, detail="Internal server error")


@router.delete("/bulk", response_model=schemas.TaskBulkDeleteResponse)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

[Question] The bulk delete path enforces creator-only via owner_attr, but (unlike the single-item delete) it doesn’t record telemetry (track_feature_usage) or return a success message.

Is that intentional for bulk endpoints? If you want parity, you could track a single action="bulk_deleted" with count + ids (or just count), and keep the response shape consistent with other bulk deletes.

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.

1 participant