refactor(api): assemble api_router in the v1 package __init__ - #585
Merged
Conversation
abdulrafey1
force-pushed
the
refey/refactor/lang-06-api-init
branch
from
August 10, 2026 09:26
7e65dc2 to
2f62930
Compare
hamza-56
approved these changes
Aug 10, 2026
abdulrafey1
force-pushed
the
refey/refactor/lang-06-api-init
branch
from
August 11, 2026 23:23
2f62930 to
70033d6
Compare
abdulrafey1
force-pushed
the
refey/refactor/lang-06-api-init
branch
from
August 11, 2026 23:31
70033d6 to
120aa80
Compare
The frontend picker and the static-UI translation layer both need this list; serving it keeps the allowlist defined once instead of duplicated client-side. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
PATCH /user/me validates the tag against the supported-language allowlist in the request model, so an unsupported value is a 422 before any domain logic runs. GET returns the raw column, null included, so the frontend can tell a user who never chose from one who chose English. PATCH loads the row it is about to write with session.get rather than mutating the injected principal. get_current_user resolves the same request-scoped session, so in production that is an identity-map hit; the guard exists because a principal that is not attached to the request session would otherwise make commit() a silent no-op while the endpoint still answered 200. Such a principal is now a 404. updated_at carries a default_factory but no onupdate, so the write bumps it explicitly. The test double for get_current_user takes the same session dependency the real one does, so a value one request writes is visible to a later request on the same client. That rewrote _override_current_user — the shared helper the three pre-existing is_admin tests also call — from a detached make_transient snapshot to a live select(User) on the request session; those three tests are themselves left unmodified and still pass. Also covers the PATCH auth gate (401 unauthenticated), that a PATCH only ever writes to the caller's own row, that a principal with no row is refused rather than handed a 200 for a write that never happened, and that a successful PATCH advances updated_at. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
openapi.json is a gitignored build artifact and is not committed; only the generated TypeScript client changes. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Records when an api/v1 module is a single file and when it is a package with routes.py and schemas.py, now that user/ and language/ join permissions/ and whitelist/ in the second shape while five modules remain single files. Covers what belongs in each file, why __init__.py re-exporting router is a deliberate exception to the avoid-re-exports rule, that domain exception -> HTTP mappings register there, and that route paths come from the prefix in api.py so converting a file to a package never moves a URL. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Covers the supported list, how to read and set a user's choice, what null means, and what happens to a stored tag once its language leaves the allowlist. The guide is explicit that generated content does not consume the preference yet, so nothing here promises behaviour the platform does not have. The DEFAULT_LANGUAGE reference entry ships with the setting itself, so this change is guide-only. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Moves the router assembly out of sparkth/api/v1/api.py into the package's own __init__.py and deletes the module, so importing the package yields the router rather than requiring a nested api submodule whose name repeated its parent's. sparkth/main.py imports it as `from sparkth.api.v1 import api_router`. Prefixes and tags are unchanged, so the OpenAPI document and the generated frontend client are byte-identical and no URL moves. The endpoint-module convention doc is updated to point at the new home. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
abdulrafey1
force-pushed
the
refey/refactor/lang-06-api-init
branch
from
August 12, 2026 06:37
120aa80 to
cb884d1
Compare
#582 and #583 landed on main as squash commits while this branch carried their original commits, so git saw the same files added on both sides and flagged six conflicts where there was one real question: which copy is current. main's is. Its copies include the review fixes made after this branch was cut — the languages endpoint dropped its auth gate, and the User schema moved back to the root sparkth/schemas.py — so every file this branch does not own is resolved to main's version, and the branch keeps only what it exists to change: the api_router assembly moving from sparkth/api/v1/api.py into the package __init__. Two of those needed more than a side chosen: - api.py was modified in main (#582 registered the language router) and deleted here. Kept deleted; the nine include_router calls in __init__.py were verified identical to main's, prefixes and tags included, so the deletion drops no route. - architectural_patterns.md is a union: main's amended shared-models rule, which now names User, plus this branch's rename of api.py to the package __init__. The re-export bullet is reworded because the rename leaves two different __init__.py roles in one section. auth.py auto-merged without a conflict and silently kept this branch's pre-fix import, dropping main's. Restored from main. Verified by diffing the whole tree against main: no file outside the four this branch owns differs from it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Moves the
api_routerassembly fromsparkth/api/v1/api.pyinto the package's ownsparkth/api/v1/__init__.pyand deletes the module, so importing the package yields the router directly.Changes
api_routerinsparkth/api/v1/__init__.pyand deletesparkth/api/v1/api.pysparkth/main.pyasfrom sparkth.api.v1 import api_routerHow to Test
uv run pytest -q— 1726 passed, 3 skipped.make test.frontend.api— no drift, so the OpenAPI document is byte-identical.uv run python -c "from sparkth.api.v1 import api_router; print(len(api_router.routes))"prints9.uv run python -c "import sparkth.api.v1.user.schemas"— importing a submodule directly still works, confirming the package__init__importing every endpoint module introduces no cycle.make backend.up.devand hit any endpoint, e.g.curl http://localhost:7727/api/v1/languages.Notes
No migration, no env var, no dependency change. Prefixes and OpenAPI tags are untouched, so no URL moves and no client regeneration is needed —
generated.tsis unchanged and the drift check proves it.api.pysat inside theapi/v1package repeating its parent's name while the package__init__was empty. Every endpoint module in the package already exposes arouter; this puts the thing that aggregates them where callers naturally look for it.#582 and #583 merged after this branch was cut, so
mainwas merged in. They landed as squash commits against this branch's copies of the same work, which git surfaced as six conflicts; each file this branch does not own is resolved tomain's version, including the review fixes those PRs picked up.api.py's deletion is kept — its nineinclude_routercalls were verified identical to the ones in__init__.py— and the convention section inarchitectural_patterns.mdis a union ofmain's amended shared-models rule and this branch's rename.This description was written with the assistance of an LLM (Claude).