[fix] Resolve broken oss invites from non-default projects#4461
[fix] Resolve broken oss invites from non-default projects#4461junaway wants to merge 3 commits into
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
📝 WalkthroughSummary by CodeRabbit
WalkthroughThis pull request refactors organization-level invitation handling to operate independently of project context, adding organization-scoped database queries and validators, and updating router endpoints and service logic to use these new organization-level lookups. Additionally, it bumps the version to 0.100.4 across all packages and manifests. ChangesOrganization-scoped invitation refactor
Version bump across monorepo
🎯 3 (Moderate) | ⏱️ ~20 minutes 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Pull request overview
This PR addresses OSS organization invitation handling so invites can be accepted regardless of the project active when they were created, alongside a patch version bump to 0.100.4.
Changes:
- Adds organization-wide invitation lookup/validation paths for OSS invite flows.
- Updates OSS invite/resend routes to use the organization’s default project when creating links.
- Bumps package, Python project, lockfile, and Helm chart versions.
Reviewed changes
Copilot reviewed 12 out of 16 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
api/oss/src/services/organization_service.py |
Adds organization-scoped invitation checks and acceptance logic. |
api/oss/src/services/db_manager.py |
Adds organization-scoped invitation queries and updates signup assignment lookup. |
api/oss/src/routers/organization_router.py |
Routes OSS invite/resend through the organization default project. |
api/pyproject.toml |
Bumps API package version. |
api/uv.lock |
Updates locked API/service-related package versions. |
clients/python/pyproject.toml |
Bumps Python client version. |
clients/python/uv.lock |
Updates locked Python client version. |
hosting/kubernetes/helm/Chart.yaml |
Bumps Helm chart and app version. |
sdks/python/pyproject.toml |
Bumps Python SDK version. |
sdks/python/uv.lock |
Updates locked SDK/client versions. |
services/pyproject.toml |
Bumps services package version. |
services/uv.lock |
Updates locked services/SDK/client versions. |
web/package.json |
Bumps web workspace version. |
web/ee/package.json |
Bumps EE web package version. |
web/oss/package.json |
Bumps OSS web package version. |
web/packages/agenta-api-client/package.json |
Bumps generated API client package version. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| select(InvitationDB) | ||
| .join(ProjectDB, InvitationDB.project_id == ProjectDB.id) | ||
| .where( | ||
| InvitationDB.email == email, | ||
| ProjectDB.organization_id == uuid.UUID(organization_id), | ||
| ) |
There was a problem hiding this comment.
Actionable comments posted: 3
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: 3523b83d-1847-4322-be7d-ebecfdb9b6d3
⛔ Files ignored due to path filters (4)
api/uv.lockis excluded by!**/*.lockclients/python/uv.lockis excluded by!**/*.locksdks/python/uv.lockis excluded by!**/*.lockservices/uv.lockis excluded by!**/*.lock
📒 Files selected for processing (12)
api/oss/src/routers/organization_router.pyapi/oss/src/services/db_manager.pyapi/oss/src/services/organization_service.pyapi/pyproject.tomlclients/python/pyproject.tomlhosting/kubernetes/helm/Chart.yamlsdks/python/pyproject.tomlservices/pyproject.tomlweb/ee/package.jsonweb/oss/package.jsonweb/package.jsonweb/packages/agenta-api-client/package.json
| project_db = await db_manager.get_default_project_by_organization_id( | ||
| organization_id=organization_id | ||
| ) |
There was a problem hiding this comment.
🛠️ Refactor suggestion | 🟠 Major | ⚡ Quick win
Keep router-to-service boundary; move default-project lookup into service layer.
The router now directly depends on db_manager for default-project resolution. Please shift this lookup into organization_service and keep the router calling service-only entrypoints.
As per coding guidelines: "Dependency direction for API layers must be: Router -> Service -> DAO Interface -> DAO Implementation -> DB."
Also applies to: 351-353
| async def get_project_invitation_by_organization_and_email( | ||
| organization_id: str, | ||
| email: str, | ||
| ) -> InvitationDB: |
There was a problem hiding this comment.
Return type should be optional for nullable invitation lookups.
Both helpers can return None (.first()), but the signatures declare InvitationDB. This weakens type contracts and can mislead callers.
Suggested fix
-async def get_project_invitation_by_organization_and_email(
+async def get_project_invitation_by_organization_and_email(
organization_id: str,
email: str,
-) -> InvitationDB:
+) -> Optional[InvitationDB]:-async def get_project_invitation_by_organization_token_and_email(
+async def get_project_invitation_by_organization_token_and_email(
organization_id: str,
token: str,
email: str,
-) -> InvitationDB:
+) -> Optional[InvitationDB]:As per coding guidelines: "Use Optional[DTO] for missing entities, List[DTO] for collections."
Also applies to: 1563-1567
| if invitation is not None and invitation.expiration_date > datetime.now( | ||
| timezone.utc | ||
| ): | ||
| return invitation | ||
|
|
||
| return None |
There was a problem hiding this comment.
Reject already-used invitations during org-scoped validation.
check_valid_organization_invitation treats a used invitation as valid if it has not expired. This makes invitation acceptance replayable until expiry.
Suggested fix
- if invitation is not None and invitation.expiration_date > datetime.now(
+ if (
+ invitation is not None
+ and not invitation.used
+ and invitation.expiration_date > datetime.now(
timezone.utc
- ):
+ )):
return invitation
Railway Preview Environment
|
Resolves this issue.