Coverage for src/qdrant_loader/cli/commands/config.py: 45%

42 statements  

« prev     ^ index     » next       coverage.py v7.10.6, created at 2025-09-08 06:05 +0000

1from __future__ import annotations 

2 

3import json 

4from pathlib import Path 

5 

6from click.exceptions import ClickException 

7 

8 

9def run_show_config( 

10 workspace: Path | None, 

11 config: Path | None, 

12 env: Path | None, 

13 log_level: str, 

14) -> str: 

15 """Load configuration and return a JSON string representation. 

16 

17 This function is a command helper used by the CLI wrapper to display configuration. 

18 It performs validation, optional workspace setup, logging setup, and settings loading. 

19 """ 

20 try: 

21 from qdrant_loader.cli.config_loader import ( 

22 load_config_with_workspace as _load_config_with_workspace, 

23 ) 

24 from qdrant_loader.cli.config_loader import ( 

25 setup_workspace as _setup_workspace_impl, 

26 ) 

27 from qdrant_loader.config import get_settings 

28 from qdrant_loader.config.workspace import validate_workspace_flags 

29 from qdrant_loader.utils.logging import LoggingConfig 

30 

31 # Validate flag combinations 

32 validate_workspace_flags(workspace, config, env) 

33 

34 # Setup workspace if provided 

35 workspace_config = None 

36 if workspace: 

37 workspace_config = _setup_workspace_impl(workspace) 

38 

39 # Setup logging with workspace support 

40 log_file = ( 

41 str(workspace_config.logs_path) if workspace_config else "qdrant-loader.log" 

42 ) 

43 LoggingConfig.setup(level=log_level, format="console", file=log_file) 

44 

45 # Load configuration (skip validation to avoid directory prompts) 

46 _load_config_with_workspace(workspace_config, config, env, skip_validation=True) 

47 settings = get_settings() 

48 if settings is None: 

49 raise ClickException("Settings not available") 

50 

51 # Redact sensitive values before dumping to JSON 

52 def _redact_secrets(value): 

53 """Recursively redact sensitive keys within nested structures. 

54 

55 Keys are compared case-insensitively and redacted if their name contains 

56 any common secret-like token. 

57 """ 

58 sensitive_tokens = { 

59 "password", 

60 "secret", 

61 "api_key", 

62 "token", 

63 "key", 

64 "credentials", 

65 "access_token", 

66 "private_key", 

67 "client_secret", 

68 } 

69 

70 mask = "****" 

71 

72 if isinstance(value, dict): 

73 redacted: dict = {} 

74 for k, v in value.items(): 

75 key_lc = str(k).lower() 

76 if any(token in key_lc for token in sensitive_tokens): 

77 redacted[k] = mask 

78 else: 

79 redacted[k] = _redact_secrets(v) 

80 return redacted 

81 if isinstance(value, list): 

82 return [_redact_secrets(item) for item in value] 

83 # Primitive or unknown types are returned as-is 

84 return value 

85 

86 config_dict = settings.model_dump(mode="json") 

87 safe_config = _redact_secrets(config_dict) 

88 return json.dumps(safe_config, indent=2, ensure_ascii=False) 

89 except ClickException: 

90 raise 

91 except Exception as e: 

92 raise ClickException(f"Failed to display configuration: {str(e)!s}") from e