Skip to content

feat: persist created_via on shortened urls - #268

Merged
Zingzy merged 4 commits into
mainfrom
feat/created-via
Jul 22, 2026
Merged

feat: persist created_via on shortened urls#268
Zingzy merged 4 commits into
mainfrom
feat/created-via

Conversation

@Zingzy

@Zingzy Zingzy commented Jul 22, 2026

Copy link
Copy Markdown
Member

Stacked on #267 (chain: #265 -> #266 -> #267 -> this).

What

  • UrlV2Doc gains a created_via field storing the validated X-Spoo-Client slug (dashboard, landing, snap, raycast, cli, bot) at creation time. Null for untagged creations.
  • The header parser moves from the logging middleware to shared/client_tag.py so the middleware and the shorten route share one implementation.
  • POST /api/v1/shorten parses the header and passes the slug into UrlService.create as an explicit parameter.

Why

Log-based attribution expires with log retention. Persisting provenance on the document makes "where did this link come from" queryable forever, supports per-client counts straight from Mongo, and leaves room for a created-via hint in the dashboard later.

Security and performance

The stored value is never the raw header: it must match ^[a-z0-9_-]{1,32}$ or it is dropped to null, so arbitrary header content cannot reach the database. No extra queries; one nullable field on insert. The field is not exposed on any public response DTO.

Notes

  • Legacy routes are untouched; legacy clients never send the header.
  • Existing documents read as null, no migration needed.

Store the validated X-Spoo-Client slug on UrlV2Doc at creation so link
provenance survives log retention. Parser moved to shared/client_tag.py
for reuse by the middleware and the shorten route. Legacy routes are
untouched since legacy clients never send the header.
Copilot AI review requested due to automatic review settings July 22, 2026 13:28

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@coderabbitai

coderabbitai Bot commented Jul 22, 2026

Copy link
Copy Markdown

Warning

Review limit reached

@Zingzy, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 30 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 0ebdde79-79da-43ff-aa42-111cb2755cc9

📥 Commits

Reviewing files that changed from the base of the PR and between bc871e8 and d7207fa.

📒 Files selected for processing (7)
  • middleware/logging.py
  • routes/api_v1/shorten.py
  • schemas/models/url.py
  • services/url_service.py
  • shared/client_tag.py
  • tests/unit/services/test_url_service.py
  • tests/unit/shared/test_client_tag.py
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/created-via

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@Zingzy Zingzy left a comment

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mergeable. One decision to make consciously before this lands, inline below. Everything else verified clean.

Verified:

  • shorten_v1 is the only v2 creation path (the emoji router only serves the emoji set, bulk has no create), so the single plumbing point covers all tagged creations; legacy stays untouched as stated.
  • created_via appears in no response DTO and no route serialization.
  • Old documents and anything cached before deploy decode fine: nullable field, None default, nothing reads it yet.
  • shared/client_tag.py fits the shared/ convention exactly (pure function, no I/O, no framework import), and moving the parser out of the middleware instead of importing middleware code from a route was the right direction.

Chain note: merge bottom-up and let CI re-run after each retarget; the stacked PRs only ran check_formatting because the test workflows filter on the base branch. Full unit suite passes at this tip locally (2001).

Comment thread routes/api_v1/shorten.py Outdated
Zingzy added 2 commits July 22, 2026 20:12
Unknown but well-formed slugs still appear in logs, where they surface
new clients without a deploy; the document field is restricted to the
known first-party set so junk header values never become permanent
history. Review follow-up on the created_via PR.
Base automatically changed from feat/request-attribution to main July 22, 2026 15:09
@Zingzy
Zingzy merged commit ec280b5 into main Jul 22, 2026
12 checks passed
@Zingzy
Zingzy deleted the feat/created-via branch July 22, 2026 15:14
@Zingzy Zingzy self-assigned this Jul 22, 2026
@Zingzy Zingzy added the backend Changes related to Backand/API label Jul 22, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

backend Changes related to Backand/API

Projects

Status: ✔️ Done

Development

Successfully merging this pull request may close these issues.

2 participants