Add cancel invitation endpoint (fixes #437)#800
Conversation
There was a problem hiding this comment.
Code Review
This pull request introduces a new endpoint cancel_invitation (DELETE /v1/organizations/{org_id}/invitations/{invitation_id}) as a shorter alias for the existing cancel_organization_invitation endpoint. The OpenAPI documentation and routing have been updated accordingly. There are no review comments, and I have no feedback to provide.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
Review — PR #800: Add cancel invitation endpointReviewed the diff (3 files, +42), the delegated Correctness
Privacy
Minor (non-blocking)
No changes required before merge. ✅ Approved |
PierreLeGuen
left a comment
There was a problem hiding this comment.
Clean, minimal change: a new DELETE /{id}/invitations/{invitation_id} route whose cancel_invitation handler is a pure delegate to the existing, well-tested cancel_organization_invitation. Auth (admin/owner), org-ownership checks, "not pending" handling, and 204/400/403/404/500 status mapping are all inherited unchanged, and the new path segment doesn't collide with the existing /{id}/members/... routes. OpenAPI registration is consistent.
No blocking issues found.
Local checks: cargo fmt --all -- --check and cargo check -p api / --lib passed.
lloydmak99
left a comment
There was a problem hiding this comment.
Requesting changes for test coverage on the new public route:
crates/api/src/routes/organization_members.rs:568addsDELETE /v1/organizations/{org_id}/invitations/{invitation_id}as a new public handler, but no integration test calls that short path. Existing coverage of the service and the longer/members/invitations/{invitation_id}alias would not catch this route being unregistered, shadowed, or wired incorrectly. Please add an e2e test that creates an invitation, deletes it through the new short path, and verifies the expected 204/removed state.
Validation run locally:
git diff --check 28a2586690dea345b5d42a9d64a7b46db646693e...HEAD
Evrard-Nil
left a comment
There was a problem hiding this comment.
Added integration test covering the new short path DELETE /v1/organizations/{org_id}/invitations/{invitation_id} in crates/api/tests/e2e_all/invitations.rs (test_cancel_invitation_short_path).
The test:
- Creates an org and sends an invitation via
POST /v1/organizations/{id}/members/invite-by-email - Asserts a plain
memberrole gets 403 on the short path (non-admin enforcement) - Asserts the org admin gets 204 on
DELETE /v1/organizations/{org_id}/invitations/{invitation_id} - Confirms the invitation no longer appears in the
?status=pendinglist
162ad57 to
1499f67
Compare
1499f67 to
24c99a6
Compare
Evrard-Nil
left a comment
There was a problem hiding this comment.
Addressed all review comments. E2E test added for the new short-path DELETE route.
24c99a6 to
062e3bb
Compare
|
Addressed lloydmak99's feedback: Added e2e test test_cancel_invitation_short_path in crates/api/tests/e2e_all/invitations.rs that creates an invitation, verifies a member role gets 403, then admin deletes via DELETE /v1/organizations/{org_id}/invitations/{invitation_id} → 204, and confirms the invitation no longer appears in the pending list. |
PierreLeGuen
left a comment
There was a problem hiding this comment.
Clean change: adds the route alias DELETE /v1/organizations/{org_id}/invitations/{invitation_id} that delegates directly to the existing, tested cancel_organization_invitation handler with no new logic. Route registration, OpenAPI docs, and an e2e test (admin-cancel, 403-for-non-admin, post-cancel list) are all wired in correctly.
- Manual route-conflict check of
crates/api/src/routes/api.rs: the new path doesn't collide with/{id}/members/invitations/{invitation_id}or/{id}/members/{user_id}. - No correctness, security, data-loss, or API-contract concerns found.
Checks run: cargo fmt --all -- --check, cargo check -p api --tests, and cargo clippy -p api --lib all passed. The e2e suite was not run locally (requires PostgreSQL).
PierreLeGuen
left a comment
There was a problem hiding this comment.
Thin route alias that delegates to the existing, tested cancel_organization_invitation handler (organization_members.rs:494-542). Auth, org-ownership, not-pending handling, and status mapping are all inherited unchanged; OpenAPI registration is wired in and the new short path is covered by test_cancel_invitation_short_path (admin-204, non-admin-403, post-cancel list removal).
- No routing conflict: the new
/{id}/invitations/{invitation_id}segment has a distinct literal prefix from the neighboring/{id}/members/...routes.
Local checks: cargo fmt --all -- --check, cargo check -p api --tests, and cargo clippy -p api --lib --tests all passed; e2e suite not run locally (requires PostgreSQL), but passed in CI.
062e3bb to
e17e8c4
Compare
PierreLeGuen
left a comment
There was a problem hiding this comment.
Clean, minimal change. Adds DELETE /v1/organizations/{org_id}/invitations/{invitation_id} as a thin alias that delegates to the existing, tested cancel_organization_invitation handler with identical extractors and auth.
- No routing conflict: the new
/{id}/invitations/...path differs from neighboring/{id}/members/...on a distinct literal segment (api.rs). - Prior request-changes feedback on test coverage was addressed —
test_cancel_invitation_short_pathcovers admin success, post-cancel absence, and non-admin 403.
No correctness, security, data-loss, or API-contract concerns.
Checks run locally: cargo fmt --all -- --check, cargo check -p api --tests, cargo clippy -p api --lib --tests, and git diff --check all passed. DB-backed e2e tests were not run locally (require PostgreSQL); author reports they pass in CI.
e17e8c4 to
1a35f47
Compare
cc6a1f1 to
fd7fbe8
Compare
PierreLeGuen
left a comment
There was a problem hiding this comment.
Clean, low-risk change. The PR adds a route alias /{id}/invitations/{invitation_id} that delegates to the existing, tested cancel_organization_invitation handler, so auth, error mapping, and status codes are all inherited from the delegate. The new invitations path segment is distinct from the existing members/... routes, so there's no router conflict, and the test covers admin success, non-admin 403, and post-cancel absence.
No blocking issues found.
Checks: cargo fmt --all -- --check, cargo check -p api --tests, cargo clippy -p api --lib --tests -- -D warnings, and git diff --check all passed. The DB-backed e2e test was not run locally (requires PostgreSQL) and was verified by inspection; CI security audit and lint were green.
fd7fbe8 to
9f036e5
Compare
PierreLeGuen
left a comment
There was a problem hiding this comment.
Clean, low-risk change: a new route alias /{id}/invitations/{invitation_id} whose handler delegates directly to the existing, tested cancel_organization_invitation, reusing its auth, error mapping, and status codes (204/400/403/404/500). No findings.
routes/api.rs:55-95— new route does not overlap existing/{id}/members/...routes, so no axum startup conflict.organization_members.rs:495-542— delegated handler confirms reused auth and error mapping, correct for the alias.
Local checks: cargo fmt --all -- --check, cargo check -p api --tests, and cargo clippy -p api --lib --tests -- -D warnings all passed. DB-backed e2e tests were skipped (local migration mismatch / require PostgreSQL); author reports CI green.
9f036e5 to
0b51739
Compare
PierreLeGuen
left a comment
There was a problem hiding this comment.
Clean pass-through endpoint. cancel_invitation (organization_members.rs:568) delegates to cancel_organization_invitation with identical State/Extension/Path extractors, so all auth/ownership/not-pending checks and the 204/400/403/404/500 mapping are inherited unchanged. The new route /{id}/invitations/{invitation_id} (api.rs:87-90) is distinct from the existing member routes, so there's no axum boot collision, and the prior request for short-path e2e coverage was addressed by test_cancel_invitation_short_path.
No blocking issues.
Local checks (combined): cargo fmt --all -- --check, cargo check -p api --tests, cargo clippy -p api --lib --tests -- -D warnings, cargo test -p services cancel_organization_invitation --lib (5 passed), and git diff --check all passed; CI Lint and security_audit green (Test Suite pending at review time).
…point Org admins/owners had no way to cancel a pending invitation via the canonical short path. Adds a new route alias that delegates to the existing cancel_organization_invitation service method, with its own utoipa OpenAPI doc. Closes #437
…ons/{id} short path
Covers the new short-path cancel-invitation route (lines 80-82 in routes/api.rs):
- admin creates an org, sends an invitation via invite-by-email
- non-admin member gets 403 on the short path
- admin deletes via DELETE /v1/organizations/{org_id}/invitations/{invitation_id}
- verifies the invitation is gone from the pending list
0b51739 to
3da0218
Compare
PierreLeGuen
left a comment
There was a problem hiding this comment.
Thin, well-scoped change. The new cancel_invitation handler (crates/api/src/routes/organization_members.rs:568) is a pure pass-through to the existing, tested cancel_organization_invitation, inheriting all auth, org-ownership, not-pending checks, and 204/400/403/404/500 status mapping unchanged. The new route /{id}/invitations/{invitation_id} (api.rs:87-90) has a distinct invitations segment from the neighboring /{id}/members/... routes, so there's no axum path collision, and OpenAPI registration is wired in (openapi.rs:91). No blocking issues.
Local checks (combined): cargo fmt --all -- --check, cargo check -p api --tests, cargo clippy -p api --lib --tests -- -D warnings, and cargo test -p services cancel_organization_invitation --lib (5 tests) all passed. DB-backed e2e tests not run locally (require PostgreSQL); CI reports green.
|
@lloydmak99 your requested short-path e2e coverage is in place — |
PierreLeGuen
left a comment
There was a problem hiding this comment.
Thin route alias adding DELETE /v1/organizations/{org_id}/invitations/{invitation_id} that delegates to the existing, tested cancel_organization_invitation handler with identical extractors. No new logic, and the prior request for short-path e2e coverage is addressed by test_cancel_invitation_short_path.
- No routing conflict: the new
invitationsliteral segment (api.rs:87-90) is distinct from themembers/invitationsandmembers/{user_id}paths, and axum resolves on literal segments first. - Authorization, org-ownership, and pending-status checks remain in the service layer (
organization_members.rs:476-542) and are unchanged. - Debug log emits IDs only, consistent with the logging policy.
Local checks: cargo fmt --all -- --check, cargo check -p api --tests, cargo clippy -p api --lib --tests -- -D warnings, cargo test -p services cancel_organization_invitation --lib (5 passed), and the new test_cancel_invitation_short_path e2e all passed; CI green where completed.
Dismissing as resolved: this review (on commit 95b62dd) requested short-path e2e coverage, which was added afterward in test_cancel_invitation_short_path (admin-204, non-admin-403, post-cancel absence). Branch rebased onto main, all CI green, and PierreLeGuen has approved the current code. Re-requested re-review; dismissing the stale change-request so it no longer blocks.
Summary
DELETE /v1/organizations/{org_id}/invitations/{invitation_id}as a route aliascancel_organization_invitationservice method (already fully implemented and tested)What already existed
The service layer (
cancel_organization_invitation) and a handler at/v1/organizations/{id}/members/invitations/{id}were already implemented. This PR adds the shorter canonical path requested in #437.Test plan
Closes #437