🐛 fix(users.py): change route paths for user-related endpoints to improve consistency and readability

✨ feat(users.py): add support for resetting a user's password
🐛 fix(crud.py): fix update_user function to properly handle unchanged attributes and return 304 status code if nothing is updated
🐛 fix(user.py): change field name in UserUpdate model from 'username' to 'password' to reflect the intended functionality
🐛 fix(index.ts): update API routes in frontend controller functions to match the changed user-related endpoint paths
This commit is contained in:
Gabriel Luiz Freitas Almeida 2023-09-05 17:19:11 -03:00
commit a7db386277
4 changed files with 60 additions and 22 deletions

View file

@ -20,13 +20,14 @@ from langflow.services.auth.utils import (
get_password_hash, get_password_hash,
) )
from langflow.services.database.models.user.crud import ( from langflow.services.database.models.user.crud import (
get_user_by_id,
update_user, update_user,
) )
router = APIRouter(tags=["Users"]) router = APIRouter(tags=["Users"], prefix="/users")
@router.post("/user", response_model=UserRead, status_code=201) @router.post("/", response_model=UserRead, status_code=201)
def add_user( def add_user(
user: UserCreate, user: UserCreate,
session: Session = Depends(get_session), session: Session = Depends(get_session),
@ -50,7 +51,7 @@ def add_user(
return new_user return new_user
@router.get("/user", response_model=UserRead) @router.get("/whoami", response_model=UserRead)
def read_current_user( def read_current_user(
current_user: User = Depends(get_current_active_user), current_user: User = Depends(get_current_active_user),
) -> User: ) -> User:
@ -60,7 +61,7 @@ def read_current_user(
return current_user return current_user
@router.get("/users", response_model=UsersResponse) @router.get("/", response_model=UsersResponse)
def read_all_users( def read_all_users(
skip: int = 0, skip: int = 0,
limit: int = 10, limit: int = 10,
@ -82,20 +83,53 @@ def read_all_users(
) )
@router.patch("/user/{user_id}", response_model=UserRead) @router.patch("/{user_id}", response_model=UserRead)
def patch_user( def patch_user(
user_id: UUID, user_id: UUID,
user: UserUpdate, user_update: UserUpdate,
_: Session = Depends(get_current_active_user), user: Session = Depends(get_current_active_user),
session: Session = Depends(get_session), session: Session = Depends(get_session),
) -> User: ) -> User:
""" """
Update an existing user's data. Update an existing user's data.
""" """
return update_user(user_id, user, session) if not user.is_superuser and user.id != user_id:
raise HTTPException(
status_code=403, detail="You don't have the permission to update this user"
)
if user_db := get_user_by_id(session, user_id):
return update_user(user_db, user_update, session)
else:
raise HTTPException(status_code=404, detail="User not found")
@router.delete("/user/{user_id}") @router.patch("/{user_id}/reset-password", response_model=UserRead)
def reset_password(
user_id: UUID,
user_update: UserUpdate,
user: Session = Depends(get_current_active_user),
session: Session = Depends(get_session),
) -> User:
"""
Reset a user's password.
"""
if user_id != user.id:
raise HTTPException(
status_code=400, detail="You can't change another user's password"
)
if not user:
raise HTTPException(status_code=404, detail="User not found")
user.password = get_password_hash(user_update.password)
session.commit()
session.refresh(user)
return user
@router.delete("/{user_id}", response_model=dict)
def delete_user( def delete_user(
user_id: UUID, user_id: UUID,
current_user: User = Depends(get_current_active_superuser), current_user: User = Depends(get_current_active_superuser),

View file

@ -20,20 +20,24 @@ def get_user_by_id(db: Session, id: UUID) -> Union[User, None]:
def update_user( def update_user(
user_id: UUID, user: UserUpdate, db: Session = Depends(get_session) user_db: User, user: UserUpdate, db: Session = Depends(get_session)
) -> User: ) -> User:
user_db = get_user_by_id(db, user_id)
if not user_db: if not user_db:
raise HTTPException(status_code=404, detail="User not found") raise HTTPException(status_code=404, detail="User not found")
user_db_by_username = get_user_by_username(db, user.username) # type: ignore # user_db_by_username = get_user_by_username(db, user.username) # type: ignore
if user_db_by_username and user_db_by_username.id != user_id: # if user_db_by_username and user_db_by_username.id != user_id:
raise HTTPException(status_code=409, detail="Username already exists") # raise HTTPException(status_code=409, detail="Username already exists")
user_data = user.dict(exclude_unset=True) user_data = user.dict(exclude_unset=True)
changed = False
for attr, value in user_data.items(): for attr, value in user_data.items():
if hasattr(user_db, attr) and value is not None: if hasattr(user_db, attr) and value is not None:
setattr(user_db, attr, value) setattr(user_db, attr, value)
changed = True
if not changed:
raise HTTPException(status_code=304, detail="Nothing to update")
user_db.updated_at = datetime.now(timezone.utc) user_db.updated_at = datetime.now(timezone.utc)
flag_modified(user_db, "updated_at") flag_modified(user_db, "updated_at")
@ -49,5 +53,5 @@ def update_user(
def update_user_last_login_at(user_id: UUID, db: Session = Depends(get_session)): def update_user_last_login_at(user_id: UUID, db: Session = Depends(get_session)):
user_data = UserUpdate(last_login_at=datetime.now(timezone.utc)) # type: ignore user_data = UserUpdate(last_login_at=datetime.now(timezone.utc)) # type: ignore
user = get_user_by_id(db, user_id)
return update_user(user_id, user_data, db) return update_user(user, user_data, db)

View file

@ -40,7 +40,7 @@ class UserRead(SQLModel):
class UserUpdate(SQLModel): class UserUpdate(SQLModel):
username: Optional[str] = Field() password: Optional[str] = Field()
is_active: Optional[bool] = Field() is_active: Optional[bool] = Field()
is_superuser: Optional[bool] = Field() is_superuser: Optional[bool] = Field()
last_login_at: Optional[datetime] = Field() last_login_at: Optional[datetime] = Field()

View file

@ -402,7 +402,7 @@ export async function renewAccessToken(token: string) {
export async function getLoggedUser(): Promise<Users | null> { export async function getLoggedUser(): Promise<Users | null> {
try { try {
const res = await api.get(`${BASE_URL_API}user`); const res = await api.get(`${BASE_URL_API}users/whoami`);
if (res.status === 200) { if (res.status === 200) {
return res.data; return res.data;
@ -416,7 +416,7 @@ export async function getLoggedUser(): Promise<Users | null> {
export async function addUser(user: UserInputType): Promise<Array<Users>> { export async function addUser(user: UserInputType): Promise<Array<Users>> {
try { try {
const res = await api.post(`${BASE_URL_API}user`, user); const res = await api.post(`${BASE_URL_API}users/`, user);
if (res.status === 200) { if (res.status === 200) {
return res.data; return res.data;
} }
@ -433,7 +433,7 @@ export async function getUsersPage(
): Promise<Array<Users>> { ): Promise<Array<Users>> {
try { try {
const res = await api.get( const res = await api.get(
`${BASE_URL_API}users?skip=${skip}&limit=${limit}` `${BASE_URL_API}users/?skip=${skip}&limit=${limit}`
); );
if (res.status === 200) { if (res.status === 200) {
return res.data; return res.data;
@ -447,7 +447,7 @@ export async function getUsersPage(
export async function deleteUser(user_id: string) { export async function deleteUser(user_id: string) {
try { try {
const res = await api.delete(`${BASE_URL_API}user/${user_id}`); const res = await api.delete(`${BASE_URL_API}users/${user_id}`);
if (res.status === 200) { if (res.status === 200) {
return res.data; return res.data;
} }
@ -459,7 +459,7 @@ export async function deleteUser(user_id: string) {
export async function updateUser(user_id: string, user: Users) { export async function updateUser(user_id: string, user: Users) {
try { try {
const res = await api.patch(`${BASE_URL_API}user/${user_id}`, user); const res = await api.patch(`${BASE_URL_API}users/${user_id}`, user);
if (res.status === 200) { if (res.status === 200) {
return res.data; return res.data;
} }