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
Binary file added Screenshot 2024-04-16 233050.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added Screenshot 2024-04-16 233123.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added Screenshot 2024-04-16 233146.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
32 changes: 12 additions & 20 deletions app/schemas/user_schemas.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,20 +35,18 @@
Data Serialization and Deserialization: Pydantic handles the secure conversion of data between JSON and Python objects, mitigating risks associated with manual serialization and deserialization.
Overall, the use of Pydantic models in this file contributes to building a secure, validated, and well-documented API for user-related operations in a FastAPI application. The combination of type annotations, regex-based validators, and inheritance promotes code reusability, maintainability, and adherence to security best practices.
"""
# Import required libraries and modules
from datetime import datetime, timezone # Provides classes for manipulating dates and times in both simple and complex ways.
from urllib.parse import urlparse # Functions for breaking down and reconstructing URLs.
from pydantic import BaseModel, EmailStr, Field, HttpUrl, validator # Pydantic is used for data validation and settings management using Python type annotations.
from typing import List, Optional # Standard library typing module, used for constructing complex type hints.
from app.schemas.link_schema import Link # Custom module, likely provides a schema for links (part of HATEOAS).
from app.schemas.pagination_schema import EnhancedPagination # Custom pagination schema supporting enriched functionality.
import re # Provides regular expression matching operations.
import uuid # Provides immutable UUID objects and functions for generating new UUIDs.
from datetime import datetime, timezone
from urllib.parse import urlparse
from pydantic import BaseModel, EmailStr, Field, HttpUrl, validator
from typing import List, Optional
from app.schemas.link_schema import Link
from app.schemas.pagination_schema import EnhancedPagination
import re
import uuid

# Define a base user model with common attributes
class UserBase(BaseModel):
username: str = Field(
..., # Ellipsis is used to indicate that the field is required.
...,
min_length=3,
max_length=50,
description="The unique username of the user. Must be 3-50 characters long. Only letters, numbers, underscores, and hyphens are allowed.",
Expand Down Expand Up @@ -77,7 +75,6 @@ class UserBase(BaseModel):
example="https://example.com/profile_pictures/john_doe.jpg"
)

# Validators are used to validate the data
@validator('username')
def validate_username(cls, v):
if not re.match(r"^[a-zA-Z0-9_-]+$", v):
Expand Down Expand Up @@ -111,7 +108,6 @@ class Config:
}
}

# Define a model for creating new user accounts with additional attributes like password
class UserCreate(UserBase):
password: str = Field(
...,
Expand Down Expand Up @@ -147,7 +143,6 @@ class Config:
}
}

# Define a model for updating user information with optional fields
class UserUpdate(BaseModel):
email: Optional[EmailStr] = Field(
None,
Expand All @@ -171,11 +166,12 @@ class UserUpdate(BaseModel):
description="An updated URL to the user's profile picture.",
example="https://example.com/profile_pictures/john_doe_updated.jpg"
)

@validator('profile_picture_url', pre=True, always=True)
def validate_profile_picture_url(cls, v):
if v is not None:
parsed_url = urlparse(str(v)) # Convert the URL object to a string before parsing
# Ensure the validation logic only runs when parsed_url is defined
if not re.search(r"\.(jpg|jpeg|png)$", parsed_url.path):
raise ValueError("Profile picture URL must point to a valid image file (JPEG, PNG).")
return v
Expand All @@ -191,7 +187,6 @@ class Config:
}
}

# Define a model for the user response, which includes fields populated during queries
class UserResponse(UserBase):
id: str = Field(
...,
Expand Down Expand Up @@ -253,7 +248,6 @@ class Config:
}
}

# Define a model for paginated list of user responses, including pagination details
class UserListResponse(BaseModel):
items: List[UserResponse] = Field(
...,
Expand Down Expand Up @@ -315,7 +309,6 @@ class Config:
}
}

# Define a model for user login requests
class LoginRequest(BaseModel):
username: str = Field(
...,
Expand All @@ -337,7 +330,6 @@ class Config:
}
}

# Define a model for error responses in case of issues
class ErrorResponse(BaseModel):
error: str = Field(
...,
Expand All @@ -357,4 +349,4 @@ class Config:
"error": "Invalid username or password.",
"details": "The provided username does not exist or the password is incorrect."
}
}
}
2 changes: 2 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ services:

fastapi:
build: .
ports:
- "8000:8000"
volumes:
- ./:/myapp/
depends_on:
Expand Down
21 changes: 18 additions & 3 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,9 +60,24 @@ To complete this assignment, submit the following:
- All issues should be merged into the main branch, following the Git workflow and best practices.

2. **Updated README**: Replace the existing README with:
- Links to the closed issues, providing easy access to your work.
- Link to project image deployed to Dockerhub.
- A 2-3 paragraph reflection on what you learned from this assignment, focusing on both technical skills and collaborative processes. Reflect on the challenges you faced, the solutions you implemented, and the insights you gained. This reflection helps solidify your learning and provides valuable feedback for improving the assignment in the future.
1. https://github.com/EDS435/event_manager/issues/14
2. https://github.com/EDS435/event_manager/issues/12
3. https://github.com/EDS435/event_manager/issues/10
4. https://github.com/EDS435/event_manager/issues/8
5. https://github.com/EDS435/event_manager/issues/6
6. https://github.com/EDS435/event_manager/issues/4 (demo test #1)
7. https://github.com/EDS435/event_manager/issues/1 (demo test #2)

![alt text](<Screenshot 2024-04-16 233050.png>)

![alt text](<Screenshot 2024-04-16 233123.png>)

![alt text](<Screenshot 2024-04-16 233146.png>)

Generally speaking this assignment had taught me two things, trying to find the root of the code issues and how to leverage AI when it came to testing code. First, I want to talk about finding the roots of my issues, the tests I had created primarily focused on testing whether the data the user had placed within the system were invalid. My first few tests were apparently already present within the code so I had decided to shift into deleting data that was unauthorized and updating old data that was no longer considered valid. These tests were rather simple but hard to implement as I was receiving assertion errors whenever I tried to run the test but eventually I had found a way to make sure my tests run easier.

Which brings me to my second point, for all my skepticism AI (specifically ChatGpt) was incredibly helpful in managing the changes I needed to make in my code. ChatGpt essentially had cleaned up most of the errors that I had created and made sure I wasn't facing assertion errors, and if there were any errors present I had to just give it a more detailed explanation of the error plus the code in order for it to make the proper adjustments. While I did have my doubts what would have been an all nighter or two was cut down to two days of back and forth and managing my own mistakes.


## Grading Rubric

Expand Down
5 changes: 5 additions & 0 deletions tests/test_api/test_users_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -116,3 +116,8 @@ async def test_delete_user_does_not_exist(async_client, token):
delete_response = await async_client.delete(f"/users/{non_existent_user_id}", headers=headers)
assert delete_response.status_code == 404

async def test_update_user_invalid_data(async_client, user, token):
updated_data = {"email": "invalid_email"} # Invalid email format
headers = {"Authorization": f"Bearer {token}"}
response = await async_client.put(f"/users/{user.id}", json=updated_data, headers=headers)
assert response.status_code == 422
2 changes: 1 addition & 1 deletion tests/test_schemas/test_user_schemas.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,4 +85,4 @@ def test_user_base_username_valid(username, user_base_data):
def test_user_base_username_invalid(username, user_base_data):
user_base_data["username"] = username
with pytest.raises(ValidationError):
UserBase(**user_base_data)
UserBase(**user_base_data)