Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 21 additions & 9 deletions code_review_graph/flows.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,16 @@
re.compile(r"(override_settings|modify_settings)", re.IGNORECASE),
# SQLAlchemy / event systems
re.compile(r"(event\.)?listens_for", re.IGNORECASE),
# Java Spring
# Java Spring MVC / WebFlux annotation-based
re.compile(r"(Get|Post|Put|Delete|Patch|RequestMapping)Mapping", re.IGNORECASE),
re.compile(r"(Scheduled|EventListener|Bean|Configuration)", re.IGNORECASE),
re.compile(r"RestController", re.IGNORECASE),
# Spring Kafka / Temporal entry points
re.compile(r"KafkaListener", re.IGNORECASE),
re.compile(r"(WorkflowMethod|ActivityMethod)", re.IGNORECASE),
# Spring WebFlux functional routing (RouterFunction / route().GET())
re.compile(r"RouterFunction", re.IGNORECASE),
re.compile(r"HandlerFunction", re.IGNORECASE),
# JS/TS frameworks
re.compile(r"(Component|Injectable|Controller|Module|Guard|Pipe)", re.IGNORECASE),
re.compile(r"(Subscribe|Mutation|Query|Resolver)", re.IGNORECASE),
Expand Down Expand Up @@ -115,17 +122,22 @@
# ---------------------------------------------------------------------------


_WEBFLUX_RETURN_TYPE_RE = re.compile(r"RouterFunction", re.IGNORECASE)


def _has_framework_decorator(node: GraphNode) -> bool:
"""Return True if *node* has a decorator matching a framework pattern."""
decorators = node.extra.get("decorators")
if not decorators:
return False
if isinstance(decorators, str):
decorators = [decorators]
for dec in decorators:
for pat in _FRAMEWORK_DECORATOR_PATTERNS:
if pat.search(dec):
return True
if decorators:
if isinstance(decorators, str):
decorators = [decorators]
for dec in decorators:
for pat in _FRAMEWORK_DECORATOR_PATTERNS:
if pat.search(dec):
return True
# WebFlux functional routing: @Bean methods returning RouterFunction<ServerResponse>
if node.return_type and _WEBFLUX_RETURN_TYPE_RE.search(node.return_type):
return True
return False


Expand Down
27 changes: 27 additions & 0 deletions code_review_graph/graph.py
Original file line number Diff line number Diff line change
Expand Up @@ -350,6 +350,33 @@ def get_edges_by_target(self, qualified_name: str) -> list[GraphEdge]:
).fetchall()
return [self._row_to_edge(r) for r in rows]

def get_edges_by_endpoint_key(self, key: str) -> list[GraphEdge]:
"""Find HANDLES edges whose target is 'http:METHOD:path'.

Endpoint nodes are stored with qualified names like
'file.java::OrderController.GET /orders' while HANDLES edges store
targets as 'http:GET:/orders'. This method bridges that gap so BFS
traversal from an Endpoint node can reach the handler that serves it.
"""
rows = self._conn.execute(
"SELECT * FROM edges WHERE target_qualified = ? AND kind = 'HANDLES'",
(key,),
).fetchall()
return [self._row_to_edge(r) for r in rows]

def get_edges_by_config_key(self, key: str) -> list[GraphEdge]:
"""Find DEPENDS_ON_CONFIG edges whose target is 'config:{key}'.

Config edges store targets as 'config:some.key' while config nodes
are stored with qualified names like 'app.yml::some.key'. This method
bridges that gap so callers can find who reads a given config property.
"""
rows = self._conn.execute(
"SELECT * FROM edges WHERE target_qualified = ? AND kind = 'DEPENDS_ON_CONFIG'",
(f"config:{key}",),
).fetchall()
return [self._row_to_edge(r) for r in rows]

def search_edges_by_target_name(self, name: str, kind: str = "CALLS") -> list[GraphEdge]:
"""Search for edges where target_qualified matches an unqualified name.

Expand Down
1 change: 1 addition & 0 deletions code_review_graph/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,7 @@ def query_graph_tool(
- children_of: Find nodes contained in a file or class
- tests_for: Find tests for the target
- inheritors_of: Find classes inheriting from the target
- consumers_of: Find classes/methods that read a config property
- file_summary: Get all nodes in a file

Args:
Expand Down
Loading
Loading