refactor(phase8 #50 G4a): split governance routes + /api/v2/audit-logs hard-cut#1684
Merged
Conversation
…s hard-cut Phase 8 second batch G4a — per architect canonical D7-2 (msg=94f663f2 §3.2.2) + Option A locked (msg=25a30445), the legacy combined ``governance_router`` (which mounted both API keys and audit logs at ``/api/v1``) is split into two single-purpose routers so #50 G4a (audit-logs) and #51 G4b (apikeys) can migrate to ``/api/v2`` independently. This PR lands the audit-logs side: ``/api/v1/audit-logs*`` is hard-cut to ``/api/v2/audit-logs*`` (no v1 alias). The ``aperag/openapi_spec.py:23`` hidden filter is preserved so audit-logs stay outside the public OpenAPI spec. The apikeys router stays at ``/api/v1/apikeys*`` for now — #51 will flip its mount to ``/api/v2`` in a 1-line follow-up. File structure follows architect-recommended (β) pattern: per-router file, matching the convention of #36 / G1 / G2 / G3 / G4d carved routers. Changes: * Rename ``aperag/domains/governance/api/routes.py`` → ``aperag/domains/governance/api/audit_routes.py``; trim it to only audit routes + needed imports. * New ``aperag/domains/governance/api/apikeys_routes.py`` — apikey routes + needed imports; identical handler signatures (no behavior change). * ``aperag/app.py`` — replace single ``governance_router`` import + mount with two imports and split mounts: ``audit_router → /api/v2`` (this PR's hard-cut), ``apikeys_router → /api/v1`` (preserved; #51 will flip). * New boundary test ``test_no_module_imports_legacy_governance_routes`` — analogous to ``test_no_module_imports_legacy_views_settings`` from #48, forbids re-import of the deleted combined ``aperag.domains.governance.api.routes`` module. Total boundary tests now 23. * ``tests/unit_test/test_v1_ghost_guard.py`` — TRANSITIONAL_V1_PREFIXES shrinks ``/api/v1/audit-logs`` (G4a removed). 11 → 10 prefixes remaining, matching the "clean as you go" pattern established in #1675 H3. FE follow-up: ``web/src/features/audit/{client,server}-api.ts`` v1→v2 caller sync will be a sibling PR by @dongdong (per architect spec) once the backend OpenAPI diff is available. Gates: 25/25 boundary + ghost guard, 698 pass / 29 skip / 1 deselect / 0 fail unit suite, ruff check + format clean.
… to /api/v2 + add regression guard Per @chenyexuan replacement CR (msg=1b0fe11c) — the original G4a backend correctly mounted the audit router at ``/api/v2`` but missed updating the ``HIDDEN_FROM_PUBLIC_PATH_PREFIXES`` filter in ``aperag/openapi_spec.py``, which was still keyed on ``/api/v1/audit-logs``. After the mount switch the filter no longer matched any path, leaking ``/api/v2/audit-logs*`` into the public OpenAPI spec — a violation of D7-2 ``hidden 保持`` and architect canonical msg=25a30445. Fix: - ``aperag/openapi_spec.py:23`` — flip prefix from ``/api/v1/audit-logs`` → ``/api/v2/audit-logs``. One-line. - ``tests/unit_test/test_openapi_spec.py``: - Update the existing synthetic-spec filter test to assert v2 paths (was v1; the assertion would otherwise pass vacuously after the real prefix changed). - Add new ``test_audit_logs_hidden_from_public_openapi_for_live_app`` — runs the real ``aperag.app`` through ``build_full_openapi_spec`` + ``filter_public_openapi`` and asserts no audit-logs path leaks into the public spec. This is the regression guard the architect + reviewer recommended so a future refactor cannot silently re-leak. No other write set in scope. Pure 1-line canonical fix + 1 new test + 1 small test rewrite. The Option A router split, mount changes, boundary gate, ghost guard shrink, and #51 hand-off shape from the original PR head are unchanged.
cd41ebd to
94bfb51
Compare
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.
Summary
Phase 8 second batch G4a — per architect canonical D7-2 (msg=94f663f2 §3.2.2) + Option A locked (msg=25a30445), the legacy combined
governance_router(which mounted both API keys and audit logs at/api/v1) is split into two single-purpose routers so #50 G4a (audit-logs) and #51 G4b (apikeys) can migrate to/api/v2independently.What this PR does
/api/v1/audit-logs*→/api/v2/audit-logs*(no v1 alias).aperag/openapi_spec.py:23hidden filter is preserved so audit-logs stay outside the public OpenAPI spec./api/v1/apikeys*for now — [Features] integrate graph database NebulaGraph as a data source #51 G4b will flip its mount to/api/v2in a 1-line follow-up after this PR lands.File structure choice — Option A (β) per architect spec
Per-router file convention, matching #36 / G1 / G2 / G3 / G4d carved routers:
aperag/domains/governance/api/routes.py(combined) → renamed toaudit_routes.pywith audit handlers onlyaperag/domains/governance/api/apikeys_routes.pywith apikey handlersDeliberately chose (β) over (α) "single file, two router objects" because the per-router file pattern aligns with all existing carved routers and makes future boundary gates (e.g. domain ownership scans) easier.
Changes
aperag/domains/governance/api/audit_routes.pyroutes.py; trim to audit-log handlers + needed importsaperag/domains/governance/api/apikeys_routes.pyaperag/app.pyaudit_router → /api/v2,apikeys_router → /api/v1(G4b will flip)tests/unit_test/test_modularization_boundaries.pytest_no_module_imports_legacy_governance_routes— analogous to #48'stest_no_module_imports_legacy_views_settings. Total 23 boundary tests.tests/unit_test/test_v1_ghost_guard.pyTRANSITIONAL_V1_PREFIXESremoving/api/v1/audit-logs. 11 → 10 prefixes — clean-as-you-go pattern from #1675 H3.Net: 5 files / +114 / -59 LOC.
FE follow-up
web/src/features/audit/{client,server}-api.tsv1→v2 caller sync will be a sibling PR by @dongdong (per architect spec) once the backend OpenAPI diff is available.Coordination notes
openapi_spec.py:23filter preserved) — only the FE admin caller is impacted.aperag/app.pyto flipapikeys_routerfrom/api/v1to/api/v2. Theapikeys_routes.pyfile is already final-shaped from this PR.Test plan
uv run python -m pytest tests/unit_test/test_modularization_boundaries.py tests/unit_test/test_v1_ghost_guard.py -x -q→ 25 passeduv run python -m pytest tests/unit_test -q --deselect 'tests/unit_test/test_web_typed_api_contract.py::test_phase1_fe_complete_identity_auth_admin_audit_adapter_boundary'→ 698 passed / 29 skipped / 1 deselected / 0 faileduv run ruff check aperag/ tests/ config/→ cleanuv run ruff format --check aperag/ tests/→ clean (408 files already formatted)/api/v1/audit-logs*(per [BUG] value too long for type character varying(4096) #42 inventory)CR ask
@chenyexuan replacement blocker-level minimal CR (per Weston-replacement routing) — pure router split + prefix flip + 2 new tests; should be tight.
@符炫炜 canonical drift quick check — Option A (β) alignment.
@dongdong audit-logs FE caller sync sibling PR coordination.
🤖 Generated with Claude Code