Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 38 additions & 17 deletions backend/routes/officers.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,11 @@
from backend.database.utils.transform import transform_dates_in_dict
from backend.auth.jwt import min_role_required
from backend.mixpanel.mix import track_to_mp
from backend.schemas import (validate_request, ordered_jsonify,
add_pagination_wrapper)
from backend.schemas import (
validate_request,
ordered_jsonify,
add_pagination_wrapper,
)
from backend.database.models.user import UserRole, User
from backend.database.models.officer import Officer
from backend.routes.search import fetch_details, build_officer_result
Expand Down Expand Up @@ -351,24 +354,47 @@
@validate_request(CreateOfficer)
def create_officer():
"""Create an officer profile.

Requires a valid source_uid. The officer will be linked to the
specified data source via an UPDATED_BY citation.
"""
logger = logging.getLogger("create_officer")
body: CreateOfficer = request.validated_body
jwt_decoded = get_jwt()
current_user = User.get(jwt_decoded["sub"])

# try:
officer = Officer.from_dict(body.model_dump())
# except Exception as e:
# abort(400, description=str(e))
source = Source.nodes.get_or_none(uid=body.source_uid)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

This is great. The only other thing is that the current user needs to be a member of the Source organization and have a member role of at least Publisher.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

members = RelationshipFrom(

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Nice to hear. I see you solved that by adding line 148, correct? Let me know if I can still develop this issue.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

No, I haven't changed anything. Just highlighting the section of code I'm talking about. Sorry I wasn't clear.

You'll need to add some logic that checks to see if the user is a member of the Source indicated by the source_uid. Something like:

    if source.members.is_connected(current_user):
        member_rel = source.members.relationship(current_user)
        if member_rel.may_publish()
          # Prodeed with Creation
        else:
        abort(403, description="No permission.")
    else:
        abort(403, description="No permission.")

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

I am looking at this! I made new changes, and I just need to run tests.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Let me know if I can help

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Hey Darrel, I believe the last change solves the check for membership. I think this is ready for merge.

if source is None:
abort(
422,
description=(
f"Source with UID '{body.source_uid}' not found. "
"A valid data source is required to create an officer."
),
)

if not source.members.is_connected(current_user):
abort(
403, description="User does not have permission to use this source"
)

member_rel = source.members.relationship(current_user)
if member_rel is None or not member_rel.may_publish():
abort(
403, description="User does not have permission to use this source"
)

officer_data = body.dict()
officer_data.pop("source_uid", None)
officer = Officer.from_dict(officer_data)

officer.citations.connect(source, {"timestamp": datetime.now()})

logger.info(f"Officer {officer.uid} created by User {current_user.uid}")
track_to_mp(
request,
"create_officer",
{
"officer_id": officer.uid
},
{"officer_id": officer.uid},
)
return officer.to_json()

Expand Down Expand Up @@ -527,8 +553,7 @@ def get_all_officers():
@min_role_required(UserRole.CONTRIBUTOR)
@validate_request(UpdateOfficer)
def update_officer(officer_uid: str):
"""Update an officer profile.
"""
"""Update an officer profile."""
body: UpdateOfficer = request.validated_body
o = Officer.nodes.get_or_none(uid=officer_uid)
if o is None:
Expand All @@ -543,9 +568,7 @@ def update_officer(officer_uid: str):
track_to_mp(
request,
"update_officer",
{
"officer_id": o.uid
},
{"officer_id": o.uid},
)
return o.to_json()

Expand All @@ -567,9 +590,7 @@ def delete_officer(officer_uid: str):
track_to_mp(
request,
"delete_officer",
{
"officer_id": uid
},
{"officer_id": uid},
)
return {"message": "Officer deleted successfully"}
except Exception as e:
Expand Down
1 change: 1 addition & 0 deletions backend/routes/tmp/pydantic/officers.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ class CreateOfficer(BaseOfficer, BaseModel):
gender: Optional[str] = Field(None, description="The gender of the officer")
date_of_birth: Optional[str] = Field(None, description="The date of birth of the officer")
state_ids: Optional[List[StateId]] = Field(None, description="The state ids of the officer")
source_uid: str = Field(..., description="The UID of the data source for this officer record")


class UpdateOfficer(BaseOfficer, BaseModel):
Expand Down
Loading
Loading