Coverage for src/mcp_atlassian/jira/users.py: 94%

63 statements  

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

1"""Module for Jira user operations.""" 

2 

3import logging 

4import re 

5from typing import Optional, Dict, Any, cast 

6 

7import requests 

8 

9from .client import JiraClient 

10 

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

12 

13 

14class UsersMixin(JiraClient): 

15 """Mixin for Jira user operations.""" 

16 

17 def get_current_user_account_id(self) -> str: 

18 """Get the account ID of the current user. 

19  

20 Returns: 

21 Account ID of the current user 

22  

23 Raises: 

24 Exception: If unable to get the current user's account ID 

25 """ 

26 if self._current_user_account_id is not None: 

27 return self._current_user_account_id 

28 

29 try: 

30 myself = self.jira.myself() 

31 if "accountId" in myself: 

32 self._current_user_account_id = myself["accountId"] 

33 return self._current_user_account_id 

34 

35 error_msg = "Could not find accountId in user data" 

36 raise ValueError(error_msg) 

37 except Exception as e: 

38 logger.error(f"Error getting current user account ID: {str(e)}") 

39 error_msg = f"Unable to get current user account ID: {str(e)}" 

40 raise Exception(error_msg) 

41 

42 def _get_account_id(self, assignee: str) -> str: 

43 """Get the account ID for a username. 

44  

45 Args: 

46 assignee: Username or account ID 

47  

48 Returns: 

49 Account ID 

50  

51 Raises: 

52 ValueError: If the account ID could not be found 

53 """ 

54 # If it looks like an account ID already, return it 

55 if assignee.startswith("5") and len(assignee) >= 10: 

56 return assignee 

57 

58 # First try direct lookup 

59 account_id = self._lookup_user_directly(assignee) 

60 if account_id: 

61 return account_id 

62 

63 # If that fails, try permissions-based lookup 

64 account_id = self._lookup_user_by_permissions(assignee) 

65 if account_id: 

66 return account_id 

67 

68 error_msg = f"Could not find account ID for user: {assignee}" 

69 raise ValueError(error_msg) 

70 

71 def _lookup_user_directly(self, username: str) -> Optional[str]: 

72 """Look up a user account ID directly. 

73  

74 Args: 

75 username: Username to look up 

76  

77 Returns: 

78 Account ID if found, None otherwise 

79 """ 

80 try: 

81 # Try to find user 

82 response = self.jira.user_find_by_user_string(query=username, start=0, limit=1) 

83 if not response: 

84 return None 

85 

86 for user in response: 

87 if ("accountId" in user and 

88 (user.get("displayName", "").lower() == username.lower() or 

89 user.get("name", "").lower() == username.lower() or 

90 user.get("emailAddress", "").lower() == username.lower())): 

91 return user["accountId"] 

92 return None 

93 except Exception as e: 

94 logger.info(f"Error looking up user directly: {str(e)}") 

95 return None 

96 

97 def _lookup_user_by_permissions(self, username: str) -> Optional[str]: 

98 """Look up a user account ID by permissions. 

99  

100 This is a fallback method when direct lookup fails. 

101  

102 Args: 

103 username: Username to look up 

104  

105 Returns: 

106 Account ID if found, None otherwise 

107 """ 

108 try: 

109 # Try to find user who has permissions for a project 

110 # This approach helps when regular lookup fails due to permissions 

111 url = f"{self.config.url}/rest/api/2/user/permission/search" 

112 params = {"query": username, "permissions": "BROWSE"} 

113 

114 auth = None 

115 headers = {} 

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

117 headers["Authorization"] = f"Bearer {self.config.personal_token}" 

118 else: 

119 auth = (self.config.username or "", self.config.api_token or "") 

120 

121 response = requests.get( 

122 url, 

123 params=params, 

124 auth=auth, 

125 headers=headers, 

126 verify=self.config.ssl_verify, 

127 ) 

128 

129 if response.status_code == 200: 

130 data = response.json() 

131 for user in data.get("users", []): 

132 if "accountId" in user: 

133 return user["accountId"] 

134 return None 

135 except Exception as e: 

136 logger.info(f"Error looking up user by permissions: {str(e)}") 

137 return None