diff --git a/Screenshot 2024-04-16 233050.png b/Screenshot 2024-04-16 233050.png new file mode 100644 index 000000000..cad58fd0f Binary files /dev/null and b/Screenshot 2024-04-16 233050.png differ diff --git a/Screenshot 2024-04-16 233123.png b/Screenshot 2024-04-16 233123.png new file mode 100644 index 000000000..208c32b2e Binary files /dev/null and b/Screenshot 2024-04-16 233123.png differ diff --git a/Screenshot 2024-04-16 233146.png b/Screenshot 2024-04-16 233146.png new file mode 100644 index 000000000..3350ab9b0 Binary files /dev/null and b/Screenshot 2024-04-16 233146.png differ diff --git a/app/schemas/user_schemas.py b/app/schemas/user_schemas.py index e1d733ae7..f16903754 100644 --- a/app/schemas/user_schemas.py +++ b/app/schemas/user_schemas.py @@ -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.", @@ -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): @@ -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( ..., @@ -147,7 +143,6 @@ class Config: } } -# Define a model for updating user information with optional fields class UserUpdate(BaseModel): email: Optional[EmailStr] = Field( None, @@ -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 @@ -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( ..., @@ -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( ..., @@ -315,7 +309,6 @@ class Config: } } -# Define a model for user login requests class LoginRequest(BaseModel): username: str = Field( ..., @@ -337,7 +330,6 @@ class Config: } } -# Define a model for error responses in case of issues class ErrorResponse(BaseModel): error: str = Field( ..., @@ -357,4 +349,4 @@ class Config: "error": "Invalid username or password.", "details": "The provided username does not exist or the password is incorrect." } - } + } \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index e5e3cec6b..e0cc08f8a 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -32,6 +32,8 @@ services: fastapi: build: . + ports: + - "8000:8000" volumes: - ./:/myapp/ depends_on: diff --git a/readme.md b/readme.md index e92936ed9..01bc7be46 100644 --- a/readme.md +++ b/readme.md @@ -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]() + + ![alt text]() + + ![alt text]() + + 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 diff --git a/tests/test_api/test_users_api.py b/tests/test_api/test_users_api.py index 8990b1366..33715d8d0 100644 --- a/tests/test_api/test_users_api.py +++ b/tests/test_api/test_users_api.py @@ -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 \ No newline at end of file diff --git a/tests/test_schemas/test_user_schemas.py b/tests/test_schemas/test_user_schemas.py index 4911b4f50..ea8e759fe 100644 --- a/tests/test_schemas/test_user_schemas.py +++ b/tests/test_schemas/test_user_schemas.py @@ -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) \ No newline at end of file