From 73e26a50144b39a44b230e968223d8c8cd9947e6 Mon Sep 17 00:00:00 2001 From: 1sakshm Date: Sun, 1 Nov 2026 08:01:52 +0530 Subject: [PATCH 1/2] Reject null name, profession and role when updating a profile These columns are NOT NULL, so an explicit null in PATCH /profiles/{id} failed on commit with a 500. It is now a 422 VALIDATION_ERROR and the profile is left unchanged. Fixes #755 --- app/api/schemas/profiles.py | 11 ++++++++++- tests/test_v1_profiles.py | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+), 1 deletion(-) diff --git a/app/api/schemas/profiles.py b/app/api/schemas/profiles.py index 170b320..7af87c2 100644 --- a/app/api/schemas/profiles.py +++ b/app/api/schemas/profiles.py @@ -1,4 +1,4 @@ -from pydantic import BaseModel, Field, ConfigDict +from pydantic import BaseModel, Field, ConfigDict, field_validator from uuid import UUID from datetime import datetime @@ -17,6 +17,15 @@ class ProfileUpdate(ProfileBase): profession: str | None = None role: str | None = None + # Leaving a field out keeps it, but these columns are NOT NULL, so an + # explicit null is rejected here instead of failing on commit. + @field_validator("name", "profession", "role") + @classmethod + def reject_null(cls, value): + if value is None: + raise ValueError("cannot be null") + return value + class ProfileResponse(ProfileBase): profile_id: UUID created_at: datetime diff --git a/tests/test_v1_profiles.py b/tests/test_v1_profiles.py index 252ffda..5085118 100644 --- a/tests/test_v1_profiles.py +++ b/tests/test_v1_profiles.py @@ -1,3 +1,4 @@ +import pytest from fastapi.testclient import TestClient from sqlmodel import Session, select from uuid import UUID @@ -46,6 +47,37 @@ def test_profile_crud(client: TestClient, db: Session): assert db_profile.role == "Battalion Chief" + +@pytest.mark.parametrize("field", ["name", "profession", "role"]) +def test_profile_patch_rejects_null_required_field(client: TestClient, field): + """An explicit null for a NOT NULL column is a 422, not a 500, and changes nothing.""" + payload = {"name": "Jane Doe", "profession": "Firefighter", "role": "Captain"} + profile_id = client.post("/api/v1/profiles/", json=payload).json()["profile_id"] + + response = client.patch(f"/api/v1/profiles/{profile_id}", json={field: None}) + assert response.status_code == 422 + body = response.json() + assert body["error_code"] == "VALIDATION_ERROR" + assert body["validation_errors"][0]["field"] == field + + assert client.get(f"/api/v1/profiles/{profile_id}").json()[field] == payload[field] + + +def test_profile_patch_allows_null_optional_fields(client: TestClient): + """description and custom_fields are nullable, and omitted fields are left alone.""" + payload = {"name": "Jane Doe", "profession": "Firefighter", "role": "Captain", "description": "Station 42"} + profile_id = client.post("/api/v1/profiles/", json=payload).json()["profile_id"] + + response = client.patch( + f"/api/v1/profiles/{profile_id}", json={"description": None, "custom_fields": None} + ) + assert response.status_code == 200 + data = response.json() + assert data["description"] is None + assert data["custom_fields"] is None + assert data["name"] == "Jane Doe" + + def test_append_profile_context(): """Unit test to ensure the LLM prompt is correctly formatted with profile data.""" base_prompt = "Extract the incident details." From ee631c7af36ff6661fa4ab31fe74c47026a93686 Mon Sep 17 00:00:00 2001 From: 1sakshm Date: Wed, 4 Nov 2026 05:59:33 +0530 Subject: [PATCH 2/2] Mark name, profession and role as non-nullable in UpdateProfileRequest --- contracts/schemas/profile.yaml | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/contracts/schemas/profile.yaml b/contracts/schemas/profile.yaml index c9acdfd..b991a54 100644 --- a/contracts/schemas/profile.yaml +++ b/contracts/schemas/profile.yaml @@ -29,17 +29,15 @@ CreateProfileRequest: UpdateProfileRequest: type: object - description: Only the keys sent are changed. + description: Only the keys sent are changed. name, profession and role + can be left out but not set to null. properties: name: type: string - nullable: true profession: type: string - nullable: true role: type: string - nullable: true description: type: string nullable: true