fix: interest endpoint validation nits (#188) #194
No reviewers
Labels
No labels
domain:backend
domain:devops
domain:frontend
status:approved
status:in-progress
status:needs-fix
status:qa
type:bug
type:devops
type:feature
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
forgejo_admin/basketball-api!194
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "188-interest-nits"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
Tightens validation on the public interest lead endpoint and adds a tenant_id index to
interest_leads.Changes
src/basketball_api/routes/public.py: Addmin_length=1andmax_lengthconstraints to all Pydantic fields matching DB column limits; addstrip_whitespacefield validator forplayer_nameandage_grade; moveparent_emailfrom INFO to DEBUG log level (PII protection)src/basketball_api/models.py: Addindex=TruetoInterestLead.tenant_idcolumnalembic/versions/026_add_index_on_interest_leads_tenant_id.py: New migration creatingix_interest_leads_tenant_idindextests/test_interest.py: Add 4 new tests: empty string player_name/age_grade returns 422, whitespace-only player_name returns 422, leading/trailing whitespace stripped from player_nameTest Plan
ruff formatandruff checkcleanReview Checklist
Related Notes
basketball-api-- the basketball API projectQA Review -- PR #194
Files Reviewed (4 changed, +98/-9)
alembic/versions/026_add_index_on_interest_leads_tenant_id.py-- Clean migration. Correct revision chain (025 -> 026). Index name follows SQLAlchemy convention. Downgrade correctly drops the index.src/basketball_api/models.py--index=Trueadded toInterestLead.tenant_id. Consistent with how all other models in this file handletenant_id(Parent, Player, Registration, Coach, Team, EmailLog, Product, Order, Outbox all haveindex=True).src/basketball_api/routes/public.pyFieldimported from pydantic; all 5 fields now havemax_lengthmatching DB column sizes (200, 100, 254, 30, 20)min_length=1onplayer_nameandage_grade-- rejects empty strings at the Pydantic layerstrip_whitespacevalidator correctly handles whitespace-only input (passesmin_lengthcheck since len > 0, then strip + empty check raises ValueError)parent_email) moved from INFO log to DEBUG -- INFO line still logs player_name/age_grade/program for operational visibilitytests/test_interest.py-- 4 new tests covering: empty player_name (422), empty age_grade (422), whitespace-only player_name (422), whitespace-stripped player_name (201 + DB assertion). All well-structured with docstrings.Nits
None.
Blockers
None.
VERDICT: APPROVE
All 5 QA nits from #188 are addressed. 586/586 tests pass. Migration 025 is untouched. No regressions.
PR #194 Review
DOMAIN REVIEW
Tech stack: Python / FastAPI / SQLAlchemy / Alembic / Pydantic
Nit-by-nit verification against issue #188 scope (5 nits):
min_length=1onplayer_nameandage_gradeField(min_length=1, ...)on both fields inInterestLeadRequestmax_lengthmatching DB columnsString(N)counterparts: player_name=200, age_grade=100, parent_email=254, parent_phone=30, program=20player_nameandage_gradestrip_whitespacevalidator on both fields with post-strip empty checkparent_emailmoved fromlogger.infotologger.debug; INFO line now only logs player_name, age_grade, programindex=TrueonInterestLead.tenant_id+ Alembic migrationix_interest_leads_tenant_id, correctly chained from 025Pydantic/FastAPI:
Fieldimport added correctly.field_validatordecorator order is correct (@field_validatorthen@classmethod).strip_whitespacevalidator runs after Pydantic'smin_lengthcheck for empty strings, but the post-strip blank check handles whitespace-only strings that would passmin_length=1. Defense in depth is correct.SQLAlchemy/Alembic:
downgrade()properly drops the index withtable_namekwarg.index=Trueon the model column matches the migration. No model/migration drift.Logging/PII:
parent_emailfully removed from INFO log. Only appears at DEBUG level.player_nameremains at INFO -- this is acceptable as it is not contact PII (no email, phone, or address).BLOCKERS
None.
All 5 nits are addressed. New functionality (validation constraints, whitespace stripping) has test coverage. No unvalidated input, no secrets, no DRY violations.
NITS
Missing whitespace tests for
age_grade: Thestrip_whitespacevalidator covers bothplayer_nameandage_grade, but tests only exercise theplayer_namepath. Addingtest_whitespace_only_age_grade_returns_422andtest_age_grade_whitespace_strippedwould provide symmetric coverage. Non-blocking because the validator is shared code (single@field_validatoron both fields), so theplayer_nametests do exercise the full code path.parent_phoneaccepts empty strings:parent_phone: str | None = Field(default=None, max_length=30)will accept""and store it. Consider addingmin_length=1or coercing""toNone. Out of scope for this PR -- flagging for future consideration.SOP COMPLIANCE
188-interest-nitsreferences #188)basketball-api)PROCESS OBSERVATIONS
VERDICT: APPROVED