-
Notifications
You must be signed in to change notification settings - Fork 10
Refactor Onboarding Endpoint to Make User Context Optional #335
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
1cd2d01
a229095
2f2f09b
77dbd0b
b24059e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -1,7 +1,8 @@ | ||||||||||||||||||||||||||||
| import uuid | ||||||||||||||||||||||||||||
| import re | ||||||||||||||||||||||||||||
| import secrets | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| from fastapi import APIRouter, HTTPException, Depends | ||||||||||||||||||||||||||||
| from pydantic import BaseModel, EmailStr | ||||||||||||||||||||||||||||
| from pydantic import BaseModel, EmailStr, model_validator, field_validator | ||||||||||||||||||||||||||||
| from sqlmodel import Session | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| from app.crud import ( | ||||||||||||||||||||||||||||
|
|
@@ -32,9 +33,34 @@ | |||||||||||||||||||||||||||
| class OnboardingRequest(BaseModel): | ||||||||||||||||||||||||||||
| organization_name: str | ||||||||||||||||||||||||||||
| project_name: str | ||||||||||||||||||||||||||||
| email: EmailStr | ||||||||||||||||||||||||||||
| password: str | ||||||||||||||||||||||||||||
| user_name: str | ||||||||||||||||||||||||||||
| email: EmailStr | None = None | ||||||||||||||||||||||||||||
| password: str | None = None | ||||||||||||||||||||||||||||
| user_name: str | None = None | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| @staticmethod | ||||||||||||||||||||||||||||
| def _clean_username(raw: str, max_len: int = 200) -> str: | ||||||||||||||||||||||||||||
| """ | ||||||||||||||||||||||||||||
| Normalize a string into a safe username that can also be used | ||||||||||||||||||||||||||||
| as the local part of an email address. | ||||||||||||||||||||||||||||
| """ | ||||||||||||||||||||||||||||
| username = re.sub(r"[^A-Za-z0-9._]", "_", raw.strip().lower()) | ||||||||||||||||||||||||||||
| username = re.sub(r"[._]{2,}", "_", username) # collapse repeats | ||||||||||||||||||||||||||||
| username = username.strip("._") # remove leading/trailing | ||||||||||||||||||||||||||||
| return username[:max_len] | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| @model_validator(mode="after") | ||||||||||||||||||||||||||||
| def set_defaults(self): | ||||||||||||||||||||||||||||
| if self.user_name is None: | ||||||||||||||||||||||||||||
| self.user_name = self.project_name + " User" | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| if self.email is None: | ||||||||||||||||||||||||||||
| local_part = self._clean_username(self.user_name, max_len=200) | ||||||||||||||||||||||||||||
| suffix = secrets.token_hex(3) | ||||||||||||||||||||||||||||
AkhileshNegi marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||||||||||||||||
| self.email = f"{local_part}.{suffix}@kaapi.org" | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
|
Comment on lines
+56
to
+60
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Fix: Ensure auto-generated email local-part is valid and within RFC length (64). Two issues can cause user creation to fail (likely as 500 from
Harden the generation by providing a fallback base and truncating to fit 64 chars (leaving room for the dot and suffix). Apply this diff: - if self.email is None:
- local_part = self._clean_username(self.user_name, max_len=200)
- suffix = secrets.token_hex(3)
- self.email = f"{local_part}.{suffix}@kaapi.org"
+ if self.email is None:
+ suffix = secrets.token_hex(3) # 6 hex chars
+ base = self._clean_username(self.user_name or self.project_name, max_len=200).strip("._")
+ if not base:
+ base = "user"
+ # RFC local-part max length is 64; reserve 1 for the dot and len(suffix)
+ allowed = max(1, 64 - 1 - len(suffix))
+ local_part = base[:allowed]
+ self.email = f"{local_part}.{suffix}@kaapi.org"📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||
| if self.password is None: | ||||||||||||||||||||||||||||
| self.password = secrets.token_urlsafe(12) | ||||||||||||||||||||||||||||
| return self | ||||||||||||||||||||||||||||
|
Comment on lines
+51
to
+63
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 💡 Verification agent 🧩 Analysis chainPlease add tests that omit optional fields to exercise default generation. Current tests historically always provided
If possible, also add a test omitting all three. You can use this script to quickly inspect existing onboarding tests for omitted fields: 🏁 Script executed: #!/bin/bash
# Show onboarding POST payloads and presence of optional keys
rg -n -C2 --type=py -P 'client\.post\(.+?/onboard' backend/app/tests
echo '--- email keys ---'
rg -n -C0 --type=py -P '"email"\s*:' backend/app/tests/api/routes/test_onboarding.py || true
echo '--- password keys ---'
rg -n -C0 --type=py -P '"password"\s*:' backend/app/tests/api/routes/test_onboarding.py || true
echo '--- user_name keys ---'
rg -n -C0 --type=py -P '"user_name"\s*:' backend/app/tests/api/routes/test_onboarding.py || trueLength of output: 954 Add tests for default generation of optional onboarding fields Current tests in
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| class OnboardingResponse(BaseModel): | ||||||||||||||||||||||||||||
|
|
@@ -82,7 +108,7 @@ def onboard_user(request: OnboardingRequest, session: SessionDep): | |||||||||||||||||||||||||||
| user = existing_user | ||||||||||||||||||||||||||||
| else: | ||||||||||||||||||||||||||||
| user_create = UserCreate( | ||||||||||||||||||||||||||||
| name=request.user_name, | ||||||||||||||||||||||||||||
| full_name=request.user_name, | ||||||||||||||||||||||||||||
avirajsingh7 marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||||||||||||||||
| email=request.email, | ||||||||||||||||||||||||||||
| password=request.password, | ||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Verification agent
🧩 Analysis chain
Making email/password/user_name optional — LGTM; ensure tests cover no-email/no-password paths
This aligns with the PR objective. Please confirm there are tests that omit each of these fields to exercise default generation.
You can verify coverage with:
🏁 Script executed:
Length of output: 1076
Add tests for missing optional fields
I reviewed
test_onboarding.pyand all existing payloads includeemail,password, anduser_name. We need explicit coverage for cases where each of these is omitted to exercise default generation.Please add tests that:
emailand assert a generated email is assignedpasswordand assert a generated password is assigneduser_nameand assert a generated user name is assignedFile to update:
backend/app/tests/api/routes/test_onboarding.py🤖 Prompt for AI Agents