Skip to content

Commit

Permalink
Migrate all v4 objects to new API
Browse files Browse the repository at this point in the history
Some things are probably broken. Next step is writting unit and
functional tests.

And fix.
  • Loading branch information
Gauvain Pocentek committed May 28, 2017
1 parent 230b567 commit 6be990c
Show file tree
Hide file tree
Showing 5 changed files with 926 additions and 1,161 deletions.
18 changes: 11 additions & 7 deletions gitlab/__init__.py
Expand Up @@ -683,7 +683,7 @@ def http_get(self, path, query_data={}, streamed=False, **kwargs):
raise GitlaParsingError(
message="Failed to parse the server message")
else:
return r
return result

def http_list(self, path, query_data={}, **kwargs):
"""Make a GET request to the Gitlab server for list-oriented queries.
Expand Down Expand Up @@ -722,19 +722,23 @@ def http_post(self, path, query_data={}, post_data={}, **kwargs):
**kwargs: Extra data to make the query (e.g. sudo, per_page, page)
Returns:
The parsed json returned by the server.
The parsed json returned by the server if json is return, else the
raw content.
Raises:
GitlabHttpError: When the return code is not 2xx
GitlabParsingError: IF the json data could not be parsed
"""
result = self.http_request('post', path, query_data=query_data,
post_data=post_data, **kwargs)
try:
return result.json()
except Exception:
raise GitlabParsingError(
message="Failed to parse the server message")
if result.headers.get('Content-Type', None) == 'application/json':
try:
return result.json()
except Exception:
raise GitlabParsingError(
message="Failed to parse the server message")
else:
return result.content

def http_put(self, path, query_data={}, post_data={}, **kwargs):
"""Make a PUT request to the Gitlab server.
Expand Down
37 changes: 9 additions & 28 deletions gitlab/base.py
Expand Up @@ -533,31 +533,6 @@ def __ne__(self, other):
return not self.__eq__(other)


class SaveMixin(object):
"""Mixin for RESTObject's that can be updated."""
def save(self, **kwargs):
"""Saves the changes made to the object to the server.
Args:
**kwargs: Extra option to send to the server (e.g. sudo)
The object is updated to match what the server returns.
"""
updated_data = {}
required, optional = self.manager.get_update_attrs()
for attr in required:
# Get everything required, no matter if it's been updated
updated_data[attr] = getattr(self, attr)
# Add the updated attributes
updated_data.update(self._updated_attrs)

# class the manager
obj_id = self.get_id()
server_data = self.manager.update(obj_id, updated_data, **kwargs)
self._updated_attrs = {}
self._attrs.update(server_data)


class RESTObject(object):
"""Represents an object built from server data.
Expand Down Expand Up @@ -618,6 +593,10 @@ def _create_managers(self):
manager = cls(self.manager.gitlab, parent=self)
self.__dict__[attr] = manager

def _update_attrs(self, new_attrs):
self._updated_attrs = {}
self._attrs.update(new_attrs)

def get_id(self):
if self._id_attr is None:
return None
Expand Down Expand Up @@ -674,13 +653,15 @@ def __init__(self, gl, parent=None):
self._parent = parent # for nested managers
self._computed_path = self._compute_path()

def _compute_path(self):
def _compute_path(self, path=None):
if path is None:
path = self._path
if self._parent is None or not hasattr(self, '_from_parent_attrs'):
return self._path
return path

data = {self_attr: getattr(self._parent, parent_attr)
for self_attr, parent_attr in self._from_parent_attrs.items()}
return self._path % data
return path % data

@property
def path(self):
Expand Down
4 changes: 4 additions & 0 deletions gitlab/exceptions.py
Expand Up @@ -39,6 +39,10 @@ class GitlabAuthenticationError(GitlabError):
pass


class GitlabParsingError(GitlabError):
pass


class GitlabConnectionError(GitlabError):
pass

Expand Down
175 changes: 159 additions & 16 deletions gitlab/mixins.py
Expand Up @@ -15,6 +15,7 @@
# You should have received a copy of the GNU Lesser General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.

import gitlab
from gitlab import base


Expand Down Expand Up @@ -70,7 +71,10 @@ def list(self, **kwargs):
list(RESTObjectList).
"""

obj = self.gitlab.http_list(self.path, **kwargs)
# Allow to overwrite the path, handy for custom listings
path = kwargs.pop('path', self.path)

obj = self.gitlab.http_list(path, **kwargs)
if isinstance(obj, list):
return [self._obj_cls(self, item) for item in obj]
else:
Expand Down Expand Up @@ -102,7 +106,7 @@ class RetrieveMixin(ListMixin, GetMixin):


class CreateMixin(object):
def _check_missing_attrs(self, data):
def _check_missing_create_attrs(self, data):
required, optional = self.get_create_attrs()
missing = []
for attr in required:
Expand All @@ -119,13 +123,10 @@ def get_create_attrs(self):
tuple: 2 items: list of required arguments and list of optional
arguments for creation (in that order)
"""
if hasattr(self, '_create_attrs'):
return (self._create_attrs['required'],
self._create_attrs['optional'])
return (tuple(), tuple())
return getattr(self, '_create_attrs', (tuple(), tuple()))

def create(self, data, **kwargs):
"""Created a new object.
"""Creates a new object.
Args:
data (dict): parameters to send to the server to create the
Expand All @@ -136,16 +137,17 @@ def create(self, data, **kwargs):
RESTObject: a new instance of the manage object class build with
the data sent by the server
"""
self._check_missing_attrs(data)
self._check_missing_create_attrs(data)
if hasattr(self, '_sanitize_data'):
data = self._sanitize_data(data, 'create')
server_data = self.gitlab.http_post(self.path, post_data=data,
**kwargs)
# Handle specific URL for creation
path = kwargs.get('path', self.path)
server_data = self.gitlab.http_post(path, post_data=data, **kwargs)
return self._obj_cls(self, server_data)


class UpdateMixin(object):
def _check_missing_attrs(self, data):
def _check_missing_update_attrs(self, data):
required, optional = self.get_update_attrs()
missing = []
for attr in required:
Expand All @@ -162,10 +164,7 @@ def get_update_attrs(self):
tuple: 2 items: list of required arguments and list of optional
arguments for update (in that order)
"""
if hasattr(self, '_update_attrs'):
return (self._update_attrs['required'],
self._update_attrs['optional'])
return (tuple(), tuple())
return getattr(self, '_update_attrs', (tuple(), tuple()))

def update(self, id=None, new_data={}, **kwargs):
"""Update an object on the server.
Expand All @@ -184,9 +183,11 @@ def update(self, id=None, new_data={}, **kwargs):
else:
path = '%s/%s' % (self.path, id)

self._check_missing_attrs(new_data)
self._check_missing_update_attrs(new_data)
if hasattr(self, '_sanitize_data'):
data = self._sanitize_data(new_data, 'update')
else:
data = new_data
server_data = self.gitlab.http_put(path, post_data=data, **kwargs)
return server_data

Expand All @@ -205,3 +206,145 @@ def delete(self, id, **kwargs):

class CRUDMixin(GetMixin, ListMixin, CreateMixin, UpdateMixin, DeleteMixin):
pass


class NoUpdateMixin(GetMixin, ListMixin, CreateMixin, DeleteMixin):
pass


class SaveMixin(object):
"""Mixin for RESTObject's that can be updated."""
def _get_updated_data(self):
updated_data = {}
required, optional = self.manager.get_update_attrs()
for attr in required:
# Get everything required, no matter if it's been updated
updated_data[attr] = getattr(self, attr)
# Add the updated attributes
updated_data.update(self._updated_attrs)

return updated_data

def save(self, **kwargs):
"""Saves the changes made to the object to the server.
Args:
**kwargs: Extra option to send to the server (e.g. sudo)
The object is updated to match what the server returns.
"""
updated_data = self._get_updated_data()

# call the manager
obj_id = self.get_id()
server_data = self.manager.update(obj_id, updated_data, **kwargs)
self._update_attrs(server_data)


class AccessRequestMixin(object):
def approve(self, access_level=gitlab.DEVELOPER_ACCESS, **kwargs):
"""Approve an access request.
Attrs:
access_level (int): The access level for the user.
Raises:
GitlabConnectionError: If the server cannot be reached.
GitlabUpdateError: If the server fails to perform the request.
"""

path = '%s/%s/approve' % (self.manager.path, self.id)
data = {'access_level': access_level}
server_data = self.manager.gitlab.http_put(url, post_data=data,
**kwargs)
self._update_attrs(server_data)


class SubscribableMixin(object):
def subscribe(self, **kwarg):
"""Subscribe to the object notifications.
raises:
gitlabconnectionerror: if the server cannot be reached.
gitlabsubscribeerror: if the subscription cannot be done
"""
path = '%s/%s/subscribe' % (self.manager.path, self.get_id())
server_data = self.manager.gitlab.http_post(path, **kwargs)
self._update_attrs(server_data)

def unsubscribe(self, **kwargs):
"""Unsubscribe from the object notifications.
raises:
gitlabconnectionerror: if the server cannot be reached.
gitlabunsubscribeerror: if the unsubscription cannot be done
"""
path = '%s/%s/unsubscribe' % (self.manager.path, self.get_id())
server_data = self.manager.gitlab.http_post(path, **kwargs)
self._update_attrs(server_data)


class TodoMixin(object):
def todo(self, **kwargs):
"""Create a todo associated to the object.
Raises:
GitlabConnectionError: If the server cannot be reached.
"""
path = '%s/%s/todo' % (self.manager.path, self.get_id())
self.manager.gitlab.http_post(path, **kwargs)


class TimeTrackingMixin(object):
def time_stats(self, **kwargs):
"""Get time stats for the object.
Raises:
GitlabConnectionError: If the server cannot be reached.
"""
path = '%s/%s/time_stats' % (self.manager.path, self.get_id())
return self.manager.gitlab.http_get(path, **kwargs)

def time_estimate(self, duration, **kwargs):
"""Set an estimated time of work for the object.
Args:
duration (str): duration in human format (e.g. 3h30)
Raises:
GitlabConnectionError: If the server cannot be reached.
"""
path = '%s/%s/time_estimate' % (self.manager.path, self.get_id())
data = {'duration': duration}
return self.manager.gitlab.http_post(path, post_data=data, **kwargs)

def reset_time_estimate(self, **kwargs):
"""Resets estimated time for the object to 0 seconds.
Raises:
GitlabConnectionError: If the server cannot be reached.
"""
path = '%s/%s/rest_time_estimate' % (self.manager.path, self.get_id())
return self.manager.gitlab.http_post(path, **kwargs)

def add_spent_time(self, duration, **kwargs):
"""Add time spent working on the object.
Args:
duration (str): duration in human format (e.g. 3h30)
Raises:
GitlabConnectionError: If the server cannot be reached.
"""
path = '%s/%s/add_spent_time' % (self.manager.path, self.get_id())
data = {'duration': duration}
return self.manager.gitlab.http_post(path, post_data=data, **kwargs)

def reset_spent_time(self, **kwargs):
"""Resets the time spent working on the object.
Raises:
GitlabConnectionError: If the server cannot be reached.
"""
path = '%s/%s/reset_spent_time' % (self.manager.path, self.get_id())
return self.manager.gitlab.http_post(path, **kwargs)

0 comments on commit 6be990c

Please sign in to comment.