Skip to content

Add cancel invitation endpoint (fixes #437)#800

Merged
Evrard-Nil merged 6 commits into
mainfrom
feat/437-cancel-invitation
Jun 24, 2026
Merged

Add cancel invitation endpoint (fixes #437)#800
Evrard-Nil merged 6 commits into
mainfrom
feat/437-cancel-invitation

Conversation

@Evrard-Nil

Copy link
Copy Markdown
Collaborator

Summary

  • Org admins/owners had no way to cancel a pending invitation via the canonical short path
  • Adds DELETE /v1/organizations/{org_id}/invitations/{invitation_id} as a route alias
  • Delegates to the existing cancel_organization_invitation service method (already fully implemented and tested)
  • Verifies caller is admin/owner, confirms invitation belongs to the org, and deletes it
  • Returns 204 on success, 404 if not found, 403 if unauthorized, 400 if invitation is not pending
  • Adds utoipa OpenAPI docs for the new path

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

  • `cargo test --lib` passes (391 tests)
  • Staging: POST invitation to an org → DELETE it → GET list shows it gone
  • Staging: non-admin attempting DELETE returns 403
  • Staging: DELETE invitation belonging to different org returns 404

Closes #437

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

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.

@claude

claude Bot commented Jun 16, 2026

Copy link
Copy Markdown

Review — PR #800: Add cancel invitation endpoint

Reviewed the diff (3 files, +42), the delegated cancel_organization_invitation handler, the router registration, and the existing log line for privacy. This is a thin, well-scoped route alias.

Correctness

  • cancel_invitation cleanly delegates to the existing, tested cancel_organization_invitation, so auth (admin/owner), org-ownership verification, and "not pending" handling are all inherited. ✅
  • Error → status mapping (204/400/403/404/500) matches the documented responses. ✅
  • No route conflict: /{id}/invitations/{invitation_id} has a distinct literal segment (invitations) from the neighboring /{id}/members/{user_id} and /{id}/members/invitations/{invitation_id}, so axum routing stays unambiguous. ✅

Privacy

  • The inherited log line emits only invitation_id, org_id, and user_id — IDs only, compliant with the logging rules. ✅

Minor (non-blocking)

  • Slight duplication of the OpenAPI #[utoipa::path] block between the two aliased handlers. Acceptable since utoipa requires per-handler annotations, but worth a doc note that the two paths are intentional aliases (the doc comment already says this).
  • The staging test-plan items (non-admin → 403, cross-org → 404) are unchecked; worth confirming before/after merge, though they're covered by the underlying service tests.

No changes required before merge.

✅ Approved

@PierreLeGuen PierreLeGuen left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

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 lloydmak99 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Requesting changes for test coverage on the new public route:

  1. crates/api/src/routes/organization_members.rs:568 adds DELETE /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 Evrard-Nil temporarily deployed to Cloud API test env June 17, 2026 06:46 — with GitHub Actions Inactive

@Evrard-Nil Evrard-Nil left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

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:

  1. Creates an org and sends an invitation via POST /v1/organizations/{id}/members/invite-by-email
  2. Asserts a plain member role gets 403 on the short path (non-admin enforcement)
  3. Asserts the org admin gets 204 on DELETE /v1/organizations/{org_id}/invitations/{invitation_id}
  4. Confirms the invitation no longer appears in the ?status=pending list

@Evrard-Nil Evrard-Nil requested a review from lloydmak99 June 17, 2026 06:46
@Evrard-Nil Evrard-Nil temporarily deployed to Cloud API test env June 17, 2026 06:54 — with GitHub Actions Inactive
@Evrard-Nil Evrard-Nil force-pushed the feat/437-cancel-invitation branch from 162ad57 to 1499f67 Compare June 17, 2026 07:37
@Evrard-Nil Evrard-Nil temporarily deployed to Cloud API test env June 17, 2026 07:37 — with GitHub Actions Inactive
@Evrard-Nil Evrard-Nil force-pushed the feat/437-cancel-invitation branch from 1499f67 to 24c99a6 Compare June 17, 2026 10:22
@Evrard-Nil Evrard-Nil temporarily deployed to Cloud API test env June 17, 2026 10:22 — with GitHub Actions Inactive

@Evrard-Nil Evrard-Nil left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Addressed all review comments. E2E test added for the new short-path DELETE route.

@Evrard-Nil Evrard-Nil force-pushed the feat/437-cancel-invitation branch from 24c99a6 to 062e3bb Compare June 17, 2026 10:40
@Evrard-Nil Evrard-Nil temporarily deployed to Cloud API test env June 17, 2026 10:40 — with GitHub Actions Inactive
@Evrard-Nil

Copy link
Copy Markdown
Collaborator Author

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.

@Evrard-Nil Evrard-Nil requested a review from PierreLeGuen June 17, 2026 11:01

@PierreLeGuen PierreLeGuen left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

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).

@Evrard-Nil Evrard-Nil requested a review from PierreLeGuen June 17, 2026 12:11

@PierreLeGuen PierreLeGuen left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

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.

@Evrard-Nil Evrard-Nil force-pushed the feat/437-cancel-invitation branch from 062e3bb to e17e8c4 Compare June 17, 2026 12:32
@Evrard-Nil Evrard-Nil temporarily deployed to Cloud API test env June 17, 2026 12:32 — with GitHub Actions Inactive
@Evrard-Nil Evrard-Nil requested a review from PierreLeGuen June 17, 2026 12:32

@PierreLeGuen PierreLeGuen left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

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_path covers 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.

@Evrard-Nil Evrard-Nil force-pushed the feat/437-cancel-invitation branch from e17e8c4 to 1a35f47 Compare June 17, 2026 13:08
@Evrard-Nil Evrard-Nil temporarily deployed to Cloud API test env June 17, 2026 13:08 — with GitHub Actions Inactive
@Evrard-Nil Evrard-Nil requested a review from PierreLeGuen June 17, 2026 13:08
@Evrard-Nil Evrard-Nil force-pushed the feat/437-cancel-invitation branch from cc6a1f1 to fd7fbe8 Compare June 17, 2026 15:25
@Evrard-Nil Evrard-Nil requested a review from PierreLeGuen June 17, 2026 15:25
@Evrard-Nil Evrard-Nil temporarily deployed to Cloud API test env June 17, 2026 15:25 — with GitHub Actions Inactive

@PierreLeGuen PierreLeGuen left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

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.

@Evrard-Nil Evrard-Nil force-pushed the feat/437-cancel-invitation branch from fd7fbe8 to 9f036e5 Compare June 17, 2026 19:33
@Evrard-Nil Evrard-Nil temporarily deployed to Cloud API test env June 17, 2026 19:33 — with GitHub Actions Inactive
@Evrard-Nil Evrard-Nil requested a review from PierreLeGuen June 17, 2026 19:33

@PierreLeGuen PierreLeGuen left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

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.

@Evrard-Nil Evrard-Nil force-pushed the feat/437-cancel-invitation branch from 9f036e5 to 0b51739 Compare June 17, 2026 19:43
@Evrard-Nil Evrard-Nil temporarily deployed to Cloud API test env June 17, 2026 19:44 — with GitHub Actions Inactive
@Evrard-Nil Evrard-Nil requested a review from PierreLeGuen June 17, 2026 19:44

@PierreLeGuen PierreLeGuen left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

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
@Evrard-Nil Evrard-Nil force-pushed the feat/437-cancel-invitation branch from 0b51739 to 3da0218 Compare June 17, 2026 20:04
@Evrard-Nil Evrard-Nil temporarily deployed to Cloud API test env June 17, 2026 20:04 — with GitHub Actions Inactive
@Evrard-Nil Evrard-Nil requested a review from PierreLeGuen June 17, 2026 20:04

@PierreLeGuen PierreLeGuen left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

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.

@Evrard-Nil Evrard-Nil had a problem deploying to Cloud API test env June 18, 2026 14:42 — with GitHub Actions Failure
@Evrard-Nil

Copy link
Copy Markdown
Collaborator Author

@lloydmak99 your requested short-path e2e coverage is in place — test_cancel_invitation_short_path in crates/api/tests/e2e_all/invitations.rs creates an invitation, deletes it via the new DELETE /v1/organizations/{org_id}/invitations/{invitation_id} path, asserts 204 + post-cancel absence from the pending list, and asserts 403 for a non-admin member. Pierre has since approved. I've rebased onto current main to clear the BEHIND state; the earlier Test Suite red was the known environmental e2e flake (Failed to acquire advisory lock for e2e bootstrap: ... Closed on an unrelated test), not this change. Could you re-review so we can land it?

@Evrard-Nil Evrard-Nil requested a review from PierreLeGuen June 24, 2026 08:27
@Evrard-Nil Evrard-Nil temporarily deployed to Cloud API test env June 24, 2026 08:27 — with GitHub Actions Inactive

@PierreLeGuen PierreLeGuen left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

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 invitations literal segment (api.rs:87-90) is distinct from the members/invitations and members/{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.

@Evrard-Nil Evrard-Nil dismissed lloydmak99’s stale review June 24, 2026 08:57

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.

@Evrard-Nil Evrard-Nil temporarily deployed to Cloud API test env June 24, 2026 15:48 — with GitHub Actions Inactive
@Evrard-Nil Evrard-Nil merged commit a2fad42 into main Jun 24, 2026
8 checks passed
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.

Add cancel/revoke invitation endpoint

3 participants