Coverage for src/gitlabracadabra/mixins/members.py: 81%

50 statements  

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

1# 

2# Copyright (C) 2019-2025 Mathieu Parent <math.parent@gmail.com> 

3# 

4# This program is free software: you can redistribute it and/or modify 

5# it under the terms of the GNU Lesser General Public License as published by 

6# the Free Software Foundation, either version 3 of the License, or 

7# (at your option) any later version. 

8# 

9# This program is distributed in the hope that it will be useful, 

10# but WITHOUT ANY WARRANTY; without even the implied warranty of 

11# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 

12# GNU Lesser General Public License for more details. 

13# 

14# You should have received a copy of the GNU Lesser General Public License 

15# along with this program. If not, see <http://www.gnu.org/licenses/>. 

16 

17import logging 

18from copy import deepcopy 

19from re import match 

20 

21from gitlab.exceptions import GitlabDeleteError 

22 

23from gitlabracadabra.gitlab.access_levels import access_level_value 

24from gitlabracadabra.objects.object import GitLabracadabraObject 

25 

26logger = logging.getLogger(__name__) 

27 

28 

29class MembersMixin(GitLabracadabraObject): 

30 """Object with members.""" 

31 

32 """_process_members() 

33 

34 Process the members param. 

35 """ 

36 

37 def _process_members(self, param_name, param_value, *, dry_run=False, skip_save=False): 

38 assert param_name == "members" # noqa: S101 

39 assert not skip_save # noqa: S101 

40 param_value = deepcopy(param_value) 

41 unknown_members = self._content.get("unknown_members", "warn") 

42 current_members = dict([[member.username, member] for member in self._obj.members.list(all=True)]) 

43 # We first check for already existing members 

44 for _member_username, member in sorted(current_members.items()): 

45 self.connection.user_cache.map_user(member.id, member.username) 

46 if member.username in param_value: 

47 target_access_level = access_level_value(param_value[member.username]) 

48 if member.access_level != target_access_level: 48 ↛ 69line 48 didn't jump to line 69 because the condition on line 48 was always true

49 if dry_run: 49 ↛ 50line 49 didn't jump to line 50 because the condition on line 49 was never true

50 logger.info( 

51 "[%s] NOT Changing user %s (%s) access level: %s -> %s (dry-run)", 

52 self._name, 

53 member.username, 

54 member.name, 

55 member.access_level, 

56 target_access_level, 

57 ) 

58 else: 

59 logger.info( 

60 "[%s] Changing user %s (%s) access level: %s -> %s", 

61 self._name, 

62 member.username, 

63 member.name, 

64 member.access_level, 

65 target_access_level, 

66 ) 

67 member.access_level = target_access_level 

68 member.save() 

69 param_value.pop(member.username) 

70 else: 

71 if match(r"(project|group)_\d+_bot_[0-9a-f]+", member.username): 71 ↛ 73line 71 didn't jump to line 73 because the condition on line 71 was never true

72 # bot 

73 continue 

74 if unknown_members == "warn": 74 ↛ 75line 74 didn't jump to line 75 because the condition on line 74 was never true

75 logger.warning("[%s] NOT Removing member: %s (%s)", self._name, member.username, member.name) 

76 if unknown_members not in ["delete", "remove"]: 

77 # warn, ignore, skip 

78 continue 

79 

80 if dry_run: 80 ↛ 81line 80 didn't jump to line 81 because the condition on line 80 was never true

81 logger.info("[%s] NOT Removing member %s (%s) (dry-run)", self._name, member.username, member.name) 

82 continue 

83 logger.info("[%s] Removing member %s (%s)", self._name, member.username, member.name) 

84 try: 

85 member.delete() 

86 except GitlabDeleteError as e: 

87 logger.warning( 

88 "[%s] Unable to remove member %s (%s): %s", 

89 self._name, 

90 member.username, 

91 member.name, 

92 e.error_message, 

93 ) 

94 # Remaining members 

95 for target_username, target_user_access in sorted(param_value.items()): 

96 user_id = self.connection.user_cache.id_from_username(target_username) 

97 if user_id is None: 

98 logger.warning("[%s] User not found %s", self._name, target_username) 

99 continue 

100 target_access_level = access_level_value(target_user_access) 

101 if dry_run: 101 ↛ 102line 101 didn't jump to line 102 because the condition on line 101 was never true

102 logger.info( 

103 "[%s] NOT Adding user %s: %s -> %s (dry-run)", self._name, target_username, 0, target_access_level 

104 ) 

105 else: 

106 logger.info("[%s] Adding user %s: %s -> %s", self._name, target_username, 0, target_access_level) 

107 self._obj.members.create( 

108 { 

109 "user_id": user_id, 

110 "access_level": target_access_level, 

111 } 

112 )