Skip to content

feat(api): add CheckProjectDelete - #2011

Merged
AmanGIT07 merged 1 commit into
mainfrom
feat/check-project-delete
Oct 23, 2026
Merged

AmanGIT07 merged 1 commit into
mainfrom
feat/check-project-delete

Conversation

@AmanGIT07

Copy link
Copy Markdown
Contributor

Summary

Adds the CheckProjectDelete handler. It returns can_delete and the blockers that would refuse DeleteProject, each RESOURCES_EXIST blocker with the number of blocking resources in count. It changes nothing, so a client can show the blockers before the user tries to delete. Stacked on #2008.

Changes

  • core/deleter/service.go: CheckProjectDelete finds the project and returns ProjectResourceBlockers. DeleteProject and CheckProjectDelete share one lookup, getProject: an id that is not a uuid returns project.ErrInvalidUUID, and a missing project returns project.ErrNotExist. Disabled projects are found.
  • internal/api/v1beta1connect/deleter.go: the CheckProjectDelete handler answers not_found for a missing project or an id that is not a uuid. transformBlockersToPB builds the response blockers for both check RPCs and now copies count.
  • internal/api/v1beta1connect/interfaces.go, mocks/cascade_deleter.go: CheckProjectDelete on CascadeDeleter; mock regenerated.
  • pkg/server/connect_interceptors/authorization.go: CheckProjectDelete needs delete permission on the project, the same as DeleteProject. Without a rule every call was denied.

Test Plan

  • core/deleter/service_test.go TestCheckProjectDelete: two blocking resources come back as one blocker with count 2; an allowed resource gives no blockers; a missing project returns project.ErrNotExist; a name returns project.ErrInvalidUUID. TestDeleteProject passes unchanged.
  • internal/api/v1beta1connect/deleter_test.go TestHandler_CheckProjectDelete: blockers with their count and can_delete false; can_delete true with no blockers; not_found for a missing project and for a name.
  • e2e TestProjectDeleteRefusedWhileResourcesBlock: the check reports the compute/order blocker with count 1 before the delete, and can_delete true once the resource is deleted.
  • go test ./core/... ./internal/api/... ./pkg/server/... passes.
  • e2e TestEndToEndAPIRegressionTestSuite, TestEndToEndServiceUsersRegressionTestSuite and TestEndToEndPATRegressionTestSuite pass.
  • golangci-lint run ./core/deleter/... ./internal/api/v1beta1connect/... ./pkg/server/... ./test/e2e/regression/... reports no issues.

@vercel

vercel Bot commented Oct 20, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
frontier Ready Ready Preview Oct 23, 2026 3:23am UTC

@coderabbitai

coderabbitai Bot commented Oct 20, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration
📥 Commits

Reviewing files that changed from the base of the PR and between 26c0f21 and fefd216.

📒 Files selected for processing: 3
  • core/deleter/service.go
  • core/deleter/service_test.go
  • test/e2e/regression/api_test.go

Included review availability: This review used your included allowance. Your plan provides up to 2 included reviews per hour; 0 remain after this review.

📝 Summary

Summary by CodeRabbit

  • New Features
    • Added a project deletion check that reports blocking resources and their counts before deletion.
    • Projects without blockers are reported as eligible for deletion.
    • Invalid or missing project identifiers are reported as not found.
    • Project deletion checks require the appropriate permission.
  • Bug Fixes
    • Organization deletion checks now include resource blocker counts.
    • Project deletion eligibility updates after blocking resources are removed.

The service now checks project deletion blockers without deleting the project. The Connect API exposes the check, returns blocker counts, and requires project-level delete permission. Tests cover service behavior, API responses, and an end-to-end deletion flow.

Changes: Project deletion check

Layer / File(s) Summary
Service preflight and project lookup
core/deleter/service.go, core/deleter/service_test.go
The service adds CheckProjectDelete and shares project lookup and validation with DeleteProject. Tests cover blockers, allowed projects, missing projects, and invalid IDs.
Connect endpoint and authorization
internal/api/v1beta1connect/interfaces.go, internal/api/v1beta1connect/deleter.go, pkg/server/connect_interceptors/authorization.go, internal/api/v1beta1connect/mocks/cascade_deleter.go, internal/api/v1beta1connect/deleter_test.go, test/e2e/regression/api_test.go
The Connect API exposes project deletion checks and converts blockers, including counts. The endpoint requires project-level DeletePermission. Mock, API, and end-to-end tests cover blocker reporting and eligibility after resource removal.

Priority: ➖ Normal

Estimated code review effort: 3 (Moderate) | ~20 minutes

Change: Feature

Suggested reviewers: whoabhisheksah

Merge Risk: ⚪ Minimal · up to fefd2

No issue identified in the supplied changes requires resolution before merging.

Pre-merge checks: Passed 2
Check name Status Explanation
Linked Issues check Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check Passed Check skipped because no linked issues were found for this pull request.
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coveralls

coveralls commented Oct 20, 2026 •

Copy link
Copy Markdown

Coverage Report for CI Build 39894702152

Coverage increased (+0.02%) to 55.741%

Details

  • Coverage increased (+0.02%) from the base build.
  • Patch coverage: 7 uncovered changes across 3 files (37 of 44 lines covered, 84.09%).
  • No coverage regressions found.

Uncovered Changes

File Changed Covered %
pkg/server/connect_interceptors/authorization.go 4 0 0.0%
core/deleter/service.go 21 19 90.48%
internal/api/v1beta1connect/deleter.go 19 18 94.74%

Coverage Regressions

No coverage regressions found.


Coverage Stats

Coverage Status
Relevant Lines: 41949
Covered Lines: 23383
Line Coverage: 55.74%
Coverage Strength: 19.35 hits per line

💛 - Coveralls

@AmanGIT07

Copy link
Copy Markdown
Contributor Author

Sandbox run: a local Frontier server with Postgres and SpiceDB in Docker, called over the Connect protocol. The binary was built from #2014 with #2011 applied, so it has main plus #2008, #2011, #2013 and #2014.

Deletion config used:

app:
  deletion:
    enabled: true
    resources:
      resource/aoi: { project: block, organization: allow }
      resource/workflow: { project: block, organization: allow }
      resource/orderitem: { project: block_unless_deletable, organization: block_unless_deletable }
      resource/insight: { project: allow, organization: allow }

resource/report has no rule.

CheckProjectDelete responses

Project with 2 AOIs, 1 workflow, 1 order item still processing, and 1 finished order item ("deletable": true):
CheckProjectDelete → HTTP 200

{
  "blockers": [
    {
      "type": "RESOURCES_EXIST",
      "subject": "resource/aoi",
      "subject_type": "resource_namespace",
      "count": 2
    },
    {
      "type": "RESOURCES_EXIST",
      "subject": "resource/orderitem",
      "subject_type": "resource_namespace",
      "count": 1
    },
    {
      "type": "RESOURCES_EXIST",
      "subject": "resource/workflow",
      "subject_type": "resource_namespace",
      "count": 1
    }
  ]
}

Project with 1 order item whose deletable is the string "true":
CheckProjectDelete → HTTP 200

{
  "blockers": [
    {
      "type": "RESOURCES_EXIST",
      "subject": "resource/orderitem",
      "subject_type": "resource_namespace",
      "count": 1
    }
  ]
}

Project with 1 resource/report resource (no rule in the config):
CheckProjectDelete → HTTP 200

{
  "blockers": [
    {
      "type": "RESOURCES_EXIST",
      "subject": "resource/report",
      "subject_type": "resource_namespace",
      "count": 1
    }
  ]
}

Disabled project with 1 AOI:
CheckProjectDelete → HTTP 200

{
  "blockers": [
    {
      "type": "RESOURCES_EXIST",
      "subject": "resource/aoi",
      "subject_type": "resource_namespace",
      "count": 1
    }
  ]
}

With enabled: false, a project with an AOI and a resource/report resource:
CheckProjectDelete → HTTP 200

{
  "can_delete": true
}

When can_delete is false it is left out of the JSON, because proto3 JSON drops default values; Connect clients read it as false.

Other checks

Check Expected Observed
Check an empty project (owner) ok, can_delete=true, no blockers ok, can_delete=true, no blockers
Check by an org viewer (no delete permission) permission_denied permission_denied
Check by a user outside the org permission_denied permission_denied
Check with the project name instead of the id not_found not_found
Check with an unknown id permission_denied permission_denied
Check the first project above after deleting its 4 blocking resources (the finished order item stays) ok, can_delete=true, no blockers ok, can_delete=true, no blockers
Check a project holding only an insight (rule allow) ok, can_delete=true, no blockers ok, can_delete=true, no blockers
With enabled: false, check a project holding an AOI and a resource/report ok, can_delete=true, no blockers ok, can_delete=true, no blockers

@AmanGIT07
AmanGIT07 force-pushed the feat/project-delete-blockers branch from 0568226 to 74cb6ad Compare October 22, 2026 10:27
@AmanGIT07
AmanGIT07 force-pushed the feat/check-project-delete branch from 2e14945 to 8f2fc33 Compare October 22, 2026 10:28
Base automatically changed from feat/project-delete-blockers to main October 23, 2026 03:21
CheckProjectDelete returns can_delete and the blockers that would refuse
DeleteProject, with the number of blocking resources per namespace. It
changes nothing. The caller needs delete permission on the project. Both
check RPCs now copy the blocker count into the response.
@AmanGIT07
AmanGIT07 force-pushed the feat/check-project-delete branch from 8f2fc33 to fefd216 Compare October 23, 2026 03:22
@AmanGIT07
AmanGIT07 merged commit cb195fb into main Oct 23, 2026
8 checks passed
@AmanGIT07
AmanGIT07 deleted the feat/check-project-delete branch October 23, 2026 03:29

This branch was successfully deployed

1 active deployment
Preview — fefd216a Deployed Oct 23, 2026 by vercel[bot]
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.

3 participants