Coverage for src/qdrant_loader/config/sources.py: 87%
55 statements
« prev ^ index » next coverage.py v7.10.0, created at 2025-07-25 11:39 +0000
« prev ^ index » next coverage.py v7.10.0, created at 2025-07-25 11:39 +0000
1"""Sources configuration.
3This module defines the configuration for all data sources, including Git repositories,
4Confluence spaces, Jira projects, and public documentation.
5"""
7from typing import TYPE_CHECKING, Any
9from pydantic import BaseModel, ConfigDict, Field
11from qdrant_loader.config.source_config import SourceConfig
12from qdrant_loader.config.types import SourceType
14# Use TYPE_CHECKING to avoid circular imports at runtime
15if TYPE_CHECKING:
16 pass
19def _get_connector_config_classes():
20 """Lazy import connector config classes to avoid circular dependencies."""
21 from qdrant_loader.connectors.confluence.config import ConfluenceSpaceConfig
22 from qdrant_loader.connectors.git.config import GitRepoConfig
23 from qdrant_loader.connectors.jira.config import JiraProjectConfig
24 from qdrant_loader.connectors.localfile.config import LocalFileConfig
25 from qdrant_loader.connectors.publicdocs.config import PublicDocsSourceConfig
27 return {
28 "PublicDocsSourceConfig": PublicDocsSourceConfig,
29 "GitRepoConfig": GitRepoConfig,
30 "ConfluenceSpaceConfig": ConfluenceSpaceConfig,
31 "JiraProjectConfig": JiraProjectConfig,
32 "LocalFileConfig": LocalFileConfig,
33 }
36class SourcesConfig(BaseModel):
37 """Configuration for all available data sources."""
39 publicdocs: dict[str, Any] = Field(
40 default_factory=dict, description="Public documentation sources"
41 )
42 git: dict[str, Any] = Field(
43 default_factory=dict, description="Git repository sources"
44 )
45 confluence: dict[str, Any] = Field(
46 default_factory=dict, description="Confluence space sources"
47 )
48 jira: dict[str, Any] = Field(
49 default_factory=dict, description="Jira project sources"
50 )
51 localfile: dict[str, Any] = Field(
52 default_factory=dict, description="Local file sources"
53 )
55 model_config = ConfigDict(arbitrary_types_allowed=False, extra="forbid")
57 def __init__(self, **data):
58 """Initialize SourcesConfig with proper connector config objects."""
59 # Convert dictionaries to proper config objects
60 processed_data = {}
62 for field_name, field_value in data.items():
63 if field_name in [
64 "publicdocs",
65 "git",
66 "confluence",
67 "jira",
68 "localfile",
69 ] and isinstance(field_value, dict):
70 processed_data[field_name] = self._convert_source_configs(
71 field_name, field_value
72 )
73 else:
74 processed_data[field_name] = field_value
76 super().__init__(**processed_data)
78 def _convert_source_configs(self, source_type: str, configs: dict) -> dict:
79 """Convert dictionary configs to proper config objects."""
80 config_classes = _get_connector_config_classes()
81 converted = {}
83 for name, config_data in configs.items():
84 if isinstance(config_data, dict):
85 # Get the appropriate config class
86 if source_type == "publicdocs":
87 config_class = config_classes["PublicDocsSourceConfig"]
88 elif source_type == "git":
89 config_class = config_classes["GitRepoConfig"]
90 elif source_type == "confluence":
91 config_class = config_classes["ConfluenceSpaceConfig"]
92 elif source_type == "jira":
93 config_class = config_classes["JiraProjectConfig"]
94 elif source_type == "localfile":
95 config_class = config_classes["LocalFileConfig"]
96 else:
97 # Unknown source type, keep as dict
98 converted[name] = config_data
99 continue
101 # Create the config object - let validation errors propagate
102 try:
103 converted[name] = config_class(**config_data)
104 except (ImportError, AttributeError, TypeError):
105 # Only catch import/type errors, not validation errors
106 # These indicate missing dependencies or code issues
107 converted[name] = config_data
108 # Let ValidationError and other Pydantic errors propagate
109 else:
110 # Already a config object or other type
111 converted[name] = config_data
113 return converted
115 def get_source_config(self, source_type: str, source: str) -> SourceConfig | None:
116 """Get the configuration for a specific source.
118 Args:
119 source_type: Type of the source (publicdocs, git, confluence, jira)
120 source: Name of the specific source configuration
122 Returns:
123 Optional[BaseModel]: The source configuration if it exists, None otherwise
124 """
125 source_dict = getattr(self, source_type, {})
126 return source_dict.get(source)
128 def to_dict(self) -> dict:
129 """Convert the configuration to a dictionary."""
130 return {
131 SourceType.PUBLICDOCS: {
132 name: config.model_dump() if hasattr(config, "model_dump") else config
133 for name, config in self.publicdocs.items()
134 },
135 SourceType.GIT: {
136 name: config.model_dump() if hasattr(config, "model_dump") else config
137 for name, config in self.git.items()
138 },
139 SourceType.CONFLUENCE: {
140 name: config.model_dump() if hasattr(config, "model_dump") else config
141 for name, config in self.confluence.items()
142 },
143 SourceType.JIRA: {
144 name: config.model_dump() if hasattr(config, "model_dump") else config
145 for name, config in self.jira.items()
146 },
147 SourceType.LOCALFILE: {
148 name: config.model_dump() if hasattr(config, "model_dump") else config
149 for name, config in self.localfile.items()
150 },
151 }