Skip to content

Commit

Permalink
Fix user and organization endpoints
Browse files Browse the repository at this point in the history
  • Loading branch information
aloftus23 committed Dec 6, 2024
1 parent 2c13b19 commit 29c4bb9
Show file tree
Hide file tree
Showing 9 changed files with 91 additions and 61 deletions.
15 changes: 14 additions & 1 deletion backend/src/xfd_django/xfd_api/api_methods/organization.py
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,7 @@ def get_organization(organization_id, current_user):
"email": role.user.email,
"firstName": role.user.firstName,
"lastName": role.user.lastName,
"fullName": role.user.fullName,
},
}
for role in organization.userRoles.all()
Expand Down Expand Up @@ -658,6 +659,7 @@ def update_organization(organization_id: str, organization_data, current_user):
"email": role.user.email,
"firstName": role.user.firstName,
"lastName": role.user.lastName,
"fullName": role.user.fullName,
},
}
for role in organization.userRoles.all()
Expand Down Expand Up @@ -1031,7 +1033,18 @@ def list_organizations_v2(state, regionId, current_user):
"countyFips": org.countyFips,
"type": org.type,
"userRoles": [
{"id": str(role.id), "role": role.role, "approved": role.approved}
{
"id": str(role.id),
"role": role.role,
"approved": role.approved,
"user": {
"id": str(role.user.id),
"email": role.user.email,
"firstName": role.user.firstName,
"lastName": role.user.lastName,
"fullName": role.user.fullName,
},
}
for role in org.userRoles.all()
],
"tags": [
Expand Down
83 changes: 55 additions & 28 deletions backend/src/xfd_django/xfd_api/api_methods/user.py
Original file line number Diff line number Diff line change
Expand Up @@ -129,50 +129,71 @@ def accept_terms(version_data, current_user):


# DELETE: /users/{userId}
def delete_user(current_user, target_user_id):
"""
Delete a user by ID.
Args:
request : The HTTP request containing authorization and target for deletion..
Raises:
HTTPException: If the user is not authorized or the user is not found.
def delete_user(target_user_id, current_user):
"""Delete a user by ID."""
# Validate that the user ID is a valid UUID
if not target_user_id:
raise HTTPException(status_code=404, detail="User not found")

Returns:
JSONResponse: The result of the deletion.
"""
# Check if the current user has permission to access/update this user
if not can_access_user(current_user, target_user_id):
return HTTPException(status_code=401, detail="Unauthorized")
raise HTTPException(status_code=403, detail="Unauthorized access.")

try:
target_user = User.objects.get(id=target_user_id)
result = target_user.delete()
return JSONResponse(status_code=200, content={"result": result})
target_user.delete()
# Return success response
return {
"status": "success",
"message": f"User {target_user_id} has been deleted successfully.",
}

except Exception as e:
raise HTTPException(status_code=500, detail=str(e))


# GET: /users
def get_users(current_user):
"""
Retrieve a list of all users.
Args:
current_user : The user making the request.
Raises:
HTTPException: If the user is not authorized.
Returns:
List[User]: A list of all users.
"""
"""Retrieve a list of all users."""
try:
# Check if user is a regional admin or global admin
if not (is_global_view_admin(current_user)):
raise HTTPException(status_code=401, detail="Unauthorized")

users = User.objects.all().prefetch_related("roles", "roles.organization")
return [UserSchema.model_validate(user) for user in users]
users = User.objects.all().prefetch_related("roles__organization")

# Return the updated user details
return [
{
"id": str(user.id),
"createdAt": user.createdAt.isoformat(),
"updatedAt": user.updatedAt.isoformat(),
"firstName": user.firstName,
"lastName": user.lastName,
"fullName": user.fullName,
"email": user.email,
"regionId": user.regionId,
"state": user.state,
"userType": user.userType,
"lastLoggedIn": user.lastLoggedIn,
"acceptedTermsVersion": user.acceptedTermsVersion,
"roles": [
{
"id": str(role.id),
"approved": role.approved,
"role": role.role,
"organization": {
"id": str(role.organization.id),
"name": role.organization.name,
}
if role.organization
else None,
}
for role in user.roles.all()
],
}
for user in users
]
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))

Expand Down Expand Up @@ -280,9 +301,12 @@ def get_users_v2(state, regionId, invitePending, current_user):
"state": user.state,
"userType": user.userType,
"lastLoggedIn": user.lastLoggedIn,
"acceptedTermsVersion": user.acceptedTermsVersion,
"roles": [
{
"id": str(role.id),
"approved": role.approved,
"role": role.role,
"organization": {
"id": str(role.organization.id),
"name": role.organization.name,
Expand Down Expand Up @@ -402,9 +426,12 @@ def update_user_v2(user_id, user_data, current_user):
"state": updated_user.state,
"userType": updated_user.userType,
"lastLoggedIn": user.lastLoggedIn,
"acceptedTermsVersion": user.acceptedTermsVersion,
"roles": [
{
"id": str(role.id),
"approved": role.approved,
"role": role.role,
"organization": {
"id": str(role.organization.id),
"name": role.organization.name,
Expand Down Expand Up @@ -548,7 +575,7 @@ def invite(new_user_data, current_user):

# Always update userType if specified
if new_user_data.userType:
user.userType = new_user_data.userType
user.userType = new_user_data.userType.value
user.save()

# Assign role if an organization is specified
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ class UserRoleSchema(BaseModel):
id: UUID
role: str
approved: bool
user: dict


class TagSchema(BaseModel):
Expand Down
1 change: 1 addition & 0 deletions backend/src/xfd_django/xfd_api/schema_models/user.py
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,7 @@ class UserResponseV2(BaseModel):
lastName: str
fullName: str
email: str
acceptedTermsVersion: Optional[str] = None
lastLoggedIn: Optional[datetime] = None
regionId: Optional[str] = None
state: Optional[str] = None
Expand Down
38 changes: 13 additions & 25 deletions backend/src/xfd_django/xfd_api/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -384,44 +384,32 @@ async def call_accept_terms(
return accept_terms(version_data, current_user)


# GET Current User
@api_router.get("/users/me", tags=["Users"])
async def read_users_me(current_user: User = Depends(get_current_active_user)):
return get_me(current_user)


@api_router.delete("/users/{userId}", tags=["Users"])
async def call_delete_user(current_user, userId: str):
"""
call delete_user()
Args:
userId: UUID of the user to delete.
Returns:
User: The user that was deleted.
"""

return delete_user(current_user, userId)
@api_router.delete(
"/users/{userId}",
response_model=OrganizationSchema.GenericMessageResponseModel,
dependencies=[Depends(get_current_active_user)],
tags=["Users"],
)
async def call_delete_user(
userId: str, current_user: User = Depends(get_current_active_user)
):
"""Delete user."""
return delete_user(userId, current_user)


@api_router.get(
"/users",
response_model=List[UserSchema],
response_model=List[UserResponseV2],
dependencies=[Depends(get_current_active_user)],
tags=["Users"],
)
async def call_get_users(current_user: User = Depends(get_current_active_user)):
"""
Call get_users()
Args:
regionId: Region IDs to filter users by.
Raises:
HTTPException: If the user is not authorized or no users are found.
Returns:
List[User]: A list of users matching the filter criteria.
"""
"""Get all users."""
return get_users(current_user)


Expand Down
8 changes: 4 additions & 4 deletions backend/src/xfd_django/xfd_django/asgi.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
"'self'",
os.getenv("COGNITO_URL"),
os.getenv("BACKEND_DOMAIN"),
"https://cdn.jsdelivr.net/npm/[email protected]/swagger-ui-bundle.js"
"https://cdn.jsdelivr.net/npm/[email protected]/swagger-ui-bundle.js",
],
"frame-src": ["'self'", "https://www.dhs.gov/ntas/"],
"img-src": [
Expand All @@ -43,7 +43,7 @@
os.getenv("FRONTEND_DOMAIN"),
"https://www.ssa.gov",
"https://www.dhs.gov",
"https://fastapi.tiangolo.com/img/favicon.png"
"https://fastapi.tiangolo.com/img/favicon.png",
],
"object-src": ["'none'"],
"script-src": [
Expand All @@ -59,8 +59,8 @@
"style-src": [
"'self'",
"'unsafe-inline'",
"https://cdn.jsdelivr.net/npm/[email protected]/swagger-ui.css"
],
"https://cdn.jsdelivr.net/npm/[email protected]/swagger-ui.css",
],
"frame-ancestors": ["'none'"],
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ export const OrganizationList: React.FC<{

const onSubmit = async (body: Object) => {
try {
const org = await apiPost('/organizations/', {
const org = await apiPost('/organizations', {
body
});
setOrganizations(organizations.concat(org));
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/pages/Users/UserForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,7 @@ export const UserForm: React.FC<UserFormProps> = ({
regionId: values.regionId
};
try {
const user = await apiPost('/users/', {
const user = await apiPost('/users', {
body
});
user.fullName = `${user.firstName} ${user.lastName}`;
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/pages/Users/Users.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ export const Users: React.FC = () => {
const fetchUsers = useCallback(async () => {
setIsLoading(true);
try {
const rows = await apiGet<UserType[]>(`/users/`);
const rows = await apiGet<UserType[]>(`/users`);
rows.forEach((row) => {
row.lastLoggedInString = row.lastLoggedIn
? format(parseISO(row.lastLoggedIn), 'MM-dd-yyyy hh:mm a')
Expand Down

0 comments on commit 29c4bb9

Please sign in to comment.