Coverage for src/mcp_atlassian/jira/client.py: 89%

28 statements  

« prev     ^ index     » next       coverage.py v7.6.12, created at 2025-03-10 03:26 +0900

1"""Base client module for Jira API interactions.""" 

2 

3import logging 

4import re 

5from typing import Optional, Dict, Any, cast 

6 

7from atlassian import Jira 

8 

9from .config import JiraConfig 

10 

11# Configure logging 

12logger = logging.getLogger("mcp-jira") 

13 

14 

15class JiraClient: 

16 """Base client for Jira API interactions.""" 

17 

18 def __init__(self, config: Optional[JiraConfig] = None) -> None: 

19 """Initialize the Jira client with configuration options. 

20  

21 Args: 

22 config: Optional configuration object (will use env vars if not provided) 

23  

24 Raises: 

25 ValueError: If configuration is invalid or required credentials are missing 

26 """ 

27 from mcp_atlassian.preprocessing import TextPreprocessor 

28 

29 # Load configuration from environment variables if not provided 

30 self.config = config or JiraConfig.from_env() 

31 

32 # Initialize the Jira client based on auth type 

33 if self.config.auth_type == "token": 

34 self.jira = Jira( 

35 url=self.config.url, 

36 token=self.config.personal_token, 

37 cloud=self.config.is_cloud, 

38 verify_ssl=self.config.ssl_verify, 

39 ) 

40 else: # basic auth 

41 self.jira = Jira( 

42 url=self.config.url, 

43 username=self.config.username, 

44 password=self.config.api_token, 

45 cloud=self.config.is_cloud, 

46 verify_ssl=self.config.ssl_verify, 

47 ) 

48 

49 # Initialize the text preprocessor for text processing capabilities 

50 self.preprocessor = TextPreprocessor(self.config.url) 

51 

52 # Cache for frequently used data 

53 self._field_ids: Optional[Dict[str, str]] = None 

54 self._current_user_account_id: Optional[str] = None 

55 

56 def _clean_text(self, text: str) -> str: 

57 """Clean text content by: 

58 1. Processing user mentions and links 

59 2. Converting HTML/wiki markup to markdown 

60  

61 Args: 

62 text: Text to clean 

63  

64 Returns: 

65 Cleaned text 

66 """ 

67 if not text: 

68 return "" 

69 

70 # Otherwise create a temporary one 

71 url = self.config.url if hasattr(self, 'config') else "" 

72 return self.preprocessor.clean_jira_text(text) 

73 

74 def _markdown_to_jira(self, markdown_text: str) -> str: 

75 """ 

76 Convert Markdown syntax to Jira markup syntax. 

77  

78 Args: 

79 markdown_text: Text in Markdown format 

80  

81 Returns: 

82 Text in Jira markup format 

83 """ 

84 if not markdown_text: 

85 return "" 

86 

87 # Use the shared preprocessor if available 

88 if hasattr(self, 'preprocessor'): 

89 return self.preprocessor.markdown_to_jira(markdown_text) 

90 

91 # Otherwise create a temporary one 

92 url = self.config.url if hasattr(self, 'config') else "" 

93 return self.preprocessor.markdown_to_jira(markdown_text)