Skip to content

[U1] User Service and REST APIs#1

Open
1jeanpaul1 wants to merge 21 commits intodevfrom
U1_JP
Open

[U1] User Service and REST APIs#1
1jeanpaul1 wants to merge 21 commits intodevfrom
U1_JP

Conversation

@1jeanpaul1
Copy link
Copy Markdown
Collaborator

@1jeanpaul1 1jeanpaul1 commented Dec 11, 2025

User Model and APIs

API docs: https://fridgefinder.github.io/CFM_User/

@1jeanpaul1 1jeanpaul1 changed the title U1 User Service and REST APIs [U1] User Service and REST APIs Dec 11, 2025
@@ -0,0 +1,41 @@
name: Deploy API Docs to GitHub Pages
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Comment thread docker-compose.yml
@@ -0,0 +1,22 @@
version: "3.9"
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

can ignore - this is for local testing

@@ -0,0 +1,41 @@
{
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

can ignore - for local testing

@@ -0,0 +1,29 @@
#!/usr/bin/env python3
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

can ignore - for local testing

return datetime.now(timezone.utc).strftime('%Y-%m-%dT%H:%M:%S.%f')[:-3] + 'Z'


class UserType(str, Enum):
Copy link
Copy Markdown
Collaborator Author

@1jeanpaul1 1jeanpaul1 Dec 11, 2025

Choose a reason for hiding this comment

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

UserTypes

Really only need Neighbor and Volunteer right now, but in the future ORGANIZER or HOST might have some special privileges

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Mobile App would have to change check from isVolunteer to user.userType == 'Volunteer'

email: Optional[str] = None
phoneNumber: Optional[str] = None
zipcode: Optional[str] = None
fcmToken: Optional[str] = None #TODO: Consider making this a set to allow for multiple devices to receive notifications
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

when signing out we need to set this to None

we should also consider allowing for multiple devices to receive notifications

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

I think fcmToken should be a list of tokens to allow for multiple devices

}


def error_response(status_code: int, code: ErrorCode, message: str, field: Optional[str] = None,
Copy link
Copy Markdown
Collaborator Author

@1jeanpaul1 1jeanpaul1 Dec 11, 2025

Choose a reason for hiding this comment

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

API errors are returned in this format:

Status Code: 400-500
Headers: X-Request-Id - to query Cloudwatch

{
    "error": {
        "code": "INTERNAL_ERROR_CODE",
        "message": "MESSAGE"
    }
}

example:

{
    "error": {
        "code": "FORBIDDEN",
        "message": "User can only update their own user data"
    }
}

Authorizers:
FirebaseAuthorizer:
JwtConfiguration:
issuer: !Sub "https://securetoken.google.com/${FirebaseProjectId}"
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Api Gateway Validates JWT tokens

@1jeanpaul1
Copy link
Copy Markdown
Collaborator Author

1jeanpaul1 commented Dec 11, 2025

User Service API Endpoints

1. Create User

POST /v1/users

  • Auth: Required (Firebase JWT)
  • Description: Create a new user account. Users are automatically assigned the Neighbor type.

Request Body:

{
  "userId": "firebase-uid-here",
  "username": "mariamekaba",
  "email": "mariame@example.com",
  "phoneNumber": "+1234567890",
  "zipcode": "12345",
  "fcmToken": "fcm-token-here",
  "settings": {
    "pushNotificationEnabled": true,
    "emailNotificationEnabled": true,
    "geofenceEnabled": false
  },
  "userType": "Neighbor"
}

Success Response (201 Created):

{
  "user": {
    "userId": "firebase-uid-here",
    ...,
    "createdAt": "2024-03-20T14:22:00.000Z",
    "lastUpdated": "2024-03-20T14:22:00.000Z",
    "lastLoginAt": "2024-03-20T14:22:00.000Z"
  }
}

2. Get User

GET /v1/users/{userId}

  • Auth: Required (Firebase JWT)
  • Description: Retrieve user profile. Users can only access their own profile.
  • Success Response (200 OK):
{
  "user": {
    "userId": "firebase-uid-here",
    ...
  }
}

3. Update User

PATCH /v1/users/{userId}

  • Auth: Required (Firebase JWT)
  • Description: Partially update user profile. Only provided fields will be updated.

Request Body (example - update email and settings):

{
  "email": "newemail@example.com",
  "settings": {
    "pushNotificationEnabled": false
  }
}

Success Response (200 OK):

{
  "user": {
    "userId": "firebase-uid-here",
    "email": "newemail@example.com",
    "settings": {
      "pushNotificationEnabled": false,
      "emailNotificationEnabled": true,
      "geofenceEnabled": false
    },
    "lastUpdated": "2024-03-20T15:30:00.000Z",
    ...,
  }
}

4. Delete User

DELETE /v1/users/{userId}

  • Auth: Required (Firebase JWT)
  • Description: Permanently delete user account. Users can only delete their own account.

Success Response (204 No Content):

(Empty response body)

5. Check Username Availability

GET /v1/users/check-username/{username}

  • Auth: Not required (Public endpoint)
  • Description: Check if a username is available (not taken by another user).

Success Response (200 OK) - Available:

{
  "available": true
}

Success Response (200 OK) - Taken:

{
  "available": false
}

API errors are returned in this format:

Status Code: 400-500
Headers: X-Request-Id - to query Cloudwatch

{
    "error": {
        "code": "INTERNAL_ERROR_CODE",
        "message": "MESSAGE"
    }
}

example:

{
    "error": {
        "code": "FORBIDDEN",
        "message": "User can only update their own user data"
    }
}

API Base URLs

  • Development: https://users-api-dev.communityfridgefinder.com

Authentication

All endpoints (except username check) require Firebase JWT in the Authorization header:

Authorization: Bearer <your-firebase-jwt-token>

Documentation

📖 Interactive API Docs: https://fridgefinder.github.io/CFM_User/

- '*'

# DynamoDB Table for Users
UsersTable:
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Hash/PrimaryKey - userId
Indexed - username

parameter_overrides = [
"Environment=dev",
"DeploymentTarget=aws",
"FirebaseProjectId=YOUR_STAGING_FIREBASE_PROJECT_ID",
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

TOOD: create firebase project on fridgefinder google account for: Dev and Prod

# Fields that can be updated (excluding userType - handled above)
allowed_fields = {
'email', 'phoneNumber', 'username', 'points',
'zipcode', 'fcmToken', 'lastLoginAt', 'settings'
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

lastLoginAt - I think there should be an api that updates this instead of the frontend sending the lastLoginAt field

GEOFENCE_ENABLED = "geofenceEnabled"


class User(BaseModel):
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Reviewer: User Model, confirm fields and structure

Copy link
Copy Markdown

@mansoorsiddiqui mansoorsiddiqui left a comment

Choose a reason for hiding this comment

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

Solid work overall, looks clean. Most of my comments are around a few edge cases and making sure we're aligned on the cross-service contract with the notification PR.

Cross-PR concerns:

  • EventBridge contract gap: The notification PR subscribes to "User Deleted" events from "user-service", but this PR never publishes that event. Notification cleanup won't happen on account deletion.
  • Error code naming: This PR uses USER_NOT_FOUND, USER_ALREADY_EXISTS while the notification PR uses NOT_FOUND, ALREADY_EXISTS. The Flutter app will need to parse errors from both — should we align on a convention?
  • Error response format: Both PRs use {"error": {"code": "...", "message": "..."}} which is good, but the OpenAPI spec here documents it differently. Let's make sure the specs match the implementations.

Comment thread user-service/functions/user_service/service.py
Comment thread user-service/functions/user_service/repository.py
Comment thread user-service/functions/user_service/repository.py Outdated
Comment thread user-service/functions/user_service/models.py Outdated
Uses Pydantic's model_dump with enum values as strings
Excludes None values to save space
"""
return self.model_dump(mode='json', exclude_none=True)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Quick sanity check — to_dict() uses exclude_none=True, and then update_user does a full put_item which replaces the entire DynamoDB item. So if a user clears a field (sets it to None), the old value in DynamoDB would get dropped because put_item replaces everything. Is that the intended behavior? Just want to make sure we're aligned on that.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Yes that was the intended behavior

Not sure why the API client would set any fields from not-None to None, but if it is set to None: it is recommended to not have the field at all in the db

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

If there are fields we don't want to go from VALUE -> NONE, we could block that as it might be a mistake? But left it open for now

Comment thread user-service/functions/username_check/username_check_handler.py
push:
branches:
- main
- U1_JP #TODO: remove this branch after testing
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

U1_JP branch is still in the trigger list with a #TODO: remove this branch after testing. Don't forget to pull this out before merging to main.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

yes will remove before merging in

Comment thread user-service/tests/unit/test_handler.py Outdated
Comment thread user-service/functions/user_service/utils.py Outdated
Comment thread user-service/samconfig_local.toml
@1jeanpaul1
Copy link
Copy Markdown
Collaborator Author

Solid work overall, looks clean. Most of my comments are around a few edge cases and making sure we're aligned on the cross-service contract with the notification PR.

Cross-PR concerns:

  • EventBridge contract gap: The notification PR subscribes to "User Deleted" events from "user-service", but this PR never publishes that event. Notification cleanup won't happen on account deletion.
  • Error code naming: This PR uses USER_NOT_FOUND, USER_ALREADY_EXISTS while the notification PR uses NOT_FOUND, ALREADY_EXISTS. The Flutter app will need to parse errors from both — should we align on a convention?
  • Error response format: Both PRs use {"error": {"code": "...", "message": "..."}} which is good, but the OpenAPI spec here documents it differently. Let's make sure the specs match the implementations.
  1. Forgot to push sorry, will push User delete event
  2. lets go with ITEM_NOT_FOUND, and ITEM_ALREADY for both
  3. Will push fix for openAPI thank you!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants