fix(graph): address blocker + high findings from PR #37 review#104
fix(graph): address blocker + high findings from PR #37 review#104damienriehl wants to merge 4 commits intoentity-graph-endpointfrom
Conversation
The endpoint had no auth dependency and constructed a fresh OntologyService
per request via get_ontology_service — that service never loads any graph,
so calling the endpoint would raise ValueError("Graph for project ... not
loaded") and 500 the request. The frontend uses the project-scoped route
at /projects/{id}/ontology/classes/graph instead, which is correctly auth'd
via verify_project_access and goes through _ensure_ontology_loaded.
Removes the dead route + its companion test class.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Schema previously typed node_type/edge_type as `str`, but the BFS only ever emits one of: focus / root / secondary_root / class / individual / property / external for nodes, and subClassOf / equivalentClass / disjointWith / seeAlso for edges. Frontend already has GraphNodeType / GraphEdgeType union types in lib/graph/types.ts; the API contract should match. Adds GraphNodeType and GraphEdgeType Literal aliases. Pydantic enforces them at serialization, OpenAPI generates proper enums for clients, and a typo in the service code (e.g., "focuss") fails type-check instead of shipping silently. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The original _get_see_also_targets and _get_see_also_referrers were inner closures inside build_entity_graph (a 330-line function with 9 nested closures). They could only be exercised through the full BFS path, requiring fixture graphs and assertions on downstream BFS state. Extracts both as top-level functions in `entity_graph_helpers.py` so they can be unit-tested directly. Adds 12 targeted tests covering FOLIO-style restriction encoding (someValuesFrom / allValuesFrom / hasValue), direct rdfs:seeAlso triples, dedup across encodings, named-superclass exclusion, restriction-with-unrelated-property exclusion, and class-only filtering for referrers. build_entity_graph still wraps them as thin closures (so the BFS code reads the same) but the heavy lifting is now reusable + independently tested. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Bundle of related changes to build_entity_graph: - B2: thread project.label_preferences through. _get_label now passes them to select_preferred_label so multilingual projects see graph labels in their preferred language. _get_definition derives a preferred language from the same preferences and prefers literals matching that tag for skos:definition / rdfs:comment, falling back to any literal. - H2: drop the unreachable `isinstance(include_see_also, bool)` runtime check — FastAPI coerces query params before they reach this code, and type hints handle direct service callers. - H3: replace the Python-2-style `total_discovered = [0]` closure workaround with `nonlocal total_discovered`. - H4: replace inner closures with calls to top-level get_see_also_targets / get_see_also_referrers from entity_graph_helpers. - node_type / edge_type closures now return GraphNodeType / GraphEdgeType to match the tightened schema. Drops the test that asserted the removed isinstance check. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
Summary
Follow-up to #37 addressing the 2 blocker and 4 high-severity findings from peer review. Targets
entity-graph-endpointso fixes land before #37 merges todev— single migration-free, single PR for review.Recommended merge order: this PR (#XX) → #37 →
dev.Findings addressed
/api/v1/ontologies/{id}/classes/graphroute inclasses.pywas unauthenticated AND non-functional — noverify_project_accessdependency, and a freshOntologyService()per request never loads any graph, so calling it raisesValueError("Graph for project ... not loaded")and 500s. The frontend uses the project route (which IS auth'd + ontology-loaded), so this was dead code.fdc51b1project.label_preferenceswas silently ignored. Every other ontology endpoint threads them through; here a project configured for Spanish SKOS labels would get Englishrdfs:labels in graph but Spanish labels in tree/detail/search — visibly inconsistent UX.6f32696node_type: strandedge_type: strin schemas are too loose. Replaced withGraphNodeType/GraphEdgeTypeLiteralunions matching the frontend'slib/graph/types.ts. Now Pydantic enforces, OpenAPI generates proper enums for clients, and a typo in the service code fails type-check.92ca7e8if not isinstance(include_see_also, bool): raise ValueErrorwas unreachable through FastAPI (which coerces query params before the service is called). Java-ism dropped; we trust type hints.6f32696total_discovered = [0]...total_discovered[0] += 1was a Python 2 closure workaround. Replaced withnonlocal total_discovered— Python 3-idiomatic.6f32696_get_see_also_targetsand_get_see_also_referrerswere inner closures insidebuild_entity_graph(a 330-line function with 9 nested closures). Could only be exercised through full BFS path. Extracted to module scope atontokit/services/entity_graph_helpers.pywith 12 new unit tests covering FOLIO restriction encoding, dedup, named-superclass exclusion, class-only filtering, etc.2e6f739Detailed rationale per fix
B1 — Delete dead unauthed route
Verified by grep that no client (frontend or other backend code) referenced the deleted route. Companion test class
TestClassesGraphRouteintest_graph_routes.pyremoved.B2 — Thread
label_preferencesthroughIn
routes/projects.py:H1 — Tighten schemas to
LiteralFrontend mirror is
lib/graph/types.ts— values stay in sync.H4 — Extract seeAlso helpers
New
ontokit/services/entity_graph_helpers.py:These were 60+ lines of nested closures inside
build_entity_graph. Now testable in isolation withtests/unit/test_entity_graph_helpers.pycovering:rdfs:seeAlsotriplessomeValuesFrom/allValuesFrom/hasValue)subClassOf SomeClassdoesn't get treated as a restriction)build_entity_graphkeeps thin wrapper closures so the BFS body reads the same.Verification
mypyclean on all 5 changed source filesruff checkclean onontokit/andtests/ruff formatappliedTest plan
uv run pytest tests/unit/test_entity_graph_helpers.py -v→ 12 cases pass (helpers tested in isolation)uv run pytest tests/unit/test_entity_graph.py tests/unit/test_graph_routes.py→ 55 cases pass (BFS + route)GET /api/v1/ontologies/{id}/classes/graphis no longer in the route table (grep'able)label_preferences=["rdfs:label@es", ...], hit the entity-graph endpoint and confirm node labels are Spanish (matching tree/detail/search)/docs) showsnode_typeandedge_typeas enum types, not arbitrary stringsFindings deferred to Discussion threads
The original review also flagged 7 medium and 9 low items. They will be filed as a tracked Discussion on this repo for follow-up.
🤖 Generated with Claude Code