fix: build full_name from parts in chat messages endpoint

- SELECT prefix, given_name, middle_name, family_name, suffix alongside full_name
- Apply db._build_full_name() fallback when full_name is NULL
- Document the pattern in AGENTS.md to prevent future regressions
This commit is contained in:
2026-09-17 19:41:38 +02:00
parent 113527a4a0
commit 43ce0bcfe1
2 changed files with 22 additions and 2 deletions
+13
View File
@@ -145,6 +145,19 @@ See `.env.example` for full list. Key variables:
4. Update endpoint table in `SPEC.md` and `README.md` 4. Update endpoint table in `SPEC.md` and `README.md`
5. Test with: `curl -H "Remote-User: <user>" http://127.0.0.1:8000/<path>` 5. Test with: `curl -H "Remote-User: <user>" http://127.0.0.1:8000/<path>`
### Querying contact names from the DB
**IMPORTANT:** The `full_name` column in `contacts` can be NULL — it is
NOT guaranteed to be set. Always SELECT `prefix, given_name, middle_name,
family_name, suffix` alongside `full_name` and apply the fallback:
```python
if not row.get("full_name"):
row["full_name"] = db._build_full_name(row)
```
This is what `_row_to_contact_out()` does for API responses. Any code
that needs a contact's display name (e.g. proxying to external APIs)
must follow the same pattern. See `get_contact_messages()` in
`src/api/main.py` for a reference implementation.
### Adding a new contact field ### Adding a new contact field
1. Add column to `contacts` table in `sql/schema.sql` 1. Add column to `contacts` table in `sql/schema.sql`
2. Update `src/vcard_parser.py` to extract the field 2. Update `src/vcard_parser.py` to extract the field
+9 -2
View File
@@ -229,14 +229,21 @@ def get_contact_messages(
id_clause = "AND id = %s" if where_clause else "WHERE id = %s" id_clause = "AND id = %s" if where_clause else "WHERE id = %s"
with conn.cursor() as cur: with conn.cursor() as cur:
cur.execute( cur.execute(
f"SELECT full_name FROM contacts {where_clause} {id_clause}", f"""SELECT full_name, prefix, given_name, middle_name, family_name, suffix
FROM contacts {where_clause} {id_clause}""",
params + [contact_id], params + [contact_id],
) )
row = cur.fetchone() row = cur.fetchone()
if not row or not row.get("full_name"): if not row:
return JSONResponse(status_code=404, content={"detail": "Kontakt nicht gefunden"}) return JSONResponse(status_code=404, content={"detail": "Kontakt nicht gefunden"})
if not row.get("full_name"):
row["full_name"] = db._build_full_name(row)
if not row.get("full_name"):
return JSONResponse(status_code=404, content={"detail": "Kontakt hat keinen Namen"})
try: try:
resp = requests.get( resp = requests.get(
f"{Config.CHATAPI_URL.rstrip('/')}/conversation", f"{Config.CHATAPI_URL.rstrip('/')}/conversation",