feat: accept tenant slug in team endpoints #88
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!88
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "87-feat-accept-tenant-slug-in-team-endpoint"
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
Team endpoints now accept tenant slugs (e.g.
westside-kings-queens) in addition to integer IDs for thetenant_idparameter. This resolves the contract mismatch where the westside-app frontend sends slugs but the API expected integers, causing 422 errors.Changes
src/basketball_api/routes/teams.py: Added_resolve_tenant_idhelper and expanded_get_tenant_or_404to resolve by numeric ID or slug. Changed alltenant_idquery params frominttostr. ChangedTeamCreate.tenant_idfrominttoint | str. Response models still returntenant_id: int.tests/test_teams.py: AddedTestSlugBasedTenantLookupclass with 6 new tests covering list by slug, create with slug, overview by slug, get team by slug, invalid slug 404, and integer backward compat.Test Plan
Review Checklist
Related
Self-Review: LGTM
Files reviewed:
src/basketball_api/routes/teams.py,tests/test_teams.pyCorrectness
tenant_idthrough the enhanced_get_tenant_or_404helper and usetenant.id(int) for all downstream DB queries. No stale references to the raw stringtenant_idremain in query filters._resolve_tenant_idcorrectly triesint()first so numeric query strings still resolve by ID (backward compat).TeamCreate.tenant_id: int | straccepts both types in the request body. Response models remainint.Test Coverage
TestSlugBasedTenantLookup: list, create, overview, detail, invalid slug 404, integer backward compat.No Issues Found
PR #88 Backend Review
DOMAIN REVIEW
PEP compliance: Clean. Type hints are correct throughout:
int | strunion types (PEP 604), proper docstrings with PEP 257 formatting on both_resolve_tenant_idand_get_tenant_or_404. The"""..."""docstrings use reStructuredText-style backticks consistently.Security (OWASP): No injection risk. The
_resolve_tenant_idhelper uses Python'sint()cast, not string interpolation into SQL. SQLAlchemy parameterized queries are used for both the ID and slug lookup paths. Tenant isolation is preserved -- all endpoints resolve theTenantobject first, then passtenant.id(the integer PK) downstream to_get_team_or_404. No mass assignment risk sinceTeamCreateis the gatekeeper and response models always emittenant_id: int.Database patterns: Correct.
Tenant.slugis alreadyunique=True, index=True(confirmed inmodels.pyline 78), so the slug lookup path in_get_tenant_or_404hits an index. No N+1 risk introduced. Thejoinedloadpatterns onTeam.coachandTeam.playersare preserved unchanged. No new queries added -- just the tenant resolution path changed from one query shape to a branching two-query shape (only one executes per call).Migrations: Not needed. No schema changes -- this is purely application-layer logic. The
Tenant.slugcolumn already exists. Confirmed no Alembic version files in the diff.Tests: Strong coverage. Six new tests in
TestSlugBasedTenantLookup:test_list_teams_by_slug-- list endpoint with slugtest_create_team_with_slug-- POST body with slug, verifies response returns integertenant_idtest_overview_by_slug-- overview endpoint with slugtest_get_team_by_slug-- detail endpoint with slugtest_invalid_slug_returns_404-- negative casetest_integer_tenant_id_still_works-- backward compatibilityMissing coverage (non-blocking): no slug-based tests for PATCH, DELETE, assign-players, or unassign-player endpoints. The code changes are mechanical (all follow the same pattern), so the risk is low, but completeness would be better.
API design: Good approach. Query params changed from
inttostrwhich is transparent to HTTP callers (query params are strings on the wire anyway). Response models correctly still emittenant_id: int-- the canonical form. Backward compatibility is preserved since numeric strings parse back to integers.BLOCKERS
None.
NITS
Numeric-slug ambiguity: If a tenant ever has an all-numeric slug (e.g., slug
"123"),_resolve_tenant_id("123")would resolve it as integer ID 123, not as slug"123". This is an unlikely edge case given the current slugs (westside-kings-queens), but worth documenting in the docstring or adding a guard (e.g., try ID first, fall back to slug if no tenant found by ID). Non-blocking for current usage.Duplicate
_get_tenant_or_404in subscriptions.py: The subscriptions routes (/home/ldraney/basketball-api/src/basketball_api/routes/subscriptions.py, line 95) have their own_get_tenant_or_404that still only acceptsint. If the frontend will also call subscription endpoints with slugs, a follow-up issue should be filed to DRY this into a shared helper and apply the same slug resolution. Non-blocking for this PR's scope (teams only per issue #87).Test coverage gap for mutating slug endpoints: The PATCH (
update_team), DELETE (delete_team), POST assign-players, and DELETE unassign-player endpoints all received theint -> strtenant_id change but lack dedicated slug-path tests. One test per write endpoint would close this gap. Non-blocking since the pattern is identical.SOP COMPLIANCE
87-feat-accept-tenant-slug-in-team-endpointreferences issue #87Closes #87present in PR bodyplan-2026-03-08-tryout-prep) -- minor gap.pysource files changedfeat: accept tenant slug in team endpoints)PROCESS OBSERVATIONS
_resolve_tenant_id+_get_tenant_or_404pattern for slug-or-ID resolution should be extracted to a shared utility (e.g.,basketball_api/utils/tenant.py) before other route modules (subscriptions, admin) need the same treatment. Currently duplicated between teams.py and subscriptions.py (subscriptions has the old int-only version).VERDICT: APPROVED
Clean, well-scoped change. The slug resolution logic is correct, backward-compatible, and properly tested for the read paths. The
Tenant.slugcolumn is indexed, so no performance concern. Response models correctly return integertenant_id. No blockers. The nits (numeric-slug edge case, DRY opportunity, write-path test coverage) are all non-blocking and appropriate for follow-up work.