Skip to content

refactor(api): Centralize project id-or-slug discrimination#117461

Closed
JoshFerge wants to merge 1 commit into
feat/project-id-or-slug-foundationfrom
claude/magical-ptolemy-gp4vmy
Closed

refactor(api): Centralize project id-or-slug discrimination#117461
JoshFerge wants to merge 1 commit into
feat/project-id-or-slug-foundationfrom
claude/magical-ptolemy-gp4vmy

Conversation

@JoshFerge

Copy link
Copy Markdown
Member

Draft / experiment stacked on #117445 (base = feat/project-id-or-slug-foundation, so this diff is just the refactor). Explores the reuse opportunity raised in review: the "is this value an ID or a slug?" decision was being re-derived in several spots.

What

Extracts a single coerce_id_or_slug() helper in src/sentry/api/helpers/projects.py as the one source of truth for the isdecimal() + -1 sentinel branching. Both ProjectIdOrSlugField.to_internal_value and parse_id_or_slug_params now call it instead of each re-implementing the rule.

Why

Before this, the same discrimination lived in three places:

  • IdOrSlugLookup.as_sql — the generic slug__id_or_slug DB lookup — str(...).isdecimal()
  • ProjectIdOrSlugField.to_internal_valuedata.isdecimal() or data == str(ALL_ACCESS_PROJECT_ID)
  • parse_id_or_slug_params — the same check again

This collapses the two new copies into one. The generic IdOrSlugLookup is intentionally left alone: it runs at the SQL layer across many models (Team, Org, SentryApp, DocIntegration, …) and deliberately has no notion of the project-specific -1 / $all sentinels, so coupling it to project constants would be wrong. The new helper's docstring records that relationship.

Behavior

Unchanged. The field still layers strict slug-format validation (MIXED_SLUG_REGEX) on top of the shared rule, and the parser still silently skips empty values without validating slug format. Validated equivalent to the pre-refactor implementation across every case in the existing tests (tests/sentry/api/helpers/test_projects.py, tests/sentry/api/serializers/rest_framework/test_project.py) plus a fuzz battery of id / slug / sentinel / edge inputs.

Out of scope (possible follow-ups)

  • _validate_slug in ProjectField still routes through the field and then rejects ints; could be expressed more directly, but left as-is to keep behavior identical.
  • The two unrelated inline str(...).isdecimal() organization id-or-slug checks in bases/organization.py are a separate concept and not touched here.

Verification note: ruff is clean; the full pytest suite + mypy could not be executed in the authoring sandbox (no devenv, sentry not importable), so equivalence was proven with a standalone harness mirroring both the old and new implementations.

https://claude.ai/code/session_014ZqyxEfBjS5hjG62L8tACF


Generated by Claude Code

Extract the "is this value an ID or a slug?" decision into a single
coerce_id_or_slug() helper so ProjectIdOrSlugField and
parse_id_or_slug_params share one rule instead of each re-deriving the
isdecimal()/-1-sentinel branching. Adds a docstring noting the
relationship to the generic IdOrSlugLookup (slug__id_or_slug) DB lookup.

Behavior is unchanged: the field still layers strict slug validation on
top of the shared rule, and the parser still skips empty values.

https://claude.ai/code/session_014ZqyxEfBjS5hjG62L8tACF
@github-actions github-actions Bot added the Scope: Backend Automatically applied to PRs that change backend components label Jun 11, 2026
@JoshFerge JoshFerge closed this Jun 11, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Scope: Backend Automatically applied to PRs that change backend components

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants