Coverage for src/gitlabracadabra/mixins/members.py: 81%
50 statements
« prev ^ index » next coverage.py v7.8.0, created at 2025-04-14 23:10 +0200
« prev ^ index » next coverage.py v7.8.0, created at 2025-04-14 23:10 +0200
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/>.
17import logging
18from copy import deepcopy
19from re import match
21from gitlab.exceptions import GitlabDeleteError
23from gitlabracadabra.gitlab.access_levels import access_level_value
24from gitlabracadabra.objects.object import GitLabracadabraObject
26logger = logging.getLogger(__name__)
29class MembersMixin(GitLabracadabraObject):
30 """Object with members."""
32 """_process_members()
34 Process the members param.
35 """
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
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 )