graph-dependency-analyzer 0.2.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (86) hide show
  1. graph_dependency_analyzer-0.2.0.dist-info/METADATA +158 -0
  2. graph_dependency_analyzer-0.2.0.dist-info/RECORD +86 -0
  3. graph_dependency_analyzer-0.2.0.dist-info/WHEEL +5 -0
  4. graph_dependency_analyzer-0.2.0.dist-info/entry_points.txt +2 -0
  5. graph_dependency_analyzer-0.2.0.dist-info/top_level.txt +1 -0
  6. src/__init__.py +0 -0
  7. src/application/__init__.py +0 -0
  8. src/application/dtos/__init__.py +0 -0
  9. src/application/dtos/affected_component.py +59 -0
  10. src/application/dtos/batch_processing_result.py +17 -0
  11. src/application/dtos/impact_analysis_response.py +56 -0
  12. src/application/dtos/impact_query_request.py +38 -0
  13. src/application/dtos/index_batch_request.py +53 -0
  14. src/application/dtos/index_batch_response.py +72 -0
  15. src/application/dtos/index_result.py +20 -0
  16. src/application/dtos/repository_index_result.py +79 -0
  17. src/application/exceptions.py +41 -0
  18. src/application/services/__init__.py +0 -0
  19. src/application/services/file_scanner_service.py +258 -0
  20. src/application/services/progress_tracker.py +132 -0
  21. src/application/use_cases/__init__.py +0 -0
  22. src/application/use_cases/clear_all_data_use_case.py +180 -0
  23. src/application/use_cases/index_batch_use_case.py +153 -0
  24. src/application/use_cases/index_repository_use_case.py +683 -0
  25. src/application/use_cases/indexing_orchestrator.py +181 -0
  26. src/application/use_cases/list_repositories_use_case.py +84 -0
  27. src/cli.py +512 -0
  28. src/config/__init__.py +5 -0
  29. src/config/container.py +211 -0
  30. src/config/logging_config.py +123 -0
  31. src/config/settings.py +165 -0
  32. src/domain/__init__.py +0 -0
  33. src/domain/entities/__init__.py +20 -0
  34. src/domain/entities/ast_node.py +32 -0
  35. src/domain/entities/code_chunk.py +45 -0
  36. src/domain/entities/code_file.py +44 -0
  37. src/domain/entities/dependency_graph.py +110 -0
  38. src/domain/entities/repository.py +46 -0
  39. src/domain/exceptions.py +36 -0
  40. src/domain/repositories/__init__.py +22 -0
  41. src/domain/repositories/i_code_parser.py +50 -0
  42. src/domain/repositories/i_dependency_extractor.py +35 -0
  43. src/domain/repositories/i_embedding_provider.py +31 -0
  44. src/domain/repositories/i_graph_repository.py +78 -0
  45. src/domain/repositories/i_vector_repository.py +55 -0
  46. src/domain/services/__init__.py +8 -0
  47. src/domain/services/dependency_extractor.py +962 -0
  48. src/domain/services/semantic_chunk_builder.py +719 -0
  49. src/domain/value_objects/__init__.py +12 -0
  50. src/domain/value_objects/chunk_type.py +24 -0
  51. src/domain/value_objects/dependency_type.py +22 -0
  52. src/domain/value_objects/node_type.py +22 -0
  53. src/domain/value_objects/qualified_name.py +78 -0
  54. src/infrastructure/__init__.py +0 -0
  55. src/infrastructure/exceptions.py +41 -0
  56. src/infrastructure/external_apis/__init__.py +0 -0
  57. src/infrastructure/external_apis/ollama_embedding_provider.py +134 -0
  58. src/infrastructure/external_apis/openai_embedding_provider.py +231 -0
  59. src/infrastructure/parsing/__init__.py +20 -0
  60. src/infrastructure/parsing/config_file_parser.py +347 -0
  61. src/infrastructure/parsing/gradle_dependency_parser.py +159 -0
  62. src/infrastructure/parsing/groovy_parser.py +198 -0
  63. src/infrastructure/parsing/maven_dependency_parser.py +147 -0
  64. src/infrastructure/parsing/microfrontend_config_parser.py +242 -0
  65. src/infrastructure/parsing/npm_package_dependency_parser.py +255 -0
  66. src/infrastructure/parsing/python_parser.py +211 -0
  67. src/infrastructure/parsing/sql_schema_parser.py +658 -0
  68. src/infrastructure/parsing/tree_sitter_java_parser.py +434 -0
  69. src/infrastructure/parsing/tree_sitter_typescript_parser.py +600 -0
  70. src/infrastructure/persistence/__init__.py +0 -0
  71. src/infrastructure/persistence/graph_export_repository.py +443 -0
  72. src/infrastructure/persistence/neo4j_dependency_repository.py +657 -0
  73. src/infrastructure/persistence/qdrant_vector_repository.py +357 -0
  74. src/infrastructure/persistence/repository_relationship_repository.py +1312 -0
  75. src/presentation/__init__.py +0 -0
  76. src/presentation/api/__init__.py +0 -0
  77. src/presentation/api/rest/__init__.py +0 -0
  78. src/presentation/api/rest/exception_handlers.py +174 -0
  79. src/presentation/api/rest/main.py +80 -0
  80. src/presentation/api/rest/routers/__init__.py +5 -0
  81. src/presentation/api/rest/routers/admin.py +103 -0
  82. src/presentation/api/rest/routers/analysis.py +980 -0
  83. src/presentation/api/rest/routers/export.py +442 -0
  84. src/presentation/api/rest/routers/health.py +117 -0
  85. src/presentation/api/rest/routers/indexing.py +148 -0
  86. src/presentation/api/rest/routers/relationships.py +1089 -0
@@ -0,0 +1,110 @@
1
+ """DependencyGraph entities - represent dependency graph structures."""
2
+ from dataclasses import dataclass, field
3
+
4
+
5
+ @dataclass
6
+ class DependencyNode:
7
+ """
8
+ DependencyNode representing a node in the dependency graph.
9
+
10
+ Can represent: Repository, Class, Method, Library, API, Database.
11
+
12
+ Attributes:
13
+ id: Unique identifier for the node
14
+ node_type: Type of node (REPOSITORY, CLASS, METHOD, LIBRARY, API, DATABASE)
15
+ properties: Node properties (name, path, version, etc.)
16
+ """
17
+ id: str
18
+ node_type: str
19
+ properties: dict
20
+
21
+
22
+ @dataclass
23
+ class DependencyEdge:
24
+ """
25
+ DependencyEdge representing an edge in the dependency graph.
26
+
27
+ Can represent: CONTAINS, HAS_METHOD, CALLS, IMPORTS, USES_LIBRARY,
28
+ CALLS_API, CONNECTS_TO, DEPENDS_ON.
29
+
30
+ Attributes:
31
+ source: Source node ID
32
+ target: Target node ID
33
+ edge_type: Type of relationship
34
+ properties: Edge properties (line number, reason, etc.)
35
+ """
36
+ source: str
37
+ target: str
38
+ edge_type: str
39
+ properties: dict = field(default_factory=dict)
40
+
41
+
42
+ @dataclass
43
+ class DependencyGraph:
44
+ """
45
+ DependencyGraph containing nodes and edges.
46
+
47
+ Represents the complete dependency structure of a repository,
48
+ including internal dependencies (code-to-code) and external
49
+ dependencies (APIs, databases, libraries).
50
+
51
+ Attributes:
52
+ nodes: List of DependencyNode entities
53
+ edges: List of DependencyEdge entities
54
+
55
+ Business Rules:
56
+ - Edges always reference nodes that exist in the graph
57
+ - No duplicate nodes (unique by ID)
58
+ """
59
+ nodes: list[DependencyNode] = field(default_factory=list)
60
+ edges: list[DependencyEdge] = field(default_factory=list)
61
+
62
+ def add_node(self, node: DependencyNode) -> None:
63
+ """
64
+ Add a node to the graph.
65
+
66
+ Args:
67
+ node: DependencyNode to add
68
+ """
69
+ self.nodes.append(node)
70
+
71
+ def add_edge(self, edge: DependencyEdge) -> None:
72
+ """
73
+ Add an edge to the graph.
74
+
75
+ Args:
76
+ edge: DependencyEdge to add
77
+ """
78
+ self.edges.append(edge)
79
+
80
+ def get_dependents(self, target: str) -> list[DependencyNode]:
81
+ """
82
+ Get all nodes that depend on the target node.
83
+
84
+ Returns nodes where there's an edge pointing TO the target.
85
+ (Who depends on this target?)
86
+
87
+ Args:
88
+ target: Node ID to find dependents for
89
+
90
+ Returns:
91
+ List of DependencyNode that have edges pointing to target
92
+ """
93
+ dependent_ids = {edge.source for edge in self.edges if edge.target == target}
94
+ return [node for node in self.nodes if node.id in dependent_ids]
95
+
96
+ def get_dependencies(self, source: str) -> list[DependencyNode]:
97
+ """
98
+ Get all dependencies of the source node.
99
+
100
+ Returns nodes where there's an edge FROM source pointing to them.
101
+ (What does this source depend on?)
102
+
103
+ Args:
104
+ source: Node ID to find dependencies for
105
+
106
+ Returns:
107
+ List of DependencyNode that source depends on
108
+ """
109
+ dependency_ids = {edge.target for edge in self.edges if edge.source == source}
110
+ return [node for node in self.nodes if node.id in dependency_ids]
@@ -0,0 +1,46 @@
1
+ """Repository entity - represents a Git repository being indexed."""
2
+ from dataclasses import dataclass, field
3
+ from datetime import datetime
4
+ from pathlib import Path
5
+
6
+
7
+ @dataclass
8
+ class Repository:
9
+ """
10
+ Repository entity representing a codebase being indexed.
11
+
12
+ Attributes:
13
+ name: Repository name (e.g., "order-service")
14
+ path: Absolute path to repository directory
15
+ language: Primary language (e.g., "java")
16
+ indexed_at: Timestamp when indexing occurred
17
+ files: List of CodeFile entities in this repository
18
+
19
+ Business Rules:
20
+ - Repository name must be unique
21
+ - Path must exist and be accessible
22
+ """
23
+ name: str
24
+ path: Path
25
+ language: str
26
+ indexed_at: datetime = field(default_factory=datetime.now)
27
+ files: list = field(default_factory=list)
28
+
29
+ def add_file(self, code_file) -> None:
30
+ """
31
+ Add a CodeFile to this repository.
32
+
33
+ Args:
34
+ code_file: CodeFile entity to add
35
+ """
36
+ self.files.append(code_file)
37
+
38
+ def get_dependency_graph(self):
39
+ """
40
+ Get dependency graph for this repository.
41
+
42
+ Returns:
43
+ DependencyGraph with nodes and edges extracted from files
44
+ """
45
+ from src.domain.entities.dependency_graph import DependencyGraph
46
+ return DependencyGraph()
@@ -0,0 +1,36 @@
1
+ """
2
+ Domain Layer Exceptions (Task 9.3)
3
+
4
+ Exception hierarchy for domain layer errors.
5
+ """
6
+
7
+
8
+ class DomainException(Exception):
9
+ """Base exception for domain layer errors."""
10
+
11
+ def __init__(self, message: str, details: dict | None = None):
12
+ """
13
+ Initialize domain exception.
14
+
15
+ Args:
16
+ message: Human-readable error message
17
+ details: Optional dict with additional context
18
+ """
19
+ super().__init__(message)
20
+ self.message = message
21
+ self.details = details or {}
22
+
23
+
24
+ class InvalidDependencyError(DomainException):
25
+ """Invalid dependency detected."""
26
+ pass
27
+
28
+
29
+ class InvalidQualifiedNameError(DomainException):
30
+ """Invalid qualified name format."""
31
+ pass
32
+
33
+
34
+ class ChunkingError(DomainException):
35
+ """Error during semantic chunking."""
36
+ pass
@@ -0,0 +1,22 @@
1
+ """Domain repository interfaces module."""
2
+ from src.domain.repositories.i_code_parser import ICodeParser, ParseResult
3
+ from src.domain.repositories.i_vector_repository import IVectorRepository, VectorStorageResult
4
+ from src.domain.repositories.i_graph_repository import (
5
+ IGraphRepository,
6
+ GraphStorageResult,
7
+ ImpactAnalysisResult,
8
+ )
9
+ from src.domain.repositories.i_embedding_provider import IEmbeddingProvider
10
+ from src.domain.repositories.i_dependency_extractor import IDependencyExtractor
11
+
12
+ __all__ = [
13
+ "ICodeParser",
14
+ "ParseResult",
15
+ "IVectorRepository",
16
+ "VectorStorageResult",
17
+ "IGraphRepository",
18
+ "GraphStorageResult",
19
+ "ImpactAnalysisResult",
20
+ "IEmbeddingProvider",
21
+ "IDependencyExtractor",
22
+ ]
@@ -0,0 +1,50 @@
1
+ """ICodeParser interface - contract for AST parsing implementations."""
2
+ from dataclasses import dataclass, field
3
+ from pathlib import Path
4
+ from typing import Protocol, Any, runtime_checkable
5
+
6
+
7
+ @dataclass
8
+ class ParseResult:
9
+ """
10
+ ParseResult containing extracted AST structures.
11
+
12
+ Attributes:
13
+ file_path: Path to the parsed file
14
+ classes: List of extracted class information
15
+ methods: List of extracted method information
16
+ imports: List of extracted import statements
17
+ method_calls: List of extracted method calls
18
+ string_literals: List of string literals found in code
19
+ """
20
+ file_path: Path
21
+ classes: list[Any] = field(default_factory=list)
22
+ methods: list[Any] = field(default_factory=list)
23
+ imports: list[Any] = field(default_factory=list)
24
+ method_calls: list[Any] = field(default_factory=list)
25
+ string_literals: list[str] = field(default_factory=list)
26
+
27
+
28
+ @runtime_checkable
29
+ class ICodeParser(Protocol):
30
+ """
31
+ Interface for code parsers (Tree-sitter, etc.).
32
+
33
+ Implementations must parse source code files and extract
34
+ AST structures like classes, methods, imports, and method calls.
35
+ """
36
+
37
+ def parse_file(self, file_path: Path) -> ParseResult:
38
+ """
39
+ Parse a source code file and extract AST structures.
40
+
41
+ Args:
42
+ file_path: Path to source file to parse
43
+
44
+ Returns:
45
+ ParseResult with extracted structures
46
+
47
+ Raises:
48
+ ParseError: If parsing fails
49
+ """
50
+ ...
@@ -0,0 +1,35 @@
1
+ """IDependencyExtractor interface - contract for dependency extraction implementations."""
2
+ from typing import Protocol, runtime_checkable
3
+
4
+
5
+ @runtime_checkable
6
+ class IDependencyExtractor(Protocol):
7
+ """
8
+ Interface for dependency extractors.
9
+
10
+ Implementations must extract dependencies from AST nodes,
11
+ build files, and configuration files.
12
+ """
13
+
14
+ def extract_dependencies(
15
+ self,
16
+ ast_nodes: list, # list[ASTNode]
17
+ build_files: dict[str, str],
18
+ config_files: dict[str, str]
19
+ ): # -> DependencyGraph
20
+ """
21
+ Extract internal and external dependencies from multiple sources.
22
+
23
+ Args:
24
+ ast_nodes: Parsed AST from source files
25
+ build_files: Content of pom.xml, build.gradle, etc (filename -> content)
26
+ config_files: Content of application.properties, .env, etc (filename -> content)
27
+
28
+ Returns:
29
+ DependencyGraph with nodes (classes, methods, libs, APIs, DBs)
30
+ and edges (calls, imports, uses)
31
+
32
+ Raises:
33
+ DependencyExtractionError: If critical extraction fails
34
+ """
35
+ ...
@@ -0,0 +1,31 @@
1
+ """IEmbeddingProvider interface - contract for embedding generation implementations."""
2
+ from typing import Protocol, runtime_checkable
3
+
4
+
5
+ @runtime_checkable
6
+ class IEmbeddingProvider(Protocol):
7
+ """
8
+ Interface for embedding providers (OpenAI, etc.).
9
+
10
+ Implementations must generate embeddings for text chunks.
11
+ """
12
+
13
+ async def embed_batch(
14
+ self,
15
+ texts: list[str],
16
+ batch_size: int = 10
17
+ ) -> list[list[float]]:
18
+ """
19
+ Generate embeddings for batch of texts.
20
+
21
+ Args:
22
+ texts: List of text chunks to embed
23
+ batch_size: Number of texts per API call (default 10)
24
+
25
+ Returns:
26
+ List of embedding vectors (1536 floats each)
27
+
28
+ Raises:
29
+ EmbeddingProviderError: If API fails after retries
30
+ """
31
+ ...
@@ -0,0 +1,78 @@
1
+ """IGraphRepository interface - contract for graph storage implementations."""
2
+ from dataclasses import dataclass
3
+ from typing import Protocol, Any, runtime_checkable
4
+
5
+
6
+ @dataclass
7
+ class GraphStorageResult:
8
+ """
9
+ GraphStorageResult containing storage statistics.
10
+
11
+ Attributes:
12
+ nodes_created: Number of nodes created in graph
13
+ relationships_created: Number of relationships created
14
+ """
15
+ nodes_created: int
16
+ relationships_created: int
17
+
18
+
19
+ @dataclass
20
+ class ImpactAnalysisResult:
21
+ """
22
+ ImpactAnalysisResult containing impact analysis query results.
23
+
24
+ Attributes:
25
+ target: Target entity being analyzed (repo, class, API)
26
+ query_type: Type of analysis performed
27
+ affected_components: List of affected components
28
+ """
29
+ target: str
30
+ query_type: str
31
+ affected_components: list[Any]
32
+
33
+
34
+ @runtime_checkable
35
+ class IGraphRepository(Protocol):
36
+ """
37
+ Interface for graph storage repositories (Neo4j, etc.).
38
+
39
+ Implementations must store dependency graphs and support
40
+ impact analysis queries.
41
+ """
42
+
43
+ async def store_dependency_graph(
44
+ self,
45
+ graph, # DependencyGraph
46
+ repository_name: str
47
+ ) -> GraphStorageResult:
48
+ """
49
+ Store complete dependency graph in graph database.
50
+
51
+ Args:
52
+ graph: DependencyGraph with nodes and edges
53
+ repository_name: Name of repository being indexed
54
+
55
+ Returns:
56
+ GraphStorageResult with counts (nodes_created, relationships_created)
57
+
58
+ Raises:
59
+ GraphStorageError: If storage fails
60
+ """
61
+ ...
62
+
63
+ async def query_impact_analysis(
64
+ self,
65
+ query_type: str,
66
+ params: dict[str, Any]
67
+ ) -> ImpactAnalysisResult:
68
+ """
69
+ Execute impact analysis query.
70
+
71
+ Args:
72
+ query_type: Type of analysis (REPO_DEPENDENTS, CLASS_IMPACT, etc)
73
+ params: Query parameters (repo_name, class_name, etc)
74
+
75
+ Returns:
76
+ ImpactAnalysisResult with affected components
77
+ """
78
+ ...
@@ -0,0 +1,55 @@
1
+ """IVectorRepository interface - contract for vector storage implementations."""
2
+ from dataclasses import dataclass
3
+ from typing import Protocol, runtime_checkable
4
+
5
+
6
+ @dataclass
7
+ class VectorStorageResult:
8
+ """
9
+ VectorStorageResult containing upsert statistics.
10
+
11
+ Attributes:
12
+ points_upserted: Number of vector points upserted
13
+ """
14
+ points_upserted: int
15
+
16
+
17
+ @runtime_checkable
18
+ class IVectorRepository(Protocol):
19
+ """
20
+ Interface for vector storage repositories (Qdrant, etc.).
21
+
22
+ Implementations must store embeddings with metadata and
23
+ support querying unique repositories.
24
+ """
25
+
26
+ async def upsert_batch(
27
+ self,
28
+ chunks: list,
29
+ embeddings: list[list[float]],
30
+ batch_size: int = 1000
31
+ ) -> VectorStorageResult:
32
+ """
33
+ Upsert batch of embeddings with metadata to vector storage.
34
+
35
+ Args:
36
+ chunks: Code chunks with metadata
37
+ embeddings: Corresponding embeddings (1536-dim)
38
+ batch_size: Points per upsert call (default 1000)
39
+
40
+ Returns:
41
+ VectorStorageResult with count of points upserted
42
+
43
+ Raises:
44
+ VectorStorageError: If upsert fails
45
+ """
46
+ ...
47
+
48
+ async def get_unique_repositories(self) -> list[str]:
49
+ """
50
+ Get list of unique repository names indexed.
51
+
52
+ Returns:
53
+ List of repository names
54
+ """
55
+ ...
@@ -0,0 +1,8 @@
1
+ """Domain services module."""
2
+ from src.domain.services.dependency_extractor import DependencyExtractor
3
+ from src.domain.services.semantic_chunk_builder import SemanticChunkBuilder
4
+
5
+ __all__ = [
6
+ "DependencyExtractor",
7
+ "SemanticChunkBuilder",
8
+ ]