Listing Household Invitations A Comprehensive Technical Guide

by StackCamp Team 62 views

Hey guys! Today, we're diving deep into the process of listing household invitations. If you've ever wondered how to display pending invitations in your budgeting app or any similar system, you're in the right place. We'll break down the requirements, API specifications, technical implementation, and everything else you need to know. Let's get started!

Understanding the User Story

First off, let's talk about the user story. As a user, the goal is simple: I want to see my pending household invitations so that I can decide which household to join. This is crucial for any application where users need to manage and accept invitations to shared spaces or groups. Think of it like getting invites to different family plans or shared budgeting groups within an app. The easier it is for users to see and manage these invitations, the better their experience will be.

Key Acceptance Criteria

To make sure we nail this feature, there are several acceptance criteria we need to meet:

  1. Shows all pending invitations for the user: This seems straightforward, but it’s the foundation of the entire feature. Users need a clear view of every invitation waiting for their response.
  2. Excludes expired invitations: Nobody wants to see old, irrelevant invitations. Expired invites should be automatically filtered out to keep things clean and current.
  3. Excludes already processed invitations: Once an invitation has been accepted or declined, it should disappear from the pending list. This prevents confusion and clutter.
  4. Shows household and inviter information: Users need context. Displaying details about the household (like its name and member count) and the person who sent the invitation (name, email) helps users make informed decisions.
  5. Sorted by creation date (newest first): This ensures that the most recent invitations are always at the top, which is generally the most intuitive way to present this information.

API Specification: The Blueprint

Now, let's talk code! The API specification is our blueprint for how the system will communicate. For this feature, we're using a GET request to fetch the invitations.

Request

GET /api/users/me/invitations

This endpoint is pretty self-explanatory. It’s a GET request to the /api/users/me/invitations URL. The me part is a common convention for accessing the currently authenticated user’s data. To access this endpoint, the user needs to be authenticated. This is done using an Authorization header.

Headers

Authorization: Bearer {token}

The Authorization header uses the Bearer scheme, which is a standard way to include a token (usually a JWT - JSON Web Token) for authentication. This token verifies the user’s identity.

Success Response (200)

When the request is successful, the server should respond with a 200 OK status code and a JSON payload. This payload will contain an array of invitation objects.

{  
  "invitations": [  
    {  
      "id": "uuid",  
      "token": "string",  
      "household": {  
        "id": "uuid",  
        "name": "string",  
        "memberCount": "integer"  
      },  
      "invitedBy": {  
        "id": "uuid",  
        "firstName": "string",  
        "lastName": "string",  
        "email": "string"  
      },  
      "createdAt": "datetime",  
      "expiresAt": "datetime"  
    }  
  ]  
}

Let's break down this JSON:

  • invitations: This is the main array containing the list of invitations.
  • id: A unique identifier (UUID) for the invitation.
  • token: A string token that might be used for accepting the invitation.
  • household: An object containing information about the household.
    • id: The household’s UUID.
    • name: The name of the household.
    • memberCount: The current number of members in the household.
  • invitedBy: An object with details about the user who sent the invitation.
    • id: The inviter’s UUID.
    • firstName: The inviter’s first name.
    • lastName: The inviter’s last name.
    • email: The inviter’s email address.
  • createdAt: A datetime string indicating when the invitation was created.
  • expiresAt: A datetime string specifying when the invitation expires.

This detailed response structure ensures that the client has all the necessary information to display the invitations effectively.

Technical Implementation: Getting Our Hands Dirty

Okay, now for the fun part – how do we actually build this? The technical implementation involves several layers, from the database to the API endpoint.

1. Repository Implementation

We’ll start with the HouseholdInvitationRepository. This is the layer that interacts directly with the database. We need to add a method to find pending invitations for a specific user.

  • Add to HouseholdInvitationRepository: findPendingByInvitedUserId()
  • Include filtering for non-expired invitations

This method should:

  • Accept a user ID as input.
  • Query the database for invitations where the invitedUserId matches the input ID.
  • Filter out any invitations that have already expired (i.e., expiresAt is in the past).
  • Return a list of pending HouseholdInvitation entities.

2. DTOs and Extensions

Next, we need to define Data Transfer Objects (DTOs). DTOs are simple objects used to transfer data between layers of the application. This helps to decouple the database entities from the API response.

  • Create InvitationListItemDto for response
  • Include nested DTOs for household and inviter

We’ll create an InvitationListItemDto that contains:

  • id: The invitation’s UUID.
  • token: The invitation token.
  • household: A nested HouseholdDto with id, name, and memberCount.
  • invitedBy: A nested InviterDto with id, firstName, lastName, and email.
  • createdAt: The invitation creation datetime.
  • expiresAt: The invitation expiration datetime.

We’ll also need extension methods (or mappers) to convert the database entities into these DTOs. This keeps our service layer clean and focused.

3. Service Layer Logic

The service layer is where the business logic lives. It orchestrates the data retrieval and transformation.

  • Get pending, non-expired invitations
  • Include household member count
  • Sort by createdAt DESC

The service layer should:

  1. Call the findPendingByInvitedUserId() method in the HouseholdInvitationRepository to get the pending invitations.
  2. For each invitation, fetch the member count of the household (this might involve another repository call).
  3. Map the HouseholdInvitation entities to InvitationListItemDto objects, including the nested DTOs for household and inviter.
  4. Sort the list of DTOs by createdAt in descending order (newest first).
  5. Return the sorted list of InvitationListItemDto objects.

4. Controller Implementation

The controller is the entry point for API requests. It receives the request, calls the service layer, and returns the response.

  • Add GET /users/me/invitations endpoint to UserController

In the UserController, we’ll add a method to handle the GET /users/me/invitations request. This method should:

  1. Authenticate the user (using the Authorization header).
  2. Call the service layer to get the list of pending invitations.
  3. Return the list of InvitationListItemDto objects in a 200 OK response.

5. Integration Tests

Testing is crucial to ensure our feature works as expected. Integration tests verify that different parts of the system work together correctly.

  • Test listing pending invitations
  • Test exclusion of expired invitations
  • Test exclusion of processed invitations
  • Test empty list scenario

We’ll write tests to cover the following scenarios:

  1. Listing pending invitations: Verify that the API returns a list of pending invitations for a user.
  2. Exclusion of expired invitations: Ensure that expired invitations are not included in the response.
  3. Exclusion of processed invitations: Confirm that invitations that have been accepted or declined are not included.
  4. Empty list scenario: Test the case where a user has no pending invitations and ensure the API returns an empty list.

Definition of Done: Tying Up Loose Ends

Before we can mark this feature as complete, we need to meet the Definition of Done. This is a checklist of all the things that need to be done.

  • All acceptance criteria met
  • Integration tests passing
  • API documentation updated
  • Query performance optimized
  • Code reviewed and approved

Let’s break it down:

  1. All acceptance criteria met: We need to make sure that the feature satisfies all the requirements outlined in the user story and acceptance criteria.
  2. Integration tests passing: All the integration tests must pass, indicating that the feature works correctly in different scenarios.
  3. API documentation updated: We need to update the API documentation (e.g., using Swagger or similar tools) to reflect the new endpoint and its response structure. This helps other developers understand how to use the API.
  4. Query performance optimized: We should analyze the database queries generated by the feature and ensure they are efficient. This might involve adding indexes or optimizing the query logic.
  5. Code reviewed and approved: The code should be reviewed by other developers to ensure it’s clean, maintainable, and follows best practices.

Conclusion: Wrapping Things Up

So, there you have it! Listing household invitations is a multi-faceted feature that requires careful planning and implementation. By following these steps, you can ensure that your users have a smooth and intuitive experience managing their invitations. Remember, the key is to focus on the user story, define clear acceptance criteria, and implement the solution in a structured and testable way.

If you have any questions or want to dive deeper into any of these topics, feel free to ask! Happy coding, guys!