Skip to content

Commit a506902

Browse files
author
Gauvain Pocentek
committed
Add support for managers in objects for new API
Convert User* to the new REST* API.
1 parent 9fbdb94 commit a506902

File tree

3 files changed

+119
-88
lines changed

3 files changed

+119
-88
lines changed

gitlab/base.py

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -575,8 +575,14 @@ def __init__(self, manager, attrs):
575575
'manager': manager,
576576
'_attrs': attrs,
577577
'_updated_attrs': {},
578+
'_module': importlib.import_module(self.__module__)
578579
})
579580

581+
# TODO(gpocentek): manage the creation of new objects from the received
582+
# data (_constructor_types)
583+
584+
self._create_managers()
585+
580586
def __getattr__(self, name):
581587
try:
582588
return self.__dict__['_updated_attrs'][name]
@@ -602,6 +608,16 @@ def __repr__(self):
602608
else:
603609
return '<%s>' % self.__class__.__name__
604610

611+
def _create_managers(self):
612+
managers = getattr(self, '_managers', None)
613+
if managers is None:
614+
return
615+
616+
for attr, cls_name in self._managers:
617+
cls = getattr(self._module, cls_name)
618+
manager = cls(self.manager.gitlab, parent=self)
619+
self.__dict__[attr] = manager
620+
605621
def get_id(self):
606622
if self._id_attr is None:
607623
return None
@@ -653,6 +669,19 @@ class RESTManager(object):
653669
_path = None
654670
_obj_cls = None
655671

656-
def __init__(self, gl, parent_attrs={}):
672+
def __init__(self, gl, parent=None):
657673
self.gitlab = gl
658-
self._parent_attrs = {} # for nested managers
674+
self._parent = parent # for nested managers
675+
self._computed_path = self._compute_path()
676+
677+
def _compute_path(self):
678+
if self._parent is None or not hasattr(self, '_from_parent_attrs'):
679+
return self._path
680+
681+
data = {self_attr: getattr(self._parent, parent_attr)
682+
for self_attr, parent_attr in self._from_parent_attrs.items()}
683+
return self._path % data
684+
685+
@property
686+
def path(self):
687+
return self._computed_path

gitlab/mixins.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ def get(self, id, **kwargs):
3232
Raises:
3333
GitlabGetError: If the server cannot perform the request.
3434
"""
35-
path = '%s/%s' % (self._path, id)
35+
path = '%s/%s' % (self.path, id)
3636
server_data = self.gitlab.http_get(path, **kwargs)
3737
return self._obj_cls(self, server_data)
3838

@@ -50,7 +50,7 @@ def get(self, **kwargs):
5050
Raises:
5151
GitlabGetError: If the server cannot perform the request.
5252
"""
53-
server_data = self.gitlab.http_get(self._path, **kwargs)
53+
server_data = self.gitlab.http_get(self.path, **kwargs)
5454
return self._obj_cls(self, server_data)
5555

5656

@@ -70,7 +70,7 @@ def list(self, **kwargs):
7070
list(RESTObjectList).
7171
"""
7272

73-
obj = self.gitlab.http_list(self._path, **kwargs)
73+
obj = self.gitlab.http_list(self.path, **kwargs)
7474
if isinstance(obj, list):
7575
return [self._obj_cls(self, item) for item in obj]
7676
else:
@@ -139,7 +139,7 @@ def create(self, data, **kwargs):
139139
self._check_missing_attrs(data)
140140
if hasattr(self, '_sanitize_data'):
141141
data = self._sanitize_data(data, 'create')
142-
server_data = self.gitlab.http_post(self._path, post_data=data,
142+
server_data = self.gitlab.http_post(self.path, post_data=data,
143143
**kwargs)
144144
return self._obj_cls(self, server_data)
145145

@@ -180,9 +180,9 @@ def update(self, id=None, new_data={}, **kwargs):
180180
"""
181181

182182
if id is None:
183-
path = self._path
183+
path = self.path
184184
else:
185-
path = '%s/%s' % (self._path, id)
185+
path = '%s/%s' % (self.path, id)
186186

187187
self._check_missing_attrs(new_data)
188188
if hasattr(self, '_sanitize_data'):
@@ -199,7 +199,7 @@ def delete(self, id, **kwargs):
199199
id: ID of the object to delete
200200
**kwargs: Extra data to send to the Gitlab server (e.g. sudo)
201201
"""
202-
path = '%s/%s' % (self._path, id)
202+
path = '%s/%s' % (self.path, id)
203203
self.gitlab.http_delete(path, **kwargs)
204204

205205

gitlab/v4/objects.py

Lines changed: 81 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -77,105 +77,107 @@ def compound_metrics(self, **kwargs):
7777
return self._simple_get('/sidekiq/compound_metrics', **kwargs)
7878

7979

80-
class UserEmail(GitlabObject):
81-
_url = '/users/%(user_id)s/emails'
82-
canUpdate = False
83-
shortPrintAttr = 'email'
84-
requiredUrlAttrs = ['user_id']
85-
requiredCreateAttrs = ['email']
80+
class UserEmail(RESTObject):
81+
_short_print_attr = 'email'
8682

8783

88-
class UserEmailManager(BaseManager):
89-
obj_cls = UserEmail
84+
class UserEmailManager(RetrieveMixin, CreateMixin, DeleteMixin, RESTManager):
85+
_path = '/users/%(user_id)s/emails'
86+
_obj_cls = UserEmail
87+
_from_parent_attrs = {'user_id': 'id'}
88+
_create_attrs = {'required': ('email', ), 'optional': tuple()}
9089

9190

92-
class UserKey(GitlabObject):
93-
_url = '/users/%(user_id)s/keys'
94-
canGet = 'from_list'
95-
canUpdate = False
96-
requiredUrlAttrs = ['user_id']
97-
requiredCreateAttrs = ['title', 'key']
91+
class UserKey(RESTObject):
92+
pass
9893

9994

100-
class UserKeyManager(BaseManager):
101-
obj_cls = UserKey
95+
class UserKeyManager(GetFromListMixin, CreateMixin, DeleteMixin, RESTManager):
96+
_path = '/users/%(user_id)s/emails'
97+
_obj_cls = UserKey
98+
_from_parent_attrs = {'user_id': 'id'}
99+
_create_attrs = {'required': ('title', 'key'), 'optional': tuple()}
102100

103101

104-
class UserProject(GitlabObject):
105-
_url = '/projects/user/%(user_id)s'
106-
_constructorTypes = {'owner': 'User', 'namespace': 'Group'}
107-
canUpdate = False
108-
canDelete = False
109-
canList = False
110-
canGet = False
111-
requiredUrlAttrs = ['user_id']
112-
requiredCreateAttrs = ['name']
113-
optionalCreateAttrs = ['default_branch', 'issues_enabled', 'wall_enabled',
114-
'merge_requests_enabled', 'wiki_enabled',
115-
'snippets_enabled', 'public', 'visibility',
116-
'description', 'builds_enabled', 'public_builds',
117-
'import_url', 'only_allow_merge_if_build_succeeds']
102+
class UserProject(RESTObject):
103+
_constructor_types = {'owner': 'User', 'namespace': 'Group'}
118104

119105

120-
class UserProjectManager(BaseManager):
121-
obj_cls = UserProject
106+
class UserProjectManager(CreateMixin, RESTManager):
107+
_path = '/projects/user/%(user_id)s'
108+
_obj_cls = UserProject
109+
_from_parent_attrs = {'user_id': 'id'}
110+
_create_attrs = {
111+
'required': ('name', ),
112+
'optional': ('default_branch', 'issues_enabled', 'wall_enabled',
113+
'merge_requests_enabled', 'wiki_enabled',
114+
'snippets_enabled', 'public', 'visibility', 'description',
115+
'builds_enabled', 'public_builds', 'import_url',
116+
'only_allow_merge_if_build_succeeds')
117+
}
122118

123119

124-
class User(GitlabObject):
125-
_url = '/users'
126-
shortPrintAttr = 'username'
127-
optionalListAttrs = ['active', 'blocked', 'username', 'extern_uid',
128-
'provider', 'external']
129-
requiredCreateAttrs = ['email', 'username', 'name']
130-
optionalCreateAttrs = ['password', 'reset_password', 'skype', 'linkedin',
131-
'twitter', 'projects_limit', 'extern_uid',
132-
'provider', 'bio', 'admin', 'can_create_group',
133-
'website_url', 'skip_confirmation', 'external',
134-
'organization', 'location']
135-
requiredUpdateAttrs = ['email', 'username', 'name']
136-
optionalUpdateAttrs = ['password', 'skype', 'linkedin', 'twitter',
137-
'projects_limit', 'extern_uid', 'provider', 'bio',
138-
'admin', 'can_create_group', 'website_url',
139-
'skip_confirmation', 'external', 'organization',
140-
'location']
141-
managers = (
142-
('emails', 'UserEmailManager', [('user_id', 'id')]),
143-
('keys', 'UserKeyManager', [('user_id', 'id')]),
144-
('projects', 'UserProjectManager', [('user_id', 'id')]),
120+
class User(SaveMixin, RESTObject):
121+
_short_print_attr = 'username'
122+
_managers = (
123+
('emails', 'UserEmailManager'),
124+
('keys', 'UserKeyManager'),
125+
('projects', 'UserProjectManager'),
145126
)
146127

147-
def _data_for_gitlab(self, extra_parameters={}, update=False,
148-
as_json=True):
149-
if hasattr(self, 'confirm'):
150-
self.confirm = str(self.confirm).lower()
151-
return super(User, self)._data_for_gitlab(extra_parameters)
152-
153128
def block(self, **kwargs):
154-
"""Blocks the user."""
155-
url = '/users/%s/block' % self.id
156-
r = self.gitlab._raw_post(url, **kwargs)
157-
raise_error_from_response(r, GitlabBlockError, 201)
158-
self.state = 'blocked'
129+
"""Blocks the user.
130+
131+
Returns:
132+
bool: whether the user status has been changed.
133+
"""
134+
path = '/users/%s/block' % self.id
135+
server_data = self.manager.gitlab.http_post(path, **kwargs)
136+
if server_data is True:
137+
self._attrs['state'] = 'blocked'
138+
return server_data
159139

160140
def unblock(self, **kwargs):
161-
"""Unblocks the user."""
162-
url = '/users/%s/unblock' % self.id
163-
r = self.gitlab._raw_post(url, **kwargs)
164-
raise_error_from_response(r, GitlabUnblockError, 201)
165-
self.state = 'active'
141+
"""Unblocks the user.
142+
143+
Returns:
144+
bool: whether the user status has been changed.
145+
"""
146+
path = '/users/%s/unblock' % self.id
147+
server_data = self.manager.gitlab.http_post(path, **kwargs)
148+
if server_data is True:
149+
self._attrs['state'] = 'active'
150+
return server_data
166151

167-
def __eq__(self, other):
168-
if type(other) is type(self):
169-
selfdict = self.as_dict()
170-
otherdict = other.as_dict()
171-
selfdict.pop('password', None)
172-
otherdict.pop('password', None)
173-
return selfdict == otherdict
174-
return False
175152

153+
class UserManager(CRUDMixin, RESTManager):
154+
_path = '/users'
155+
_obj_cls = User
176156

177-
class UserManager(BaseManager):
178-
obj_cls = User
157+
_list_filters = ('active', 'blocked', 'username', 'extern_uid', 'provider',
158+
'external')
159+
_create_attrs = {
160+
'required': ('email', 'username', 'name'),
161+
'optional': ('password', 'reset_password', 'skype', 'linkedin',
162+
'twitter', 'projects_limit', 'extern_uid', 'provider',
163+
'bio', 'admin', 'can_create_group', 'website_url',
164+
'skip_confirmation', 'external', 'organization',
165+
'location')
166+
}
167+
_update_attrs = {
168+
'required': ('email', 'username', 'name'),
169+
'optional': ('password', 'skype', 'linkedin', 'twitter',
170+
'projects_limit', 'extern_uid', 'provider', 'bio',
171+
'admin', 'can_create_group', 'website_url',
172+
'skip_confirmation', 'external', 'organization',
173+
'location')
174+
}
175+
176+
def _sanitize_data(self, data, action):
177+
new_data = data.copy()
178+
if 'confirm' in data:
179+
new_data['confirm'] = str(new_data['confirm']).lower()
180+
return new_data
179181

180182

181183
class CurrentUserEmail(GitlabObject):

0 commit comments

Comments
 (0)