summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--docs/api-usage.rst92
-rw-r--r--docs/api/gitlab.rst1
-rw-r--r--docs/api/gitlab.v3.rst22
-rw-r--r--gitlab/__init__.py357
-rw-r--r--gitlab/base.py526
-rw-r--r--gitlab/config.py2
-rw-r--r--gitlab/exceptions.py37
-rw-r--r--gitlab/tests/test_cli.py43
-rw-r--r--gitlab/tests/test_gitlab.py615
-rw-r--r--gitlab/tests/test_gitlabobject.py500
-rw-r--r--gitlab/tests/test_manager.py309
-rw-r--r--gitlab/v3/__init__.py0
-rw-r--r--gitlab/v3/cli.py524
-rw-r--r--gitlab/v3/objects.py2389
-rwxr-xr-xtools/build_test_env.sh4
-rw-r--r--tools/cli_test_v3.sh107
-rw-r--r--tools/python_test_v3.py354
-rw-r--r--tools/python_test_v4.py10
-rw-r--r--tox.ini6
19 files changed, 60 insertions, 5838 deletions
diff --git a/docs/api-usage.rst b/docs/api-usage.rst
index d435c31..0882b21 100644
--- a/docs/api-usage.rst
+++ b/docs/api-usage.rst
@@ -2,13 +2,12 @@
Getting started with the API
############################
-python-gitlab supports both GitLab v3 and v4 APIs.
+python-gitlab supports both GitLab v3 and v4 APIs. To use the v3 make sure to
-v3 being deprecated by GitLab, its support in python-gitlab will be minimal.
-The development team will focus on v4.
-
-v4 is the default API used by python-gitlab since version 1.3.0.
+.. note::
+ To use the v3 make sure to install python-gitlab 1.4. Only the v4 API is
+ documented here. See the documentation of earlier version for the v3 API.
``gitlab.Gitlab`` class
=======================
@@ -60,23 +59,6 @@ https://gist.github.com/gpocentek/bd4c3fbf8a6ce226ebddc4aad6b46c0a.
See `issue 380 <https://github.com/python-gitlab/python-gitlab/issues/380>`_
for a detailed discussion.
-API version
-===========
-
-``python-gitlab`` uses the v4 GitLab API by default. Use the ``api_version``
-parameter to switch to v3:
-
-.. code-block:: python
-
- import gitlab
-
- gl = gitlab.Gitlab('http://10.0.0.1', 'JVNSESs8EwWRx5yDxM5q', api_version=3)
-
-.. warning::
-
- The python-gitlab API is not the same for v3 and v4. Make sure to read
- :ref:`switching_to_v4` if you are upgrading from v3.
-
Managers
========
@@ -103,10 +85,10 @@ Examples:
user = gl.users.create(user_data)
print(user)
-You can list the mandatory and optional attributes for object creation
-with the manager's ``get_create_attrs()`` method. It returns 2 tuples, the
-first one is the list of mandatory attributes, the second one the list of
-optional attribute:
+You can list the mandatory and optional attributes for object creation and
+update with the manager's ``get_create_attrs()`` and ``get_update_attrs()``
+methods. They return 2 tuples, the first one is the list of mandatory
+attributes, the second one the list of optional attribute:
.. code-block:: python
@@ -116,19 +98,11 @@ optional attribute:
The attributes of objects are defined upon object creation, and depend on the
GitLab API itself. To list the available information associated with an object
-use the python introspection tools for v3, or the ``attributes`` attribute for
-v4:
+use the ``attributes`` attribute:
.. code-block:: python
project = gl.projects.get(1)
-
- # v3
- print(vars(project))
- # or
- print(project.__dict__)
-
- # v4
print(project.attributes)
Some objects also provide managers to access related GitLab resources:
@@ -171,32 +145,21 @@ The ``gitlab`` package provides some base types.
* ``gitlab.Gitlab`` is the primary class, handling the HTTP requests. It holds
the GitLab URL and authentication information.
-
-For v4 the following types are defined:
-
* ``gitlab.base.RESTObject`` is the base class for all the GitLab v4 objects.
These objects provide an abstraction for GitLab resources (projects, groups,
and so on).
* ``gitlab.base.RESTManager`` is the base class for v4 objects managers,
providing the API to manipulate the resources and their attributes.
-For v3 the following types are defined:
-
-* ``gitlab.base.GitlabObject`` is the base class for all the GitLab v3 objects.
- These objects provide an abstraction for GitLab resources (projects, groups,
- and so on).
-* ``gitlab.base.BaseManager`` is the base class for v3 objects managers,
- providing the API to manipulate the resources and their attributes.
+Lazy objects
+============
-Lazy objects (v4 only)
-======================
-
-To avoid useless calls to the server API, you can create lazy objects. These
+To avoid useless API calls to the server you can create lazy objects. These
objects are created locally using a known ID, and give access to other managers
and methods.
The following example will only make one API call to the GitLab server to star
-a project:
+a project (the previous example used 2 API calls):
.. code-block:: python
@@ -214,9 +177,9 @@ listing methods support the ``page`` and ``per_page`` parameters:
ten_first_groups = gl.groups.list(page=1, per_page=10)
-.. note::
+.. warning::
- The first page is page 1, not page 0, except for project commits in v3 API.
+ The first page is page 1, not page 0.
By default GitLab does not return the complete list of items. Use the ``all``
parameter to get all the items when using listing methods:
@@ -226,18 +189,9 @@ parameter to get all the items when using listing methods:
all_groups = gl.groups.list(all=True)
all_owned_projects = gl.projects.owned(all=True)
-.. warning::
-
- With API v3 python-gitlab will iterate over the list by calling the
- corresponding API multiple times. This might take some time if you have a
- lot of items to retrieve. This might also consume a lot of memory as all the
- items will be stored in RAM. If you're encountering the python recursion
- limit exception, use ``safe_all=True`` to stop pagination automatically if
- the recursion limit is hit.
-
-With API v4, ``list()`` methods can also return a generator object which will
-handle the next calls to the API when required. This is the recommended way to
-iterate through a large number of items:
+``list()`` methods can also return a generator object which will handle the
+next calls to the API when required. This is the recommended way to iterate
+through a large number of items:
.. code-block:: python
@@ -331,12 +285,12 @@ http://docs.python-requests.org/en/master/user/advanced/#client-side-certificate
Rate limits
-----------
-python-gitlab will obey the rate limit of the GitLab server by default.
-On receiving a 429 response (Too Many Requests), python-gitlab will sleep for the amount of time
-in the Retry-After header, that GitLab sends back.
+python-gitlab obeys the rate limit of the GitLab server by default. On
+receiving a 429 response (Too Many Requests), python-gitlab sleeps for the
+amount of time in the Retry-After header that GitLab sends back.
-If you don't want to wait, you can disable the rate-limiting feature, by supplying the
-``obey_rate_limit`` argument.
+If you don't want to wait, you can disable the rate-limiting feature, by
+supplying the ``obey_rate_limit`` argument.
.. code-block:: python
diff --git a/docs/api/gitlab.rst b/docs/api/gitlab.rst
index e75f843..1dabad2 100644
--- a/docs/api/gitlab.rst
+++ b/docs/api/gitlab.rst
@@ -6,7 +6,6 @@ Subpackages
.. toctree::
- gitlab.v3
gitlab.v4
Submodules
diff --git a/docs/api/gitlab.v3.rst b/docs/api/gitlab.v3.rst
deleted file mode 100644
index 61879bc..0000000
--- a/docs/api/gitlab.v3.rst
+++ /dev/null
@@ -1,22 +0,0 @@
-gitlab.v3 package
-=================
-
-Submodules
-----------
-
-gitlab.v3.objects module
-------------------------
-
-.. automodule:: gitlab.v3.objects
- :members:
- :undoc-members:
- :show-inheritance:
-
-
-Module contents
----------------
-
-.. automodule:: gitlab.v3
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/gitlab/__init__.py b/gitlab/__init__.py
index f0eb136..af38680 100644
--- a/gitlab/__init__.py
+++ b/gitlab/__init__.py
@@ -19,10 +19,6 @@
from __future__ import print_function
from __future__ import absolute_import
import importlib
-import inspect
-import itertools
-import json
-import re
import time
import warnings
@@ -32,7 +28,6 @@ import six
import gitlab.config
from gitlab.const import * # noqa
from gitlab.exceptions import * # noqa
-from gitlab.v3.objects import * # noqa
__title__ = 'python-gitlab'
__version__ = '1.4.0'
@@ -69,7 +64,7 @@ class Gitlab(object):
timeout (float): Timeout to use for requests to the GitLab server.
http_username (str): Username for HTTP authentication
http_password (str): Password for HTTP authentication
- api_version (str): Gitlab API version to use (3 or 4)
+ api_version (str): Gitlab API version to use (support for 4 only)
"""
def __init__(self, url, private_token=None, oauth_token=None, email=None,
@@ -123,31 +118,11 @@ class Gitlab(object):
self.snippets = objects.SnippetManager(self)
self.users = objects.UserManager(self)
self.todos = objects.TodoManager(self)
- if self._api_version == '3':
- self.teams = objects.TeamManager(self)
- else:
- self.dockerfiles = objects.DockerfileManager(self)
- self.events = objects.EventManager(self)
- self.features = objects.FeatureManager(self)
- self.pagesdomains = objects.PagesDomainManager(self)
- self.user_activities = objects.UserActivitiesManager(self)
-
- if self._api_version == '3':
- # build the "submanagers"
- for parent_cls in six.itervalues(vars(objects)):
- if (not inspect.isclass(parent_cls)
- or not issubclass(parent_cls, objects.GitlabObject)
- or parent_cls == objects.CurrentUser):
- continue
-
- if not parent_cls.managers:
- continue
-
- for var, cls_name, attrs in parent_cls.managers:
- prefix = self._cls_to_manager_prefix(parent_cls)
- var_name = '%s_%s' % (prefix, var)
- manager = getattr(objects, cls_name)(self)
- setattr(self, var_name, manager)
+ self.dockerfiles = objects.DockerfileManager(self)
+ self.events = objects.EventManager(self)
+ self.features = objects.FeatureManager(self)
+ self.pagesdomains = objects.PagesDomainManager(self)
+ self.user_activities = objects.UserActivitiesManager(self)
def __enter__(self):
return self
@@ -178,17 +153,9 @@ class Gitlab(object):
@property
def api_version(self):
- """The API version used (3 or 4)."""
+ """The API version used (4 only)."""
return self._api_version
- def _cls_to_manager_prefix(self, cls):
- # Manage bad naming decisions
- camel_case = (cls.__name__
- .replace('NotificationSettings', 'Notificationsettings')
- .replace('MergeRequest', 'Mergerequest')
- .replace('AccessRequest', 'Accessrequest'))
- return re.sub(r'(.)([A-Z])', r'\1_\2', camel_case).lower()
-
@staticmethod
def from_config(gitlab_id=None, config_files=None):
"""Create a Gitlab connection from configuration files.
@@ -227,23 +194,14 @@ class Gitlab(object):
def _credentials_auth(self):
data = {'email': self.email, 'password': self.password}
- if self.api_version == '3':
- r = self._raw_post('/session', json.dumps(data),
- content_type='application/json')
- raise_error_from_response(r, GitlabAuthenticationError, 201)
- self.user = self._objects.CurrentUser(self, r.json())
- else:
- r = self.http_post('/session', data)
- manager = self._objects.CurrentUserManager(self)
- self.user = self._objects.CurrentUser(manager, r)
+ r = self.http_post('/session', data)
+ manager = self._objects.CurrentUserManager(self)
+ self.user = self._objects.CurrentUser(manager, r)
self.private_token = self.user.private_token
self._set_auth_info()
def _token_auth(self):
- if self.api_version == '3':
- self.user = self._objects.CurrentUser(self)
- else:
- self.user = self._objects.CurrentUserManager(self).get()
+ self.user = self._objects.CurrentUserManager(self).get()
def version(self):
"""Returns the version and revision of the gitlab server.
@@ -252,18 +210,16 @@ class Gitlab(object):
object.
Returns:
- tuple (str, str): The server version and server revision, or
+ tuple (str, str): The server version and server revision.
('unknown', 'unknwown') if the server doesn't
- support this API call (gitlab < 8.13.0)
+ perform as expected.
"""
if self._server_version is None:
- r = self._raw_get('/version')
try:
- raise_error_from_response(r, GitlabGetError, 200)
- data = r.json()
+ data = self.http_get('/version')
self._server_version = data['version']
self._server_revision = data['revision']
- except GitlabGetError:
+ except Exception:
self._server_version = self._server_revision = 'unknown'
return self._server_version, self._server_revision
@@ -279,13 +235,7 @@ class Gitlab(object):
if hasattr(obj, attr):
url_attr = attr
obj_url = getattr(obj, url_attr)
-
- # TODO(gpocentek): the following will need an update when we have
- # object with both urlPlural and _ACTION_url attributes
- if id_ is None and obj._urlPlural is not None:
- url = obj._urlPlural % args
- else:
- url = obj_url % args
+ url = obj_url % args
if id_ is not None:
return '%s/%s' % (url, str(id_))
@@ -345,287 +295,12 @@ class Gitlab(object):
'verify': self.ssl_verify
}
- def _raw_get(self, path_, content_type=None, streamed=False, **kwargs):
- if path_.startswith('http://') or path_.startswith('https://'):
- url = path_
- else:
- url = '%s%s' % (self._url, path_)
-
- opts = self._get_session_opts(content_type)
- try:
- return self.session.get(url, params=kwargs, stream=streamed,
- **opts)
- except Exception as e:
- raise GitlabConnectionError(
- "Can't connect to GitLab server (%s)" % e)
-
- def _raw_list(self, path_, cls, **kwargs):
- params = kwargs.copy()
-
- catch_recursion_limit = kwargs.get('safe_all', False)
- get_all_results = (kwargs.get('all', False) is True
- or catch_recursion_limit)
-
- # Remove these keys to avoid breaking the listing (urls will get too
- # long otherwise)
- for key in ['all', 'next_url', 'safe_all']:
- if key in params:
- del params[key]
-
- r = self._raw_get(path_, **params)
- raise_error_from_response(r, GitlabListError)
-
- # These attributes are not needed in the object
- for key in ['page', 'per_page', 'sudo']:
- if key in params:
- del params[key]
-
- # Add _from_api manually, because we are not creating objects
- # through normal path_
- params['_from_api'] = True
-
- results = [cls(self, item, **params) for item in r.json()
- if item is not None]
- try:
- if ('next' in r.links and 'url' in r.links['next']
- and get_all_results):
- args = kwargs.copy()
- args['next_url'] = r.links['next']['url']
- results.extend(self.list(cls, **args))
- except Exception as e:
- # Catch the recursion limit exception if the 'safe_all'
- # kwarg was provided
- if not (catch_recursion_limit and
- "maximum recursion depth exceeded" in str(e)):
- raise e
-
- return results
-
- def _raw_post(self, path_, data=None, content_type=None,
- files=None, **kwargs):
- url = '%s%s' % (self._url, path_)
- opts = self._get_session_opts(content_type)
- try:
- return self.session.post(url, params=kwargs, data=data,
- files=files, **opts)
- except Exception as e:
- raise GitlabConnectionError(
- "Can't connect to GitLab server (%s)" % e)
-
- def _raw_put(self, path_, data=None, content_type=None, **kwargs):
- url = '%s%s' % (self._url, path_)
- opts = self._get_session_opts(content_type)
- try:
- return self.session.put(url, data=data, params=kwargs, **opts)
- except Exception as e:
- raise GitlabConnectionError(
- "Can't connect to GitLab server (%s)" % e)
-
- def _raw_delete(self, path_, content_type=None, **kwargs):
- url = '%s%s' % (self._url, path_)
- opts = self._get_session_opts(content_type)
- try:
- return self.session.delete(url, params=kwargs, **opts)
- except Exception as e:
- raise GitlabConnectionError(
- "Can't connect to GitLab server (%s)" % e)
-
- def list(self, obj_class, **kwargs):
- """Request the listing of GitLab resources.
-
- Args:
- obj_class (object): The class of resource to request.
- **kwargs: Additional arguments to send to GitLab.
-
- Returns:
- list(obj_class): A list of objects of class `obj_class`.
-
- Raises:
- GitlabConnectionError: If the server cannot be reached.
- GitlabListError: If the server fails to perform the request.
- """
- missing = []
- for k in itertools.chain(obj_class.requiredUrlAttrs,
- obj_class.requiredListAttrs):
- if k not in kwargs:
- missing.append(k)
- if missing:
- raise GitlabListError('Missing attribute(s): %s' %
- ", ".join(missing))
-
- url = self._construct_url(id_=None, obj=obj_class, parameters=kwargs)
-
- return self._raw_list(url, obj_class, **kwargs)
-
- def get(self, obj_class, id=None, **kwargs):
- """Request a GitLab resources.
-
- Args:
- obj_class (object): The class of resource to request.
- id: The object ID.
- **kwargs: Additional arguments to send to GitLab.
-
- Returns:
- obj_class: An object of class `obj_class`.
-
- Raises:
- GitlabConnectionError: If the server cannot be reached.
- GitlabGetError: If the server fails to perform the request.
- """
- missing = []
- for k in itertools.chain(obj_class.requiredUrlAttrs,
- obj_class.requiredGetAttrs):
- if k not in kwargs:
- missing.append(k)
- if missing:
- raise GitlabGetError('Missing attribute(s): %s' %
- ", ".join(missing))
-
- url = self._construct_url(id_=_sanitize(id), obj=obj_class,
- parameters=kwargs)
-
- r = self._raw_get(url, **kwargs)
- raise_error_from_response(r, GitlabGetError)
- return r.json()
-
- def delete(self, obj, id=None, **kwargs):
- """Delete an object on the GitLab server.
-
- Args:
- obj (object or id): The object, or the class of the object to
- delete. If it is the class, the id of the object must be
- specified as the `id` arguments.
- id: ID of the object to remove. Required if `obj` is a class.
- **kwargs: Additional arguments to send to GitLab.
-
- Returns:
- bool: True if the operation succeeds.
-
- Raises:
- GitlabConnectionError: If the server cannot be reached.
- GitlabDeleteError: If the server fails to perform the request.
- """
- if inspect.isclass(obj):
- if not issubclass(obj, GitlabObject):
- raise GitlabError("Invalid class: %s" % obj)
-
- params = {obj.idAttr: id if id else getattr(obj, obj.idAttr)}
- params.update(kwargs)
-
- missing = []
- for k in itertools.chain(obj.requiredUrlAttrs,
- obj.requiredDeleteAttrs):
- if k not in params:
- try:
- params[k] = getattr(obj, k)
- except KeyError:
- missing.append(k)
- if missing:
- raise GitlabDeleteError('Missing attribute(s): %s' %
- ", ".join(missing))
-
- obj_id = params[obj.idAttr] if obj._id_in_delete_url else None
- url = self._construct_url(id_=obj_id, obj=obj, parameters=params)
-
- if obj._id_in_delete_url:
- # The ID is already built, no need to add it as extra key in query
- # string
- params.pop(obj.idAttr)
-
- r = self._raw_delete(url, **params)
- raise_error_from_response(r, GitlabDeleteError,
- expected_code=[200, 202, 204])
- return True
-
- def create(self, obj, **kwargs):
- """Create an object on the GitLab server.
-
- The object class and attributes define the request to be made on the
- GitLab server.
-
- Args:
- obj (object): The object to create.
- **kwargs: Additional arguments to send to GitLab.
-
- Returns:
- str: A json representation of the object as returned by the GitLab
- server
-
- Raises:
- GitlabConnectionError: If the server cannot be reached.
- GitlabCreateError: If the server fails to perform the request.
- """
- params = obj.__dict__.copy()
- params.update(kwargs)
- missing = []
- for k in itertools.chain(obj.requiredUrlAttrs,
- obj.requiredCreateAttrs):
- if k not in params:
- missing.append(k)
- if missing:
- raise GitlabCreateError('Missing attribute(s): %s' %
- ", ".join(missing))
-
- url = self._construct_url(id_=None, obj=obj, parameters=params,
- action='create')
-
- # build data that can really be sent to server
- data = obj._data_for_gitlab(extra_parameters=kwargs)
-
- r = self._raw_post(url, data=data, content_type='application/json')
- raise_error_from_response(r, GitlabCreateError, 201)
- return r.json()
-
- def update(self, obj, **kwargs):
- """Update an object on the GitLab server.
-
- The object class and attributes define the request to be made on the
- GitLab server.
-
- Args:
- obj (object): The object to create.
- **kwargs: Additional arguments to send to GitLab.
-
- Returns:
- str: A json representation of the object as returned by the GitLab
- server
-
- Raises:
- GitlabConnectionError: If the server cannot be reached.
- GitlabUpdateError: If the server fails to perform the request.
- """
- params = obj.__dict__.copy()
- params.update(kwargs)
- missing = []
- if obj.requiredUpdateAttrs or obj.optionalUpdateAttrs:
- required_attrs = obj.requiredUpdateAttrs
- else:
- required_attrs = obj.requiredCreateAttrs
- for k in itertools.chain(obj.requiredUrlAttrs, required_attrs):
- if k not in params:
- missing.append(k)
- if missing:
- raise GitlabUpdateError('Missing attribute(s): %s' %
- ", ".join(missing))
- obj_id = params[obj.idAttr] if obj._id_in_update_url else None
- url = self._construct_url(id_=obj_id, obj=obj, parameters=params)
-
- # build data that can really be sent to server
- data = obj._data_for_gitlab(extra_parameters=kwargs, update=True)
-
- r = self._raw_put(url, data=data, content_type='application/json')
- raise_error_from_response(r, GitlabUpdateError)
- return r.json()
-
def _build_url(self, path):
"""Returns the full url from path.
If path is already a url, return it unchanged. If it's a path, append
it to the stored url.
- This is a low-level method, different from _construct_url _build_url
- have no knowledge of GitlabObject's.
-
Returns:
str: The full URL
"""
diff --git a/gitlab/base.py b/gitlab/base.py
index fd79c53..7324c31 100644
--- a/gitlab/base.py
+++ b/gitlab/base.py
@@ -15,533 +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 copy
import importlib
-import itertools
-import json
-import sys
-
-import six
-
-import gitlab
-from gitlab.exceptions import * # noqa
-
-
-class jsonEncoder(json.JSONEncoder):
- def default(self, obj):
- if isinstance(obj, GitlabObject):
- return obj.as_dict()
- elif isinstance(obj, gitlab.Gitlab):
- return {'url': obj._url}
- return json.JSONEncoder.default(self, obj)
-
-
-class BaseManager(object):
- """Base manager class for API operations.
-
- Managers provide method to manage GitLab API objects, such as retrieval,
- listing, creation.
-
- Inherited class must define the ``obj_cls`` attribute.
-
- Attributes:
- obj_cls (class): class of objects wrapped by this manager.
- """
-
- obj_cls = None
-
- def __init__(self, gl, parent=None, args=[]):
- """Constructs a manager.
-
- Args:
- gl (gitlab.Gitlab): Gitlab object referencing the GitLab server.
- parent (Optional[Manager]): A parent manager.
- args (list): A list of tuples defining a link between the
- parent/child attributes.
-
- Raises:
- AttributeError: If `obj_cls` is None.
- """
- self.gitlab = gl
- self.args = args
- self.parent = parent
-
- if self.obj_cls is None:
- raise AttributeError("obj_cls must be defined")
-
- def _set_parent_args(self, **kwargs):
- args = copy.copy(kwargs)
- if self.parent is not None:
- for attr, parent_attr in self.args:
- args.setdefault(attr, getattr(self.parent, parent_attr))
-
- return args
-
- def get(self, id=None, **kwargs):
- """Get a GitLab object.
-
- Args:
- id: ID of the object to retrieve.
- **kwargs: Additional arguments to send to GitLab.
-
- Returns:
- object: An object of class `obj_cls`.
-
- Raises:
- NotImplementedError: If objects cannot be retrieved.
- GitlabGetError: If the server fails to perform the request.
- """
- args = self._set_parent_args(**kwargs)
- if not self.obj_cls.canGet:
- raise NotImplementedError
- if id is None and self.obj_cls.getRequiresId is True:
- raise ValueError('The id argument must be defined.')
- return self.obj_cls.get(self.gitlab, id, **args)
-
- def list(self, **kwargs):
- """Get a list of GitLab objects.
-
- Args:
- **kwargs: Additional arguments to send to GitLab.
-
- Returns:
- list[object]: A list of `obj_cls` objects.
-
- Raises:
- NotImplementedError: If objects cannot be listed.
- GitlabListError: If the server fails to perform the request.
- """
- args = self._set_parent_args(**kwargs)
- if not self.obj_cls.canList:
- raise NotImplementedError
- return self.obj_cls.list(self.gitlab, **args)
-
- def create(self, data, **kwargs):
- """Create a new object of class `obj_cls`.
-
- Args:
- data (dict): The parameters to send to the GitLab server to create
- the object. Required and optional arguments are defined in the
- `requiredCreateAttrs` and `optionalCreateAttrs` of the
- `obj_cls` class.
- **kwargs: Additional arguments to send to GitLab.
-
- Returns:
- object: A newly create `obj_cls` object.
-
- Raises:
- NotImplementedError: If objects cannot be created.
- GitlabCreateError: If the server fails to perform the request.
- """
- args = self._set_parent_args(**kwargs)
- if not self.obj_cls.canCreate:
- raise NotImplementedError
- return self.obj_cls.create(self.gitlab, data, **args)
-
- def delete(self, id, **kwargs):
- """Delete a GitLab object.
-
- Args:
- id: ID of the object to delete.
-
- Raises:
- NotImplementedError: If objects cannot be deleted.
- GitlabDeleteError: If the server fails to perform the request.
- """
- args = self._set_parent_args(**kwargs)
- if not self.obj_cls.canDelete:
- raise NotImplementedError
- self.gitlab.delete(self.obj_cls, id, **args)
-
-
-class GitlabObject(object):
- """Base class for all classes that interface with GitLab."""
- #: Url to use in GitLab for this object
- _url = None
- # Some objects (e.g. merge requests) have different urls for singular and
- # plural
- _urlPlural = None
- _id_in_delete_url = True
- _id_in_update_url = True
- _constructorTypes = None
-
- #: Tells if GitLab-api allows retrieving single objects.
- canGet = True
- #: Tells if GitLab-api allows listing of objects.
- canList = True
- #: Tells if GitLab-api allows creation of new objects.
- canCreate = True
- #: Tells if GitLab-api allows updating object.
- canUpdate = True
- #: Tells if GitLab-api allows deleting object.
- canDelete = True
- #: Attributes that are required for constructing url.
- requiredUrlAttrs = []
- #: Attributes that are required when retrieving list of objects.
- requiredListAttrs = []
- #: Attributes that are optional when retrieving list of objects.
- optionalListAttrs = []
- #: Attributes that are optional when retrieving single object.
- optionalGetAttrs = []
- #: Attributes that are required when retrieving single object.
- requiredGetAttrs = []
- #: Attributes that are required when deleting object.
- requiredDeleteAttrs = []
- #: Attributes that are required when creating a new object.
- requiredCreateAttrs = []
- #: Attributes that are optional when creating a new object.
- optionalCreateAttrs = []
- #: Attributes that are required when updating an object.
- requiredUpdateAttrs = []
- #: Attributes that are optional when updating an object.
- optionalUpdateAttrs = []
- #: Whether the object ID is required in the GET url.
- getRequiresId = True
- #: List of managers to create.
- managers = []
- #: Name of the identifier of an object.
- idAttr = 'id'
- #: Attribute to use as ID when displaying the object.
- shortPrintAttr = None
-
- def _data_for_gitlab(self, extra_parameters={}, update=False,
- as_json=True):
- data = {}
- if update and (self.requiredUpdateAttrs or self.optionalUpdateAttrs):
- attributes = itertools.chain(self.requiredUpdateAttrs,
- self.optionalUpdateAttrs)
- else:
- attributes = itertools.chain(self.requiredCreateAttrs,
- self.optionalCreateAttrs)
- attributes = list(attributes) + ['sudo', 'page', 'per_page']
- for attribute in attributes:
- if hasattr(self, attribute):
- value = getattr(self, attribute)
- # labels need to be sent as a comma-separated list
- if attribute == 'labels' and isinstance(value, list):
- value = ", ".join(value)
- elif attribute == 'sudo':
- value = str(value)
- data[attribute] = value
-
- data.update(extra_parameters)
-
- return json.dumps(data) if as_json else data
-
- @classmethod
- def list(cls, gl, **kwargs):
- """Retrieve a list of objects from GitLab.
-
- Args:
- gl (gitlab.Gitlab): Gitlab object referencing the GitLab server.
- per_page (int): Maximum number of items to return.
- page (int): ID of the page to return when using pagination.
-
- Returns:
- list[object]: A list of objects.
-
- Raises:
- NotImplementedError: If objects can't be listed.
- GitlabListError: If the server cannot perform the request.
- """
- if not cls.canList:
- raise NotImplementedError
-
- if not cls._url:
- raise NotImplementedError
-
- return gl.list(cls, **kwargs)
-
- @classmethod
- def get(cls, gl, id, **kwargs):
- """Retrieve a single object.
-
- Args:
- gl (gitlab.Gitlab): Gitlab object referencing the GitLab server.
- id (int or str): ID of the object to retrieve.
-
- Returns:
- object: The found GitLab object.
-
- Raises:
- NotImplementedError: If objects can't be retrieved.
- GitlabGetError: If the server cannot perform the request.
- """
-
- if cls.canGet is False:
- raise NotImplementedError
- elif cls.canGet is True:
- return cls(gl, id, **kwargs)
- elif cls.canGet == 'from_list':
- for obj in cls.list(gl, **kwargs):
- obj_id = getattr(obj, obj.idAttr)
- if str(obj_id) == str(id):
- return obj
-
- raise GitlabGetError("Object not found")
-
- def _get_object(self, k, v, **kwargs):
- if self._constructorTypes and k in self._constructorTypes:
- cls = getattr(self._module, self._constructorTypes[k])
- return cls(self.gitlab, v, **kwargs)
- else:
- return v
-
- def _set_from_dict(self, data, **kwargs):
- if not hasattr(data, 'items'):
- return
-
- for k, v in data.items():
- # If a k attribute already exists and is a Manager, do nothing (see
- # https://github.com/python-gitlab/python-gitlab/issues/209)
- if isinstance(getattr(self, k, None), BaseManager):
- continue
-
- if isinstance(v, list):
- self.__dict__[k] = []
- for i in v:
- self.__dict__[k].append(self._get_object(k, i, **kwargs))
- elif v is None:
- self.__dict__[k] = None
- else:
- self.__dict__[k] = self._get_object(k, v, **kwargs)
-
- def _create(self, **kwargs):
- if not self.canCreate:
- raise NotImplementedError
-
- json = self.gitlab.create(self, **kwargs)
- self._set_from_dict(json)
- self._from_api = True
-
- def _update(self, **kwargs):
- if not self.canUpdate:
- raise NotImplementedError
-
- json = self.gitlab.update(self, **kwargs)
- self._set_from_dict(json)
-
- def save(self, **kwargs):
- if self._from_api:
- self._update(**kwargs)
- else:
- self._create(**kwargs)
-
- def delete(self, **kwargs):
- if not self.canDelete:
- raise NotImplementedError
-
- if not self._from_api:
- raise GitlabDeleteError("Object not yet created")
-
- return self.gitlab.delete(self, **kwargs)
-
- @classmethod
- def create(cls, gl, data, **kwargs):
- """Create an object.
-
- Args:
- gl (gitlab.Gitlab): Gitlab object referencing the GitLab server.
- data (dict): The data used to define the object.
-
- Returns:
- object: The new object.
-
- Raises:
- NotImplementedError: If objects can't be created.
- GitlabCreateError: If the server cannot perform the request.
- """
- if not cls.canCreate:
- raise NotImplementedError
-
- obj = cls(gl, data, **kwargs)
- obj.save()
-
- return obj
-
- def __init__(self, gl, data=None, **kwargs):
- """Constructs a new object.
-
- Do not use this method. Use the `get` or `create` class methods
- instead.
-
- Args:
- gl (gitlab.Gitlab): Gitlab object referencing the GitLab server.
- data: If `data` is a dict, create a new object using the
- information. If it is an int or a string, get a GitLab object
- from an API request.
- **kwargs: Additional arguments to send to GitLab.
- """
- self._from_api = False
- #: (gitlab.Gitlab): Gitlab connection.
- self.gitlab = gl
-
- # store the module in which the object has been created (v3/v4) to be
- # able to reference other objects from the same module
- self._module = importlib.import_module(self.__module__)
-
- if (data is None or isinstance(data, six.integer_types) or
- isinstance(data, six.string_types)):
- if not self.canGet:
- raise NotImplementedError
- data = self.gitlab.get(self.__class__, data, **kwargs)
- self._from_api = True
-
- # the API returned a list because custom kwargs where used
- # instead of the id to request an object. Usually parameters
- # other than an id return ambiguous results. However in the
- # gitlab universe iids together with a project_id are
- # unambiguous for merge requests and issues, too.
- # So if there is only one element we can use it as our data
- # source.
- if 'iid' in kwargs and isinstance(data, list):
- if len(data) < 1:
- raise GitlabGetError('Not found')
- elif len(data) == 1:
- data = data[0]
- else:
- raise GitlabGetError('Impossible! You found multiple'
- ' elements with the same iid.')
-
- self._set_from_dict(data, **kwargs)
-
- if kwargs:
- for k, v in kwargs.items():
- # Don't overwrite attributes returned by the server (#171)
- if k not in self.__dict__ or not self.__dict__[k]:
- self.__dict__[k] = v
-
- # Special handling for api-objects that don't have id-number in api
- # responses. Currently only Labels and Files
- if not hasattr(self, "id"):
- self.id = None
-
- def __getstate__(self):
- state = self.__dict__.copy()
- module = state.pop('_module')
- state['_module_name'] = module.__name__
- return state
-
- def __setstate__(self, state):
- module_name = state.pop('_module_name')
- self.__dict__.update(state)
- self._module = importlib.import_module(module_name)
-
- def _set_manager(self, var, cls, attrs):
- manager = cls(self.gitlab, self, attrs)
- setattr(self, var, manager)
-
- def __getattr__(self, name):
- # build a manager if it doesn't exist yet
- for var, cls, attrs in self.managers:
- if var != name:
- continue
- # Build the full class path if needed
- if isinstance(cls, six.string_types):
- cls = getattr(self._module, cls)
- self._set_manager(var, cls, attrs)
- return getattr(self, var)
-
- raise AttributeError(name)
-
- def __str__(self):
- return '%s => %s' % (type(self), str(self.__dict__))
-
- def __repr__(self):
- return '<%s %s:%s>' % (self.__class__.__name__,
- self.idAttr,
- getattr(self, self.idAttr))
-
- def display(self, pretty):
- if pretty:
- self.pretty_print()
- else:
- self.short_print()
-
- def short_print(self, depth=0):
- """Print the object on the standard output (verbose).
-
- Args:
- depth (int): Used internaly for recursive call.
- """
- id = self.__dict__[self.idAttr]
- print("%s%s: %s" % (" " * depth * 2, self.idAttr, id))
- if self.shortPrintAttr:
- print("%s%s: %s" % (" " * depth * 2,
- self.shortPrintAttr.replace('_', '-'),
- self.__dict__[self.shortPrintAttr]))
-
- @staticmethod
- def _get_display_encoding():
- return sys.stdout.encoding or sys.getdefaultencoding()
-
- @staticmethod
- def _obj_to_str(obj):
- if isinstance(obj, dict):
- s = ", ".join(["%s: %s" %
- (x, GitlabObject._obj_to_str(y))
- for (x, y) in obj.items()])
- return "{ %s }" % s
- elif isinstance(obj, list):
- s = ", ".join([GitlabObject._obj_to_str(x) for x in obj])
- return "[ %s ]" % s
- elif six.PY2 and isinstance(obj, six.text_type):
- return obj.encode(GitlabObject._get_display_encoding(), "replace")
- else:
- return str(obj)
-
- def pretty_print(self, depth=0):
- """Print the object on the standard output (verbose).
-
- Args:
- depth (int): Used internaly for recursive call.
- """
- id = self.__dict__[self.idAttr]
- print("%s%s: %s" % (" " * depth * 2, self.idAttr, id))
- for k in sorted(self.__dict__.keys()):
- if k in (self.idAttr, 'id', 'gitlab'):
- continue
- if k[0] == '_':
- continue
- v = self.__dict__[k]
- pretty_k = k.replace('_', '-')
- if six.PY2:
- pretty_k = pretty_k.encode(
- GitlabObject._get_display_encoding(), "replace")
- if isinstance(v, GitlabObject):
- if depth == 0:
- print("%s:" % pretty_k)
- v.pretty_print(1)
- else:
- print("%s: %s" % (pretty_k, v.id))
- elif isinstance(v, BaseManager):
- continue
- else:
- if hasattr(v, __name__) and v.__name__ == 'Gitlab':
- continue
- v = GitlabObject._obj_to_str(v)
- print("%s%s: %s" % (" " * depth * 2, pretty_k, v))
-
- def json(self):
- """Dump the object as json.
-
- Returns:
- str: The json string.
- """
- return json.dumps(self, cls=jsonEncoder)
-
- def as_dict(self):
- """Dump the object as a dict."""
- return {k: v for k, v in six.iteritems(self.__dict__)
- if (not isinstance(v, BaseManager) and not k[0] == '_')}
-
- def __eq__(self, other):
- if type(other) is type(self):
- return self.as_dict() == other.as_dict()
- return False
-
- def __ne__(self, other):
- return not self.__eq__(other)
class RESTObject(object):
diff --git a/gitlab/config.py b/gitlab/config.py
index 0f4c424..c3fcf70 100644
--- a/gitlab/config.py
+++ b/gitlab/config.py
@@ -137,6 +137,6 @@ class GitlabConfigParser(object):
self.api_version = self._config.get(self.gitlab_id, 'api_version')
except Exception:
pass
- if self.api_version not in ('3', '4'):
+ if self.api_version not in ('4',):
raise GitlabDataError("Unsupported API version: %s" %
self.api_version)
diff --git a/gitlab/exceptions.py b/gitlab/exceptions.py
index 5825d23..744890f 100644
--- a/gitlab/exceptions.py
+++ b/gitlab/exceptions.py
@@ -197,43 +197,6 @@ class GitlabOwnershipError(GitlabOperationError):
pass
-def raise_error_from_response(response, error, expected_code=200):
- """Tries to parse gitlab error message from response and raises error.
-
- Do nothing if the response status is the expected one.
-
- If response status code is 401, raises instead GitlabAuthenticationError.
-
- Args:
- response: requests response object
- error: Error-class or dict {return-code => class} of possible error
- class to raise. Should be inherited from GitLabError
- """
-
- if isinstance(expected_code, int):
- expected_codes = [expected_code]
- else:
- expected_codes = expected_code
-
- if response.status_code in expected_codes:
- return
-
- try:
- message = response.json()['message']
- except (KeyError, ValueError, TypeError):
- message = response.content
-
- if isinstance(error, dict):
- error = error.get(response.status_code, GitlabOperationError)
- else:
- if response.status_code == 401:
- error = GitlabAuthenticationError
-
- raise error(error_message=message,
- response_code=response.status_code,
- response_body=response.content)
-
-
def on_http_error(error):
"""Manage GitlabHttpError exceptions.
diff --git a/gitlab/tests/test_cli.py b/gitlab/tests/test_cli.py
index a39ef96..034beed 100644
--- a/gitlab/tests/test_cli.py
+++ b/gitlab/tests/test_cli.py
@@ -23,14 +23,12 @@ import argparse
import os
import tempfile
-import six
try:
import unittest
except ImportError:
import unittest2 as unittest
from gitlab import cli
-import gitlab.v3.cli
import gitlab.v4.cli
@@ -121,44 +119,3 @@ class TestV4CLI(unittest.TestCase):
actions = user_subparsers.choices['create']._option_string_actions
self.assertFalse(actions['--description'].required)
self.assertTrue(actions['--name'].required)
-
-
-class TestV3CLI(unittest.TestCase):
- def test_parse_args(self):
- parser = cli._get_parser(gitlab.v3.cli)
- args = parser.parse_args(['project', 'list'])
- self.assertEqual(args.what, 'project')
- self.assertEqual(args.action, 'list')
-
- def test_parser(self):
- parser = cli._get_parser(gitlab.v3.cli)
- subparsers = None
- for action in parser._actions:
- if type(action) == argparse._SubParsersAction:
- subparsers = action
- break
- self.assertIsNotNone(subparsers)
- self.assertIn('user', subparsers.choices)
-
- user_subparsers = None
- for action in subparsers.choices['user']._actions:
- if type(action) == argparse._SubParsersAction:
- user_subparsers = action
- break
- self.assertIsNotNone(user_subparsers)
- self.assertIn('list', user_subparsers.choices)
- self.assertIn('get', user_subparsers.choices)
- self.assertIn('delete', user_subparsers.choices)
- self.assertIn('update', user_subparsers.choices)
- self.assertIn('create', user_subparsers.choices)
- self.assertIn('block', user_subparsers.choices)
- self.assertIn('unblock', user_subparsers.choices)
-
- actions = user_subparsers.choices['create']._option_string_actions
- self.assertFalse(actions['--twitter'].required)
- self.assertTrue(actions['--username'].required)
-
- def test_extra_actions(self):
- for cls, data in six.iteritems(gitlab.v3.cli.EXTRA_ACTIONS):
- for key in data:
- self.assertIsInstance(data[key], dict)
diff --git a/gitlab/tests/test_gitlab.py b/gitlab/tests/test_gitlab.py
index 1a1f3d8..daa2694 100644
--- a/gitlab/tests/test_gitlab.py
+++ b/gitlab/tests/test_gitlab.py
@@ -28,10 +28,10 @@ from httmock import HTTMock # noqa
from httmock import response # noqa
from httmock import urlmatch # noqa
import requests
-import six
import gitlab
from gitlab import * # noqa
+from gitlab.v4.objects import * # noqa
class TestSanitize(unittest.TestCase):
@@ -49,130 +49,6 @@ class TestSanitize(unittest.TestCase):
self.assertEqual(expected, gitlab._sanitize(source))
-class TestGitlabRawMethods(unittest.TestCase):
- def setUp(self):
- self.gl = Gitlab("http://localhost", private_token="private_token",
- email="testuser@test.com", password="testpassword",
- ssl_verify=True, api_version=3)
-
- @urlmatch(scheme="http", netloc="localhost", path="/api/v3/known_path",
- method="get")
- def resp_get(self, url, request):
- headers = {'content-type': 'application/json'}
- content = 'response'.encode("utf-8")
- return response(200, content, headers, None, 5, request)
-
- def test_raw_get_unknown_path(self):
-
- @urlmatch(scheme="http", netloc="localhost",
- path="/api/v3/unknown_path",
- method="get")
- def resp_cont(url, request):
- headers = {'content-type': 'application/json'}
- content = '{"message": "message"}'.encode("utf-8")
- return response(404, content, headers, None, 5, request)
-
- with HTTMock(resp_cont):
- resp = self.gl._raw_get("/unknown_path")
- self.assertEqual(resp.status_code, 404)
-
- def test_raw_get_without_kwargs(self):
- with HTTMock(self.resp_get):
- resp = self.gl._raw_get("/known_path")
- self.assertEqual(resp.content, b'response')
- self.assertEqual(resp.status_code, 200)
-
- def test_raw_get_with_kwargs(self):
- with HTTMock(self.resp_get):
- resp = self.gl._raw_get("/known_path", sudo="testing")
- self.assertEqual(resp.content, b'response')
- self.assertEqual(resp.status_code, 200)
-
- def test_raw_post(self):
-
- @urlmatch(scheme="http", netloc="localhost", path="/api/v3/known_path",
- method="post")
- def resp_post(url, request):
- headers = {'content-type': 'application/json'}
- content = 'response'.encode("utf-8")
- return response(200, content, headers, None, 5, request)
-
- with HTTMock(resp_post):
- resp = self.gl._raw_post("/known_path")
- self.assertEqual(resp.content, b'response')
- self.assertEqual(resp.status_code, 200)
-
- def test_raw_post_unknown_path(self):
-
- @urlmatch(scheme="http", netloc="localhost",
- path="/api/v3/unknown_path",
- method="post")
- def resp_cont(url, request):
- headers = {'content-type': 'application/json'}
- content = '{"message": "message"}'.encode("utf-8")
- return response(404, content, headers, None, 5, request)
-
- with HTTMock(resp_cont):
- resp = self.gl._raw_post("/unknown_path")
- self.assertEqual(resp.status_code, 404)
-
- def test_raw_put(self):
-
- @urlmatch(scheme="http", netloc="localhost", path="/api/v3/known_path",
- method="put")
- def resp_put(url, request):
- headers = {'content-type': 'application/json'}
- content = 'response'.encode("utf-8")
- return response(200, content, headers, None, 5, request)
-
- with HTTMock(resp_put):
- resp = self.gl._raw_put("/known_path")
- self.assertEqual(resp.content, b'response')
- self.assertEqual(resp.status_code, 200)
-
- def test_raw_put_unknown_path(self):
-
- @urlmatch(scheme="http", netloc="localhost",
- path="/api/v3/unknown_path",
- method="put")
- def resp_cont(url, request):
- headers = {'content-type': 'application/json'}
- content = '{"message": "message"}'.encode("utf-8")
- return response(404, content, headers, None, 5, request)
-
- with HTTMock(resp_cont):
- resp = self.gl._raw_put("/unknown_path")
- self.assertEqual(resp.status_code, 404)
-
- def test_raw_delete(self):
-
- @urlmatch(scheme="http", netloc="localhost", path="/api/v3/known_path",
- method="delete")
- def resp_delete(url, request):
- headers = {'content-type': 'application/json'}
- content = 'response'.encode("utf-8")
- return response(200, content, headers, None, 5, request)
-
- with HTTMock(resp_delete):
- resp = self.gl._raw_delete("/known_path")
- self.assertEqual(resp.content, b'response')
- self.assertEqual(resp.status_code, 200)
-
- def test_raw_delete_unknown_path(self):
-
- @urlmatch(scheme="http", netloc="localhost",
- path="/api/v3/unknown_path",
- method="delete")
- def resp_cont(url, request):
- headers = {'content-type': 'application/json'}
- content = '{"message": "message"}'.encode("utf-8")
- return response(404, content, headers, None, 5, request)
-
- with HTTMock(resp_cont):
- resp = self.gl._raw_delete("/unknown_path")
- self.assertEqual(resp.status_code, 404)
-
-
class TestGitlabList(unittest.TestCase):
def setUp(self):
self.gl = Gitlab("http://localhost", private_token="private_token",
@@ -450,441 +326,6 @@ class TestGitlabHttpMethods(unittest.TestCase):
'/not_there')
-class TestGitlabMethods(unittest.TestCase):
- def setUp(self):
- self.gl = Gitlab("http://localhost", private_token="private_token",
- email="testuser@test.com", password="testpassword",
- ssl_verify=True, api_version=3)
-
- def test_list(self):
- @urlmatch(scheme="http", netloc="localhost",
- path="/api/v3/projects/1/repository/branches", method="get")
- def resp_cont(url, request):
- headers = {'content-type': 'application/json'}
- content = ('[{"branch_name": "testbranch", '
- '"project_id": 1, "ref": "a"}]').encode("utf-8")
- return response(200, content, headers, None, 5, request)
-
- with HTTMock(resp_cont):
- data = self.gl.list(ProjectBranch, project_id=1, page=1,
- per_page=20)
- self.assertEqual(len(data), 1)
- data = data[0]
- self.assertEqual(data.branch_name, "testbranch")
- self.assertEqual(data.project_id, 1)
- self.assertEqual(data.ref, "a")
-
- def test_list_next_link(self):
- @urlmatch(scheme="http", netloc="localhost",
- path='/api/v3/projects/1/repository/branches', method="get")
- def resp_one(url, request):
- """First request:
-
- http://localhost/api/v3/projects/1/repository/branches?per_page=1
- """
- headers = {
- 'content-type': 'application/json',
- 'link': '<http://localhost/api/v3/projects/1/repository/branc'
- 'hes?page=2&per_page=0>; rel="next", <http://localhost/api/v3'
- '/projects/1/repository/branches?page=2&per_page=0>; rel="las'
- 't", <http://localhost/api/v3/projects/1/repository/branches?'
- 'page=1&per_page=0>; rel="first"'
- }
- content = ('[{"branch_name": "otherbranch", '
- '"project_id": 1, "ref": "b"}]').encode("utf-8")
- resp = response(200, content, headers, None, 5, request)
- return resp
-
- @urlmatch(scheme="http", netloc="localhost",
- path='/api/v3/projects/1/repository/branches', method="get",
- query=r'.*page=2.*')
- def resp_two(url, request):
- headers = {
- 'content-type': 'application/json',
- 'link': '<http://localhost/api/v3/projects/1/repository/branc'
- 'hes?page=1&per_page=0>; rel="prev", <http://localhost/api/v3'
- '/projects/1/repository/branches?page=2&per_page=0>; rel="las'
- 't", <http://localhost/api/v3/projects/1/repository/branches?'
- 'page=1&per_page=0>; rel="first"'
- }
- content = ('[{"branch_name": "testbranch", '
- '"project_id": 1, "ref": "a"}]').encode("utf-8")
- resp = response(200, content, headers, None, 5, request)
- return resp
-
- with HTTMock(resp_two, resp_one):
- data = self.gl.list(ProjectBranch, project_id=1, per_page=1,
- all=True)
- self.assertEqual(data[1].branch_name, "testbranch")
- self.assertEqual(data[1].project_id, 1)
- self.assertEqual(data[1].ref, "a")
- self.assertEqual(data[0].branch_name, "otherbranch")
- self.assertEqual(data[0].project_id, 1)
- self.assertEqual(data[0].ref, "b")
- self.assertEqual(len(data), 2)
-
- def test_list_recursion_limit_caught(self):
- @urlmatch(scheme="http", netloc="localhost",
- path='/api/v3/projects/1/repository/branches', method="get")
- def resp_one(url, request):
- """First request:
-
- http://localhost/api/v3/projects/1/repository/branches?per_page=1
- """
- headers = {
- 'content-type': 'application/json',
- 'link': '<http://localhost/api/v3/projects/1/repository/branc'
- 'hes?page=2&per_page=0>; rel="next", <http://localhost/api/v3'
- '/projects/1/repository/branches?page=2&per_page=0>; rel="las'
- 't", <http://localhost/api/v3/projects/1/repository/branches?'
- 'page=1&per_page=0>; rel="first"'
- }
- content = ('[{"branch_name": "otherbranch", '
- '"project_id": 1, "ref": "b"}]').encode("utf-8")
- resp = response(200, content, headers, None, 5, request)
- return resp
-
- @urlmatch(scheme="http", netloc="localhost",
- path='/api/v3/projects/1/repository/branches', method="get",
- query=r'.*page=2.*')
- def resp_two(url, request):
- # Mock a runtime error
- raise RuntimeError("maximum recursion depth exceeded")
-
- with HTTMock(resp_two, resp_one):
- data = self.gl.list(ProjectBranch, project_id=1, per_page=1,
- safe_all=True)
- self.assertEqual(data[0].branch_name, "otherbranch")
- self.assertEqual(data[0].project_id, 1)
- self.assertEqual(data[0].ref, "b")
- self.assertEqual(len(data), 1)
-
- def test_list_recursion_limit_not_caught(self):
- @urlmatch(scheme="http", netloc="localhost",
- path='/api/v3/projects/1/repository/branches', method="get")
- def resp_one(url, request):
- """First request:
-
- http://localhost/api/v3/projects/1/repository/branches?per_page=1
- """
- headers = {
- 'content-type': 'application/json',
- 'link': '<http://localhost/api/v3/projects/1/repository/branc'
- 'hes?page=2&per_page=0>; rel="next", <http://localhost/api/v3'
- '/projects/1/repository/branches?page=2&per_page=0>; rel="las'
- 't", <http://localhost/api/v3/projects/1/repository/branches?'
- 'page=1&per_page=0>; rel="first"'
- }
- content = ('[{"branch_name": "otherbranch", '
- '"project_id": 1, "ref": "b"}]').encode("utf-8")
- resp = response(200, content, headers, None, 5, request)
- return resp
-
- @urlmatch(scheme="http", netloc="localhost",
- path='/api/v3/projects/1/repository/branches', method="get",
- query=r'.*page=2.*')
- def resp_two(url, request):
- # Mock a runtime error
- raise RuntimeError("maximum recursion depth exceeded")
-
- with HTTMock(resp_two, resp_one):
- with six.assertRaisesRegex(self, GitlabError,
- "(maximum recursion depth exceeded)"):
- self.gl.list(ProjectBranch, project_id=1, per_page=1, all=True)
-
- def test_list_401(self):
- @urlmatch(scheme="http", netloc="localhost",
- path="/api/v3/projects/1/repository/branches", method="get")
- def resp_cont(url, request):
- headers = {'content-type': 'application/json'}
- content = '{"message":"message"}'.encode("utf-8")
- return response(401, content, headers, None, 5, request)
-
- with HTTMock(resp_cont):
- self.assertRaises(GitlabAuthenticationError, self.gl.list,
- ProjectBranch, project_id=1)
-
- def test_list_unknown_error(self):
- @urlmatch(scheme="http", netloc="localhost",
- path="/api/v3/projects/1/repository/branches", method="get")
- def resp_cont(url, request):
- headers = {'content-type': 'application/json'}
- content = '{"message":"message"}'.encode("utf-8")
- return response(405, content, headers, None, 5, request)
-
- with HTTMock(resp_cont):
- self.assertRaises(GitlabListError, self.gl.list,
- ProjectBranch, project_id=1)
-
- def test_list_kw_missing(self):
- self.assertRaises(GitlabListError, self.gl.list, ProjectBranch)
-
- def test_list_no_connection(self):
- self.gl._url = 'http://localhost:66000/api/v3'
- self.assertRaises(GitlabConnectionError, self.gl.list, ProjectBranch,
- project_id=1)
-
- def test_get(self):
- @urlmatch(scheme="http", netloc="localhost",
- path="/api/v3/projects/1", method="get")
- def resp_cont(url, request):
- headers = {'content-type': 'application/json'}
- content = '{"name": "testproject"}'.encode("utf-8")
- return response(200, content, headers, None, 5, request)
-
- with HTTMock(resp_cont):
- data = self.gl.get(Project, id=1)
- expected = {"name": "testproject"}
- self.assertEqual(expected, data)
-
- def test_get_unknown_path(self):
- @urlmatch(scheme="http", netloc="localhost", path="/api/v3/groups/1",
- method="get")
- def resp_cont(url, request):
- headers = {'content-type': 'application/json'}
- content = '{"message": "message"}'.encode("utf-8")
- return response(404, content, headers, None, 5, request)
-
- with HTTMock(resp_cont):
- self.assertRaises(GitlabGetError, self.gl.get, Group, 1)
-
- def test_get_missing_kw(self):
- self.assertRaises(GitlabGetError, self.gl.get, ProjectBranch)
-
- def test_get_401(self):
- @urlmatch(scheme="http", netloc="localhost", path="/api/v3/projects/1",
- method="get")
- def resp_cont(url, request):
- headers = {'content-type': 'application/json'}
- content = '{"message": "message"}'.encode("utf-8")
- return response(401, content, headers, None, 5, request)
-
- with HTTMock(resp_cont):
- self.assertRaises(GitlabAuthenticationError, self.gl.get,
- Project, 1)
-
- def test_get_404(self):
- @urlmatch(scheme="http", netloc="localhost", path="/api/v3/projects/1",
- method="get")
- def resp_cont(url, request):
- headers = {'content-type': 'application/json'}
- content = '{"message": "message"}'.encode("utf-8")
- return response(404, content, headers, None, 5, request)
-
- with HTTMock(resp_cont):
- self.assertRaises(GitlabGetError, self.gl.get,
- Project, 1)
-
- def test_get_unknown_error(self):
- @urlmatch(scheme="http", netloc="localhost", path="/api/v3/projects/1",
- method="get")
- def resp_cont(url, request):
- headers = {'content-type': 'application/json'}
- content = '{"message": "message"}'.encode("utf-8")
- return response(405, content, headers, None, 5, request)
-
- with HTTMock(resp_cont):
- self.assertRaises(GitlabGetError, self.gl.get,
- Project, 1)
-
- def test_delete_from_object(self):
- @urlmatch(scheme="http", netloc="localhost", path="/api/v3/groups/1",
- method="delete")
- def resp_delete_group(url, request):
- headers = {'content-type': 'application/json'}
- content = ''.encode("utf-8")
- return response(200, content, headers, None, 5, request)
-
- obj = Group(self.gl, data={"name": "testname", "id": 1})
- with HTTMock(resp_delete_group):
- data = self.gl.delete(obj)
- self.assertIs(data, True)
-
- def test_delete_from_invalid_class(self):
- class InvalidClass(object):
- pass
-
- self.assertRaises(GitlabError, self.gl.delete, InvalidClass, 1)
-
- def test_delete_from_class(self):
- @urlmatch(scheme="http", netloc="localhost", path="/api/v3/groups/1",
- method="delete")
- def resp_delete_group(url, request):
- headers = {'content-type': 'application/json'}
- content = ''.encode("utf-8")
- return response(200, content, headers, None, 5, request)
-
- with HTTMock(resp_delete_group):
- data = self.gl.delete(Group, 1)
- self.assertIs(data, True)
-
- def test_delete_unknown_path(self):
- obj = Project(self.gl, data={"name": "testname", "id": 1})
- obj._from_api = True
-
- @urlmatch(scheme="http", netloc="localhost", path="/api/v3/projects/1",
- method="delete")
- def resp_cont(url, request):
- headers = {'content-type': 'application/json'}
- content = '{"message": "message"}'.encode("utf-8")
- return response(404, content, headers, None, 5, request)
-
- with HTTMock(resp_cont):
- self.assertRaises(GitlabDeleteError, self.gl.delete, obj)
-
- def test_delete_401(self):
- obj = Project(self.gl, data={"name": "testname", "id": 1})
-
- @urlmatch(scheme="http", netloc="localhost", path="/api/v3/projects/1",
- method="delete")
- def resp_cont(url, request):
- headers = {'content-type': 'application/json'}
- content = '{"message": "message"}'.encode("utf-8")
- return response(401, content, headers, None, 5, request)
-
- with HTTMock(resp_cont):
- self.assertRaises(GitlabAuthenticationError, self.gl.delete, obj)
-
- def test_delete_unknown_error(self):
- obj = Project(self.gl, data={"name": "testname", "id": 1})
-
- @urlmatch(scheme="http", netloc="localhost", path="/api/v3/projects/1",
- method="delete")
- def resp_cont(url, request):
- headers = {'content-type': 'application/json'}
- content = '{"message": "message"}'.encode("utf-8")
- return response(405, content, headers, None, 5, request)
-
- with HTTMock(resp_cont):
- self.assertRaises(GitlabDeleteError, self.gl.delete, obj)
-
- def test_create(self):
- @urlmatch(scheme="http", netloc="localhost", path="/api/v3/projects",
- method="post")
- def resp_create_project(url, request):
- headers = {'content-type': 'application/json'}
- content = '{"name": "testname", "id": 1}'.encode("utf-8")
- return response(201, content, headers, None, 5, request)
-
- obj = Project(self.gl, data={"name": "testname"})
-
- with HTTMock(resp_create_project):
- data = self.gl.create(obj)
- expected = {u"name": u"testname", u"id": 1}
- self.assertEqual(expected, data)
-
- def test_create_kw_missing(self):
- obj = Group(self.gl, data={"name": "testgroup"})
- self.assertRaises(GitlabCreateError, self.gl.create, obj)
-
- def test_create_unknown_path(self):
- obj = Project(self.gl, data={"name": "name"})
- obj.id = 1
- obj._from_api = True
-
- @urlmatch(scheme="http", netloc="localhost", path="/api/v3/projects/1",
- method="delete")
- def resp_cont(url, request):
- headers = {'content-type': 'application/json'}
- content = '{"message": "message"}'.encode("utf-8")
- return response(404, content, headers, None, 5, request)
-
- with HTTMock(resp_cont):
- self.assertRaises(GitlabDeleteError, self.gl.delete, obj)
-
- def test_create_401(self):
- obj = Group(self.gl, data={"name": "testgroup", "path": "testpath"})
-
- @urlmatch(scheme="http", netloc="localhost", path="/api/v3/groups",
- method="post")
- def resp_cont(url, request):
- headers = {'content-type': 'application/json'}
- content = '{"message": "message"}'.encode("utf-8")
- return response(401, content, headers, None, 5, request)
-
- with HTTMock(resp_cont):
- self.assertRaises(GitlabAuthenticationError, self.gl.create, obj)
-
- def test_create_unknown_error(self):
- obj = Group(self.gl, data={"name": "testgroup", "path": "testpath"})
-
- @urlmatch(scheme="http", netloc="localhost", path="/api/v3/groups",
- method="post")
- def resp_cont(url, request):
- headers = {'content-type': 'application/json'}
- content = '{"message": "message"}'.encode("utf-8")
- return response(405, content, headers, None, 5, request)
-
- with HTTMock(resp_cont):
- self.assertRaises(GitlabCreateError, self.gl.create, obj)
-
- def test_update(self):
- obj = User(self.gl, data={"email": "testuser@testmail.com",
- "password": "testpassword",
- "name": u"testuser",
- "username": "testusername",
- "can_create_group": True,
- "id": 1})
-
- @urlmatch(scheme="http", netloc="localhost", path="/api/v3/users/1",
- method="put")
- def resp_cont(url, request):
- headers = {'content-type': 'application/json'}
- content = '{"first": "return1"}'.encode("utf-8")
- return response(200, content, headers, None, 5, request)
-
- with HTTMock(resp_cont):
- data = self.gl.update(obj)
- expected = {"first": "return1"}
- self.assertEqual(expected, data)
-
- def test_update_kw_missing(self):
- obj = Hook(self.gl, data={"name": "testgroup"})
- self.assertRaises(GitlabUpdateError, self.gl.update, obj)
-
- def test_update_401(self):
- obj = Group(self.gl, data={"name": "testgroup", "path": "testpath",
- "id": 1})
-
- @urlmatch(scheme="http", netloc="localhost", path="/api/v3/groups/1",
- method="put")
- def resp_cont(url, request):
- headers = {'content-type': 'application/json'}
- content = '{"message": "message"}'.encode("utf-8")
- return response(401, content, headers, None, 5, request)
-
- with HTTMock(resp_cont):
- self.assertRaises(GitlabAuthenticationError, self.gl.update, obj)
-
- def test_update_unknown_error(self):
- obj = Group(self.gl, data={"name": "testgroup", "path": "testpath",
- "id": 1})
-
- @urlmatch(scheme="http", netloc="localhost", path="/api/v3/groups/1",
- method="put")
- def resp_cont(url, request):
- headers = {'content-type': 'application/json'}
- content = '{"message": "message"}'.encode("utf-8")
- return response(405, content, headers, None, 5, request)
-
- with HTTMock(resp_cont):
- self.assertRaises(GitlabUpdateError, self.gl.update, obj)
-
- def test_update_unknown_path(self):
- obj = Group(self.gl, data={"name": "testgroup", "path": "testpath",
- "id": 1})
-
- @urlmatch(scheme="http", netloc="localhost", path="/api/v3/groups/1",
- method="put")
- def resp_cont(url, request):
- headers = {'content-type': 'application/json'}
- content = '{"message": "message"}'.encode("utf-8")
- return response(404, content, headers, None, 5, request)
-
- with HTTMock(resp_cont):
- self.assertRaises(GitlabUpdateError, self.gl.update, obj)
-
-
class TestGitlabAuth(unittest.TestCase):
def test_invalid_auth_args(self):
self.assertRaises(ValueError,
@@ -938,7 +379,7 @@ class TestGitlab(unittest.TestCase):
def setUp(self):
self.gl = Gitlab("http://localhost", private_token="private_token",
email="testuser@test.com", password="testpassword",
- ssl_verify=True, api_version=3)
+ ssl_verify=True, api_version=4)
def test_pickability(self):
original_gl_objects = self.gl._objects
@@ -952,7 +393,7 @@ class TestGitlab(unittest.TestCase):
self.gl.email = None
self.gl.password = None
- @urlmatch(scheme="http", netloc="localhost", path="/api/v3/session",
+ @urlmatch(scheme="http", netloc="localhost", path="/api/v4/session",
method="post")
def resp_cont(url, request):
headers = {'content-type': 'application/json'}
@@ -960,11 +401,10 @@ class TestGitlab(unittest.TestCase):
return response(404, content, headers, None, 5, request)
with HTTMock(resp_cont):
- self.assertRaises(GitlabAuthenticationError,
- self.gl._credentials_auth)
+ self.assertRaises(GitlabHttpError, self.gl._credentials_auth)
def test_credentials_auth_notok(self):
- @urlmatch(scheme="http", netloc="localhost", path="/api/v3/session",
+ @urlmatch(scheme="http", netloc="localhost", path="/api/v4/session",
method="post")
def resp_cont(url, request):
headers = {'content-type': 'application/json'}
@@ -972,8 +412,7 @@ class TestGitlab(unittest.TestCase):
return response(404, content, headers, None, 5, request)
with HTTMock(resp_cont):
- self.assertRaises(GitlabAuthenticationError,
- self.gl._credentials_auth)
+ self.assertRaises(GitlabHttpError, self.gl._credentials_auth)
def test_auth_with_credentials(self):
self.gl.private_token = None
@@ -989,7 +428,7 @@ class TestGitlab(unittest.TestCase):
id_ = 1
expected = {"PRIVATE-TOKEN": token}
- @urlmatch(scheme="http", netloc="localhost", path="/api/v3/session",
+ @urlmatch(scheme="http", netloc="localhost", path="/api/v4/session",
method="post")
def resp_cont(url, request):
headers = {'content-type': 'application/json'}
@@ -1009,7 +448,7 @@ class TestGitlab(unittest.TestCase):
name = "username"
id_ = 1
- @urlmatch(scheme="http", netloc="localhost", path="/api/v3/user",
+ @urlmatch(scheme="http", netloc="localhost", path="/api/v4/user",
method="get")
def resp_cont(url, request):
headers = {'content-type': 'application/json'}
@@ -1024,7 +463,7 @@ class TestGitlab(unittest.TestCase):
self.assertEqual(type(self.gl.user), CurrentUser)
def test_hooks(self):
- @urlmatch(scheme="http", netloc="localhost", path="/api/v3/hooks/1",
+ @urlmatch(scheme="http", netloc="localhost", path="/api/v4/hooks/1",
method="get")
def resp_get_hook(url, request):
headers = {'content-type': 'application/json'}
@@ -1038,7 +477,7 @@ class TestGitlab(unittest.TestCase):
self.assertEqual(data.id, 1)
def test_projects(self):
- @urlmatch(scheme="http", netloc="localhost", path="/api/v3/projects/1",
+ @urlmatch(scheme="http", netloc="localhost", path="/api/v4/projects/1",
method="get")
def resp_get_project(url, request):
headers = {'content-type': 'application/json'}
@@ -1051,20 +490,8 @@ class TestGitlab(unittest.TestCase):
self.assertEqual(data.name, "name")
self.assertEqual(data.id, 1)
- def test_userprojects(self):
- @urlmatch(scheme="http", netloc="localhost",
- path="/api/v3/projects/user/2", method="get")
- def resp_get_userproject(url, request):
- headers = {'content-type': 'application/json'}
- content = '{"name": "name", "id": 1, "user_id": 2}'.encode("utf-8")
- return response(200, content, headers, None, 5, request)
-
- with HTTMock(resp_get_userproject):
- self.assertRaises(NotImplementedError, self.gl.user_projects.get,
- 1, user_id=2)
-
def test_groups(self):
- @urlmatch(scheme="http", netloc="localhost", path="/api/v3/groups/1",
+ @urlmatch(scheme="http", netloc="localhost", path="/api/v4/groups/1",
method="get")
def resp_get_group(url, request):
headers = {'content-type': 'application/json'}
@@ -1080,7 +507,7 @@ class TestGitlab(unittest.TestCase):
self.assertEqual(data.id, 1)
def test_issues(self):
- @urlmatch(scheme="http", netloc="localhost", path="/api/v3/issues",
+ @urlmatch(scheme="http", netloc="localhost", path="/api/v4/issues",
method="get")
def resp_get_issue(url, request):
headers = {'content-type': 'application/json'}
@@ -1095,7 +522,7 @@ class TestGitlab(unittest.TestCase):
self.assertEqual(data.name, 'other_name')
def test_users(self):
- @urlmatch(scheme="http", netloc="localhost", path="/api/v3/users/1",
+ @urlmatch(scheme="http", netloc="localhost", path="/api/v4/users/1",
method="get")
def resp_get_user(url, request):
headers = {'content-type': 'application/json'}
@@ -1109,19 +536,3 @@ class TestGitlab(unittest.TestCase):
self.assertEqual(type(user), User)
self.assertEqual(user.name, "name")
self.assertEqual(user.id, 1)
-
- def test_teams(self):
- @urlmatch(scheme="http", netloc="localhost",
- path="/api/v3/user_teams/1", method="get")
- def resp_get_group(url, request):
- headers = {'content-type': 'application/json'}
- content = '{"name": "name", "id": 1, "path": "path"}'
- content = content.encode('utf-8')
- return response(200, content, headers, None, 5, request)
-
- with HTTMock(resp_get_group):
- data = self.gl.teams.get(1)
- self.assertEqual(type(data), Team)
- self.assertEqual(data.name, "name")
- self.assertEqual(data.path, "path")
- self.assertEqual(data.id, 1)
diff --git a/gitlab/tests/test_gitlabobject.py b/gitlab/tests/test_gitlabobject.py
deleted file mode 100644
index 844ba9e..0000000
--- a/gitlab/tests/test_gitlabobject.py
+++ /dev/null
@@ -1,500 +0,0 @@
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-#
-# Copyright (C) 2014 Mika Mäenpää <mika.j.maenpaa@tut.fi>
-# Tampere University of Technology
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Lesser General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU Lesser General Public License for more details.
-#
-# 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/>.
-
-from __future__ import print_function
-from __future__ import absolute_import
-
-import json
-import pickle
-try:
- import unittest
-except ImportError:
- import unittest2 as unittest
-
-from httmock import HTTMock # noqa
-from httmock import response # noqa
-from httmock import urlmatch # noqa
-
-from gitlab import * # noqa
-
-
-@urlmatch(scheme="http", netloc="localhost", path="/api/v4/projects/1",
- method="get")
-def resp_get_project(url, request):
- headers = {'content-type': 'application/json'}
- content = '{"name": "name", "id": 1}'.encode("utf-8")
- return response(200, content, headers, None, 5, request)
-
-
-@urlmatch(scheme="http", netloc="localhost", path="/api/v4/projects",
- method="get")
-def resp_list_project(url, request):
- headers = {'content-type': 'application/json'}
- content = '[{"name": "name", "id": 1}]'.encode("utf-8")
- return response(200, content, headers, None, 5, request)
-
-
-@urlmatch(scheme="http", netloc="localhost", path="/api/v4/issues/1",
- method="get")
-def resp_get_issue(url, request):
- headers = {'content-type': 'application/json'}
- content = '{"name": "name", "id": 1}'.encode("utf-8")
- return response(200, content, headers, None, 5, request)
-
-
-@urlmatch(scheme="http", netloc="localhost", path="/api/v4/users/1",
- method="put")
-def resp_update_user(url, request):
- headers = {'content-type': 'application/json'}
- content = ('{"name": "newname", "id": 1, "password": "password", '
- '"username": "username", "email": "email"}').encode("utf-8")
- return response(200, content, headers, None, 5, request)
-
-
-@urlmatch(scheme="http", netloc="localhost", path="/api/v4/projects",
- method="post")
-def resp_create_project(url, request):
- headers = {'content-type': 'application/json'}
- content = '{"name": "testname", "id": 1}'.encode("utf-8")
- return response(201, content, headers, None, 5, request)
-
-
-@urlmatch(scheme="http", netloc="localhost", path="/api/v4/groups/2/members",
- method="post")
-def resp_create_groupmember(url, request):
- headers = {'content-type': 'application/json'}
- content = '{"access_level": 50, "id": 3}'.encode("utf-8")
- return response(201, content, headers, None, 5, request)
-
-
-@urlmatch(scheme="http", netloc="localhost",
- path="/api/v4/projects/2/snippets/3", method="get")
-def resp_get_projectsnippet(url, request):
- headers = {'content-type': 'application/json'}
- content = '{"title": "test", "id": 3}'.encode("utf-8")
- return response(200, content, headers, None, 5, request)
-
-
-@urlmatch(scheme="http", netloc="localhost", path="/api/v4/groups/1",
- method="delete")
-def resp_delete_group(url, request):
- headers = {'content-type': 'application/json'}
- content = ''.encode("utf-8")
- return response(200, content, headers, None, 5, request)
-
-
-@urlmatch(scheme="http", netloc="localhost",
- path="/api/v4/groups/2/projects/3",
- method="post")
-def resp_transfer_project(url, request):
- headers = {'content-type': 'application/json'}
- content = ''.encode("utf-8")
- return response(201, content, headers, None, 5, request)
-
-
-@urlmatch(scheme="http", netloc="localhost",
- path="/api/v4/groups/2/projects/3",
- method="post")
-def resp_transfer_project_fail(url, request):
- headers = {'content-type': 'application/json'}
- content = '{"message": "messagecontent"}'.encode("utf-8")
- return response(400, content, headers, None, 5, request)
-
-
-@urlmatch(scheme="http", netloc="localhost",
- path="/api/v4/projects/2/repository/branches/branchname/protect",
- method="put")
-def resp_protect_branch(url, request):
- headers = {'content-type': 'application/json'}
- content = ''.encode("utf-8")
- return response(200, content, headers, None, 5, request)
-
-
-@urlmatch(scheme="http", netloc="localhost",
- path="/api/v4/projects/2/repository/branches/branchname/unprotect",
- method="put")
-def resp_unprotect_branch(url, request):
- headers = {'content-type': 'application/json'}
- content = ''.encode("utf-8")
- return response(200, content, headers, None, 5, request)
-
-
-@urlmatch(scheme="http", netloc="localhost",
- path="/api/v4/projects/2/repository/branches/branchname/protect",
- method="put")
-def resp_protect_branch_fail(url, request):
- headers = {'content-type': 'application/json'}
- content = '{"message": "messagecontent"}'.encode("utf-8")
- return response(400, content, headers, None, 5, request)
-
-
-class TestGitlabObject(unittest.TestCase):
-
- def setUp(self):
- self.gl = Gitlab("http://localhost", private_token="private_token",
- email="testuser@test.com", password="testpassword",
- ssl_verify=True)
-
- def test_json(self):
- gl_object = CurrentUser(self.gl, data={"username": "testname"})
- json_str = gl_object.json()
- data = json.loads(json_str)
- self.assertIn("id", data)
- self.assertEqual(data["username"], "testname")
- self.assertEqual(data["gitlab"]["url"], "http://localhost/api/v4")
-
- def test_pickability(self):
- gl_object = CurrentUser(self.gl, data={"username": "testname"})
- original_obj_module = gl_object._module
- pickled = pickle.dumps(gl_object)
- unpickled = pickle.loads(pickled)
- self.assertIsInstance(unpickled, CurrentUser)
- self.assertTrue(hasattr(unpickled, '_module'))
- self.assertEqual(unpickled._module, original_obj_module)
-
- def test_data_for_gitlab(self):
- class FakeObj1(GitlabObject):
- _url = '/fake1'
- requiredCreateAttrs = ['create_req']
- optionalCreateAttrs = ['create_opt']
- requiredUpdateAttrs = ['update_req']
- optionalUpdateAttrs = ['update_opt']
-
- class FakeObj2(GitlabObject):
- _url = '/fake2'
- requiredCreateAttrs = ['create_req']
- optionalCreateAttrs = ['create_opt']
-
- obj1 = FakeObj1(self.gl, {'update_req': 1, 'update_opt': 1,
- 'create_req': 1, 'create_opt': 1})
- obj2 = FakeObj2(self.gl, {'create_req': 1, 'create_opt': 1})
-
- obj1_data = json.loads(obj1._data_for_gitlab())
- self.assertIn('create_req', obj1_data)
- self.assertIn('create_opt', obj1_data)
- self.assertNotIn('update_req', obj1_data)
- self.assertNotIn('update_opt', obj1_data)
- self.assertNotIn('gitlab', obj1_data)
-
- obj1_data = json.loads(obj1._data_for_gitlab(update=True))
- self.assertNotIn('create_req', obj1_data)
- self.assertNotIn('create_opt', obj1_data)
- self.assertIn('update_req', obj1_data)
- self.assertIn('update_opt', obj1_data)
-
- obj1_data = json.loads(obj1._data_for_gitlab(
- extra_parameters={'foo': 'bar'}))
- self.assertIn('foo', obj1_data)
- self.assertEqual(obj1_data['foo'], 'bar')
-
- obj2_data = json.loads(obj2._data_for_gitlab(update=True))
- self.assertIn('create_req', obj2_data)
- self.assertIn('create_opt', obj2_data)
-
- def test_list_not_implemented(self):
- self.assertRaises(NotImplementedError, CurrentUser.list, self.gl)
-
- def test_list(self):
- with HTTMock(resp_list_project):
- data = Project.list(self.gl, id=1)
- self.assertEqual(type(data), list)
- self.assertEqual(len(data), 1)
- self.assertEqual(type(data[0]), Project)
- self.assertEqual(data[0].name, "name")
- self.assertEqual(data[0].id, 1)
-
- def test_create_cantcreate(self):
- gl_object = CurrentUser(self.gl, data={"username": "testname"})
- self.assertRaises(NotImplementedError, gl_object._create)
-
- def test_create(self):
- obj = Project(self.gl, data={"name": "testname"})
- with HTTMock(resp_create_project):
- obj._create()
- self.assertEqual(obj.id, 1)
-
- def test_create_with_kw(self):
- obj = GroupMember(self.gl, data={"access_level": 50, "user_id": 3},
- group_id=2)
- with HTTMock(resp_create_groupmember):
- obj._create()
- self.assertEqual(obj.id, 3)
- self.assertEqual(obj.group_id, 2)
- self.assertEqual(obj.user_id, 3)
- self.assertEqual(obj.access_level, 50)
-
- def test_get_with_kw(self):
- with HTTMock(resp_get_projectsnippet):
- obj = ProjectSnippet(self.gl, data=3, project_id=2)
- self.assertEqual(obj.id, 3)
- self.assertEqual(obj.project_id, 2)
- self.assertEqual(obj.title, "test")
-
- def test_create_cantupdate(self):
- gl_object = CurrentUser(self.gl, data={"username": "testname"})
- self.assertRaises(NotImplementedError, gl_object._update)
-
- def test_update(self):
- obj = User(self.gl, data={"name": "testname", "email": "email",
- "password": "password", "id": 1,
- "username": "username"})
- self.assertEqual(obj.name, "testname")
- obj.name = "newname"
- with HTTMock(resp_update_user):
- obj._update()
- self.assertEqual(obj.name, "newname")
-
- def test_save_with_id(self):
- obj = User(self.gl, data={"name": "testname", "email": "email",
- "password": "password", "id": 1,
- "username": "username"})
- self.assertEqual(obj.name, "testname")
- obj._from_api = True
- obj.name = "newname"
- with HTTMock(resp_update_user):
- obj.save()
- self.assertEqual(obj.name, "newname")
-
- def test_save_without_id(self):
- obj = Project(self.gl, data={"name": "testname"})
- with HTTMock(resp_create_project):
- obj.save()
- self.assertEqual(obj.id, 1)
-
- def test_delete(self):
- obj = Group(self.gl, data={"name": "testname", "id": 1})
- obj._from_api = True
- with HTTMock(resp_delete_group):
- data = obj.delete()
- self.assertIs(data, True)
-
- def test_delete_with_no_id(self):
- obj = Group(self.gl, data={"name": "testname"})
- self.assertRaises(GitlabDeleteError, obj.delete)
-
- def test_delete_cant_delete(self):
- obj = CurrentUser(self.gl, data={"name": "testname", "id": 1})
- self.assertRaises(NotImplementedError, obj.delete)
-
- def test_set_from_dict_BooleanTrue(self):
- obj = Project(self.gl, data={"name": "testname"})
- data = {"issues_enabled": True}
- obj._set_from_dict(data)
- self.assertIs(obj.issues_enabled, True)
-
- def test_set_from_dict_BooleanFalse(self):
- obj = Project(self.gl, data={"name": "testname"})
- data = {"issues_enabled": False}
- obj._set_from_dict(data)
- self.assertIs(obj.issues_enabled, False)
-
- def test_set_from_dict_None(self):
- obj = Project(self.gl, data={"name": "testname"})
- data = {"issues_enabled": None}
- obj._set_from_dict(data)
- self.assertIsNone(obj.issues_enabled)
-
-
-class TestGroup(unittest.TestCase):
- def setUp(self):
- self.gl = Gitlab("http://localhost", private_token="private_token",
- email="testuser@test.com", password="testpassword",
- ssl_verify=True)
-
- def test_transfer_project(self):
- obj = Group(self.gl, data={"name": "testname", "path": "testpath",
- "id": 2})
- with HTTMock(resp_transfer_project):
- obj.transfer_project(3)
-
- def test_transfer_project_fail(self):
- obj = Group(self.gl, data={"name": "testname", "path": "testpath",
- "id": 2})
- with HTTMock(resp_transfer_project_fail):
- self.assertRaises(GitlabTransferProjectError,
- obj.transfer_project, 3)
-
-
-class TestProjectBranch(unittest.TestCase):
- def setUp(self):
- self.gl = Gitlab("http://localhost", private_token="private_token",
- email="testuser@test.com", password="testpassword",
- ssl_verify=True)
- self.obj = ProjectBranch(self.gl, data={"name": "branchname",
- "ref": "ref_name", "id": 3,
- "project_id": 2})
-
- def test_protect(self):
- self.assertRaises(AttributeError, getattr, self.obj, 'protected')
- with HTTMock(resp_protect_branch):
- self.obj.protect(True)
- self.assertIs(self.obj.protected, True)
-
- def test_protect_unprotect(self):
- self.obj.protected = True
- with HTTMock(resp_unprotect_branch):
- self.obj.protect(False)
- self.assertRaises(AttributeError, getattr, self.obj, 'protected')
-
- def test_protect_unprotect_again(self):
- self.assertRaises(AttributeError, getattr, self.obj, 'protected')
- with HTTMock(resp_protect_branch):
- self.obj.protect(True)
- self.assertIs(self.obj.protected, True)
- self.assertEqual(True, self.obj.protected)
- with HTTMock(resp_unprotect_branch):
- self.obj.protect(False)
- self.assertRaises(AttributeError, getattr, self.obj, 'protected')
-
- def test_protect_protect_fail(self):
- with HTTMock(resp_protect_branch_fail):
- self.assertRaises(GitlabProtectError, self.obj.protect)
-
- def test_unprotect(self):
- self.obj.protected = True
- with HTTMock(resp_unprotect_branch):
- self.obj.unprotect()
- self.assertRaises(AttributeError, getattr, self.obj, 'protected')
-
-
-class TestProjectCommit(unittest.TestCase):
- def setUp(self):
- self.gl = Gitlab("http://localhost", private_token="private_token",
- email="testuser@test.com", password="testpassword",
- ssl_verify=True)
- self.obj = ProjectCommit(self.gl, data={"id": 3, "project_id": 2})
-
- @urlmatch(scheme="http", netloc="localhost",
- path="/api/v4/projects/2/repository/commits/3/diff",
- method="get")
- def resp_diff(self, url, request):
- headers = {'content-type': 'application/json'}
- content = '{"json": 2 }'.encode("utf-8")
- return response(200, content, headers, None, 5, request)
-
- @urlmatch(scheme="http", netloc="localhost",
- path="/api/v4/projects/2/repository/commits/3/diff",
- method="get")
- def resp_diff_fail(self, url, request):
- headers = {'content-type': 'application/json'}
- content = '{"message": "messagecontent" }'.encode("utf-8")
- return response(400, content, headers, None, 5, request)
-
- @urlmatch(scheme="http", netloc="localhost",
- path="/api/v4/projects/2/repository/blobs/3",
- method="get")
- def resp_blob(self, url, request):
- headers = {'content-type': 'application/json'}
- content = 'blob'.encode("utf-8")
- return response(200, content, headers, None, 5, request)
-
- @urlmatch(scheme="http", netloc="localhost",
- path="/api/v4/projects/2/repository/blobs/3",
- method="get")
- def resp_blob_fail(self, url, request):
- headers = {'content-type': 'application/json'}
- content = '{"message": "messagecontent" }'.encode("utf-8")
- return response(400, content, headers, None, 5, request)
-
- def test_diff(self):
- with HTTMock(self.resp_diff):
- data = {"json": 2}
- diff = self.obj.diff()
- self.assertEqual(diff, data)
-
- def test_diff_fail(self):
- with HTTMock(self.resp_diff_fail):
- self.assertRaises(GitlabGetError, self.obj.diff)
-
- def test_blob(self):
- with HTTMock(self.resp_blob):
- blob = self.obj.blob("testing")
- self.assertEqual(blob, b'blob')
-
- def test_blob_fail(self):
- with HTTMock(self.resp_blob_fail):
- self.assertRaises(GitlabGetError, self.obj.blob, "testing")
-
-
-class TestProjectSnippet(unittest.TestCase):
- def setUp(self):
- self.gl = Gitlab("http://localhost", private_token="private_token",
- email="testuser@test.com", password="testpassword",
- ssl_verify=True)
- self.obj = ProjectSnippet(self.gl, data={"id": 3, "project_id": 2})
-
- @urlmatch(scheme="http", netloc="localhost",
- path="/api/v4/projects/2/snippets/3/raw",
- method="get")
- def resp_content(self, url, request):
- headers = {'content-type': 'application/json'}
- content = 'content'.encode("utf-8")
- return response(200, content, headers, None, 5, request)
-
- @urlmatch(scheme="http", netloc="localhost",
- path="/api/v4/projects/2/snippets/3/raw",
- method="get")
- def resp_content_fail(self, url, request):
- headers = {'content-type': 'application/json'}
- content = '{"message": "messagecontent" }'.encode("utf-8")
- return response(400, content, headers, None, 5, request)
-
- def test_content(self):
- with HTTMock(self.resp_content):
- data = b'content'
- content = self.obj.content()
- self.assertEqual(content, data)
-
- def test_blob_fail(self):
- with HTTMock(self.resp_content_fail):
- self.assertRaises(GitlabGetError, self.obj.content)
-
-
-class TestSnippet(unittest.TestCase):
- def setUp(self):
- self.gl = Gitlab("http://localhost", private_token="private_token",
- email="testuser@test.com", password="testpassword",
- ssl_verify=True)
- self.obj = Snippet(self.gl, data={"id": 3})
-
- @urlmatch(scheme="http", netloc="localhost",
- path="/api/v4/snippets/3/raw",
- method="get")
- def resp_content(self, url, request):
- headers = {'content-type': 'application/json'}
- content = 'content'.encode("utf-8")
- return response(200, content, headers, None, 5, request)
-
- @urlmatch(scheme="http", netloc="localhost",
- path="/api/v4/snippets/3/raw",
- method="get")
- def resp_content_fail(self, url, request):
- headers = {'content-type': 'application/json'}
- content = '{"message": "messagecontent" }'.encode("utf-8")
- return response(400, content, headers, None, 5, request)
-
- def test_content(self):
- with HTTMock(self.resp_content):
- data = b'content'
- content = self.obj.raw()
- self.assertEqual(content, data)
-
- def test_blob_fail(self):
- with HTTMock(self.resp_content_fail):
- self.assertRaises(GitlabGetError, self.obj.raw)
diff --git a/gitlab/tests/test_manager.py b/gitlab/tests/test_manager.py
deleted file mode 100644
index c6ef299..0000000
--- a/gitlab/tests/test_manager.py
+++ /dev/null
@@ -1,309 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# Copyright (C) 2016-2017 Gauvain Pocentek <gauvain@pocentek.net>
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Lesser General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU Lesser General Public License for more details.
-#
-# 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/>.
-
-try:
- import unittest
-except ImportError:
- import unittest2 as unittest
-
-from httmock import HTTMock # noqa
-from httmock import response # noqa
-from httmock import urlmatch # noqa
-
-from gitlab import * # noqa
-from gitlab.v3.objects import BaseManager # noqa
-
-
-class FakeChildObject(GitlabObject):
- _url = "/fake/%(parent_id)s/fakechild"
- requiredCreateAttrs = ['name']
- requiredUrlAttrs = ['parent_id']
-
-
-class FakeChildManager(BaseManager):
- obj_cls = FakeChildObject
-
-
-class FakeObject(GitlabObject):
- _url = "/fake"
- requiredCreateAttrs = ['name']
- managers = [('children', FakeChildManager, [('parent_id', 'id')])]
-
-
-class FakeObjectManager(BaseManager):
- obj_cls = FakeObject
-
-
-class TestGitlabManager(unittest.TestCase):
- def setUp(self):
- self.gitlab = Gitlab("http://localhost", private_token="private_token",
- email="testuser@test.com",
- password="testpassword", ssl_verify=True,
- api_version=3)
-
- def test_set_parent_args(self):
- @urlmatch(scheme="http", netloc="localhost", path="/api/v3/fake",
- method="POST")
- def resp_create(url, request):
- headers = {'content-type': 'application/json'}
- content = '{"id": 1, "name": "name"}'.encode("utf-8")
- return response(201, content, headers, None, 5, request)
-
- mgr = FakeChildManager(self.gitlab)
- args = mgr._set_parent_args(name="name")
- self.assertEqual(args, {"name": "name"})
-
- with HTTMock(resp_create):
- o = FakeObjectManager(self.gitlab).create({"name": "name"})
- args = o.children._set_parent_args(name="name")
- self.assertEqual(args, {"name": "name", "parent_id": 1})
-
- def test_constructor(self):
- self.assertRaises(AttributeError, BaseManager, self.gitlab)
-
- @urlmatch(scheme="http", netloc="localhost", path="/api/v3/fake/1",
- method="get")
- def resp_get(url, request):
- headers = {'content-type': 'application/json'}
- content = '{"id": 1, "name": "fake_name"}'.encode("utf-8")
- return response(200, content, headers, None, 5, request)
-
- with HTTMock(resp_get):
- mgr = FakeObjectManager(self.gitlab)
- fake_obj = mgr.get(1)
- self.assertEqual(fake_obj.id, 1)
- self.assertEqual(fake_obj.name, "fake_name")
- self.assertEqual(mgr.gitlab, self.gitlab)
- self.assertEqual(mgr.args, [])
- self.assertEqual(mgr.parent, None)
-
- self.assertIsInstance(fake_obj.children, FakeChildManager)
- self.assertEqual(fake_obj.children.gitlab, self.gitlab)
- self.assertEqual(fake_obj.children.parent, fake_obj)
- self.assertEqual(len(fake_obj.children.args), 1)
-
- fake_child = fake_obj.children.get(1)
- self.assertEqual(fake_child.id, 1)
- self.assertEqual(fake_child.name, "fake_name")
-
- def test_get(self):
- mgr = FakeObjectManager(self.gitlab)
- FakeObject.canGet = False
- self.assertRaises(NotImplementedError, mgr.get, 1)
-
- @urlmatch(scheme="http", netloc="localhost", path="/api/v3/fake/1",
- method="get")
- def resp_get(url, request):
- headers = {'content-type': 'application/json'}
- content = '{"id": 1, "name": "fake_name"}'.encode("utf-8")
- return response(200, content, headers, None, 5, request)
-
- with HTTMock(resp_get):
- FakeObject.canGet = True
- mgr = FakeObjectManager(self.gitlab)
- fake_obj = mgr.get(1)
- self.assertIsInstance(fake_obj, FakeObject)
- self.assertEqual(fake_obj.id, 1)
- self.assertEqual(fake_obj.name, "fake_name")
-
- def test_list(self):
- mgr = FakeObjectManager(self.gitlab)
- FakeObject.canList = False
- self.assertRaises(NotImplementedError, mgr.list)
-
- @urlmatch(scheme="http", netloc="localhost", path="/api/v3/fake",
- method="get")
- def resp_get(url, request):
- headers = {'content-type': 'application/json'}
- content = ('[{"id": 1, "name": "fake_name1"},'
- '{"id": 2, "name": "fake_name2"}]')
- content = content.encode("utf-8")
- return response(200, content, headers, None, 5, request)
-
- with HTTMock(resp_get):
- FakeObject.canList = True
- mgr = FakeObjectManager(self.gitlab)
- fake_list = mgr.list()
- self.assertEqual(len(fake_list), 2)
- self.assertIsInstance(fake_list[0], FakeObject)
- self.assertEqual(fake_list[0].id, 1)
- self.assertEqual(fake_list[0].name, "fake_name1")
- self.assertIsInstance(fake_list[1], FakeObject)
- self.assertEqual(fake_list[1].id, 2)
- self.assertEqual(fake_list[1].name, "fake_name2")
-
- def test_create(self):
- mgr = FakeObjectManager(self.gitlab)
- FakeObject.canCreate = False
- self.assertRaises(NotImplementedError, mgr.create, {'name': 'name'})
-
- @urlmatch(scheme="http", netloc="localhost", path="/api/v3/fake",
- method="post")
- def resp_post(url, request):
- headers = {'content-type': 'application/json'}
- data = '{"name": "fake_name"}'
- content = '{"id": 1, "name": "fake_name"}'.encode("utf-8")
- return response(201, content, headers, data, 5, request)
-
- with HTTMock(resp_post):
- FakeObject.canCreate = True
- mgr = FakeObjectManager(self.gitlab)
- fake_obj = mgr.create({'name': 'fake_name'})
- self.assertIsInstance(fake_obj, FakeObject)
- self.assertEqual(fake_obj.id, 1)
- self.assertEqual(fake_obj.name, "fake_name")
-
- def test_project_manager_owned(self):
- mgr = ProjectManager(self.gitlab)
-
- @urlmatch(scheme="http", netloc="localhost",
- path="/api/v3/projects/owned", method="get")
- def resp_get_all(url, request):
- headers = {'content-type': 'application/json'}
- content = ('[{"name": "name1", "id": 1}, '
- '{"name": "name2", "id": 2}]')
- content = content.encode("utf-8")
- return response(200, content, headers, None, 5, request)
-
- with HTTMock(resp_get_all):
- data = mgr.owned()
- self.assertEqual(type(data), list)
- self.assertEqual(2, len(data))
- self.assertEqual(type(data[0]), Project)
- self.assertEqual(type(data[1]), Project)
- self.assertEqual(data[0].name, "name1")
- self.assertEqual(data[1].name, "name2")
- self.assertEqual(data[0].id, 1)
- self.assertEqual(data[1].id, 2)
-
- def test_project_manager_all(self):
- mgr = ProjectManager(self.gitlab)
-
- @urlmatch(scheme="http", netloc="localhost",
- path="/api/v3/projects/all", method="get")
- def resp_get_all(url, request):
- headers = {'content-type': 'application/json'}
- content = ('[{"name": "name1", "id": 1}, '
- '{"name": "name2", "id": 2}]')
- content = content.encode("utf-8")
- return response(200, content, headers, None, 5, request)
-
- with HTTMock(resp_get_all):
- data = mgr.all()
- self.assertEqual(type(data), list)
- self.assertEqual(2, len(data))
- self.assertEqual(type(data[0]), Project)
- self.assertEqual(type(data[1]), Project)
- self.assertEqual(data[0].name, "name1")
- self.assertEqual(data[1].name, "name2")
- self.assertEqual(data[0].id, 1)
- self.assertEqual(data[1].id, 2)
-
- def test_project_manager_search(self):
- mgr = ProjectManager(self.gitlab)
-
- @urlmatch(scheme="http", netloc="localhost", path="/api/v3/projects",
- query="search=foo", method="get")
- def resp_get_all(url, request):
- headers = {'content-type': 'application/json'}
- content = ('[{"name": "foo1", "id": 1}, '
- '{"name": "foo2", "id": 2}]')
- content = content.encode("utf-8")
- return response(200, content, headers, None, 5, request)
-
- with HTTMock(resp_get_all):
- data = mgr.list(search='foo')
- self.assertEqual(type(data), list)
- self.assertEqual(2, len(data))
- self.assertEqual(type(data[0]), Project)
- self.assertEqual(type(data[1]), Project)
- self.assertEqual(data[0].name, "foo1")
- self.assertEqual(data[1].name, "foo2")
- self.assertEqual(data[0].id, 1)
- self.assertEqual(data[1].id, 2)
-
- def test_user_manager_search(self):
- mgr = UserManager(self.gitlab)
-
- @urlmatch(scheme="http", netloc="localhost", path="/api/v3/users",
- query="search=foo", method="get")
- def resp_get_search(url, request):
- headers = {'content-type': 'application/json'}
- content = ('[{"name": "foo1", "id": 1}, '
- '{"name": "foo2", "id": 2}]')
- content = content.encode("utf-8")
- return response(200, content, headers, None, 5, request)
-
- with HTTMock(resp_get_search):
- data = mgr.search('foo')
- self.assertEqual(type(data), list)
- self.assertEqual(2, len(data))
- self.assertEqual(type(data[0]), User)
- self.assertEqual(type(data[1]), User)
- self.assertEqual(data[0].name, "foo1")
- self.assertEqual(data[1].name, "foo2")
- self.assertEqual(data[0].id, 1)
- self.assertEqual(data[1].id, 2)
-
- def test_user_manager_get_by_username(self):
- mgr = UserManager(self.gitlab)
-
- @urlmatch(scheme="http", netloc="localhost", path="/api/v3/users",
- query="username=foo", method="get")
- def resp_get_username(url, request):
- headers = {'content-type': 'application/json'}
- content = '[{"name": "foo", "id": 1}]'.encode("utf-8")
- return response(200, content, headers, None, 5, request)
-
- with HTTMock(resp_get_username):
- data = mgr.get_by_username('foo')
- self.assertEqual(type(data), User)
- self.assertEqual(data.name, "foo")
- self.assertEqual(data.id, 1)
-
- @urlmatch(scheme="http", netloc="localhost", path="/api/v3/users",
- query="username=foo", method="get")
- def resp_get_username_nomatch(url, request):
- headers = {'content-type': 'application/json'}
- content = '[]'.encode("utf-8")
- return response(200, content, headers, None, 5, request)
-
- with HTTMock(resp_get_username_nomatch):
- self.assertRaises(GitlabGetError, mgr.get_by_username, 'foo')
-
- def test_group_manager_search(self):
- mgr = GroupManager(self.gitlab)
-
- @urlmatch(scheme="http", netloc="localhost", path="/api/v3/groups",
- query="search=foo", method="get")
- def resp_get_search(url, request):
- headers = {'content-type': 'application/json'}
- content = ('[{"name": "foo1", "id": 1}, '
- '{"name": "foo2", "id": 2}]')
- content = content.encode("utf-8")
- return response(200, content, headers, None, 5, request)
-
- with HTTMock(resp_get_search):
- data = mgr.search('foo')
- self.assertEqual(type(data), list)
- self.assertEqual(2, len(data))
- self.assertEqual(type(data[0]), Group)
- self.assertEqual(type(data[1]), Group)
- self.assertEqual(data[0].name, "foo1")
- self.assertEqual(data[1].name, "foo2")
- self.assertEqual(data[0].id, 1)
- self.assertEqual(data[1].id, 2)
diff --git a/gitlab/v3/__init__.py b/gitlab/v3/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/gitlab/v3/__init__.py
+++ /dev/null
diff --git a/gitlab/v3/cli.py b/gitlab/v3/cli.py
deleted file mode 100644
index 94fa03c..0000000
--- a/gitlab/v3/cli.py
+++ /dev/null
@@ -1,524 +0,0 @@
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-#
-# Copyright (C) 2013-2017 Gauvain Pocentek <gauvain@pocentek.net>
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Lesser General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU Lesser General Public License for more details.
-#
-# 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/>.
-
-from __future__ import print_function
-from __future__ import absolute_import
-import inspect
-import operator
-import sys
-
-import six
-
-import gitlab
-import gitlab.base
-from gitlab import cli
-import gitlab.v3.objects
-
-
-EXTRA_ACTIONS = {
- gitlab.v3.objects.Group: {
- 'search': {'required': ['query']}},
- gitlab.v3.objects.ProjectBranch: {
- 'protect': {'required': ['id', 'project-id']},
- 'unprotect': {'required': ['id', 'project-id']}},
- gitlab.v3.objects.ProjectBuild: {
- 'cancel': {'required': ['id', 'project-id']},
- 'retry': {'required': ['id', 'project-id']},
- 'artifacts': {'required': ['id', 'project-id']},
- 'trace': {'required': ['id', 'project-id']}},
- gitlab.v3.objects.ProjectCommit: {
- 'diff': {'required': ['id', 'project-id']},
- 'blob': {'required': ['id', 'project-id', 'filepath']},
- 'builds': {'required': ['id', 'project-id']},
- 'cherrypick': {'required': ['id', 'project-id', 'branch']}},
- gitlab.v3.objects.ProjectIssue: {
- 'subscribe': {'required': ['id', 'project-id']},
- 'unsubscribe': {'required': ['id', 'project-id']},
- 'move': {'required': ['id', 'project-id', 'to-project-id']}},
- gitlab.v3.objects.ProjectMergeRequest: {
- 'closes-issues': {'required': ['id', 'project-id']},
- 'cancel': {'required': ['id', 'project-id']},
- 'merge': {'required': ['id', 'project-id'],
- 'optional': ['merge-commit-message',
- 'should-remove-source-branch',
- 'merged-when-build-succeeds']}},
- gitlab.v3.objects.ProjectMilestone: {
- 'issues': {'required': ['id', 'project-id']}},
- gitlab.v3.objects.Project: {
- 'search': {'required': ['query']},
- 'owned': {},
- 'all': {'optional': [('all', bool)]},
- 'starred': {},
- 'star': {'required': ['id']},
- 'unstar': {'required': ['id']},
- 'archive': {'required': ['id']},
- 'unarchive': {'required': ['id']},
- 'share': {'required': ['id', 'group-id', 'group-access']},
- 'unshare': {'required': ['id', 'group-id']},
- 'upload': {'required': ['id', 'filename', 'filepath']}},
- gitlab.v3.objects.User: {
- 'block': {'required': ['id']},
- 'unblock': {'required': ['id']},
- 'search': {'required': ['query']},
- 'get-by-username': {'required': ['query']}},
-}
-
-
-class GitlabCLI(object):
- def _get_id(self, cls, args):
- try:
- id = args.pop(cls.idAttr)
- except Exception:
- cli.die("Missing --%s argument" % cls.idAttr.replace('_', '-'))
-
- return id
-
- def do_create(self, cls, gl, what, args):
- if not cls.canCreate:
- cli.die("%s objects can't be created" % what)
-
- try:
- o = cls.create(gl, args)
- except Exception as e:
- cli.die("Impossible to create object", e)
-
- return o
-
- def do_list(self, cls, gl, what, args):
- if not cls.canList:
- cli.die("%s objects can't be listed" % what)
-
- try:
- l = cls.list(gl, **args)
- except Exception as e:
- cli.die("Impossible to list objects", e)
-
- return l
-
- def do_get(self, cls, gl, what, args):
- if cls.canGet is False:
- cli.die("%s objects can't be retrieved" % what)
-
- id = None
- if cls not in [gitlab.v3.objects.CurrentUser] and cls.getRequiresId:
- id = self._get_id(cls, args)
-
- try:
- o = cls.get(gl, id, **args)
- except Exception as e:
- cli.die("Impossible to get object", e)
-
- return o
-
- def do_delete(self, cls, gl, what, args):
- if not cls.canDelete:
- cli.die("%s objects can't be deleted" % what)
-
- id = args.pop(cls.idAttr)
- try:
- gl.delete(cls, id, **args)
- except Exception as e:
- cli.die("Impossible to destroy object", e)
-
- def do_update(self, cls, gl, what, args):
- if not cls.canUpdate:
- cli.die("%s objects can't be updated" % what)
-
- o = self.do_get(cls, gl, what, args)
- try:
- for k, v in args.items():
- o.__dict__[k] = v
- o.save()
- except Exception as e:
- cli.die("Impossible to update object", e)
-
- return o
-
- def do_group_search(self, cls, gl, what, args):
- try:
- return gl.groups.search(args['query'])
- except Exception as e:
- cli.die("Impossible to search projects", e)
-
- def do_project_search(self, cls, gl, what, args):
- try:
- return gl.projects.search(args['query'])
- except Exception as e:
- cli.die("Impossible to search projects", e)
-
- def do_project_all(self, cls, gl, what, args):
- try:
- return gl.projects.all(all=args.get('all', False))
- except Exception as e:
- cli.die("Impossible to list all projects", e)
-
- def do_project_starred(self, cls, gl, what, args):
- try:
- return gl.projects.starred()
- except Exception as e:
- cli.die("Impossible to list starred projects", e)
-
- def do_project_owned(self, cls, gl, what, args):
- try:
- return gl.projects.owned()
- except Exception as e:
- cli.die("Impossible to list owned projects", e)
-
- def do_project_star(self, cls, gl, what, args):
- try:
- o = self.do_get(cls, gl, what, args)
- o.star()
- except Exception as e:
- cli.die("Impossible to star project", e)
-
- def do_project_unstar(self, cls, gl, what, args):
- try:
- o = self.do_get(cls, gl, what, args)
- o.unstar()
- except Exception as e:
- cli.die("Impossible to unstar project", e)
-
- def do_project_archive(self, cls, gl, what, args):
- try:
- o = self.do_get(cls, gl, what, args)
- o.archive_()
- except Exception as e:
- cli.die("Impossible to archive project", e)
-
- def do_project_unarchive(self, cls, gl, what, args):
- try:
- o = self.do_get(cls, gl, what, args)
- o.unarchive_()
- except Exception as e:
- cli.die("Impossible to unarchive project", e)
-
- def do_project_share(self, cls, gl, what, args):
- try:
- o = self.do_get(cls, gl, what, args)
- o.share(args['group_id'], args['group_access'])
- except Exception as e:
- cli.die("Impossible to share project", e)
-
- def do_project_unshare(self, cls, gl, what, args):
- try:
- o = self.do_get(cls, gl, what, args)
- o.unshare(args['group_id'])
- except Exception as e:
- cli.die("Impossible to unshare project", e)
-
- def do_user_block(self, cls, gl, what, args):
- try:
- o = self.do_get(cls, gl, what, args)
- o.block()
- except Exception as e:
- cli.die("Impossible to block user", e)
-
- def do_user_unblock(self, cls, gl, what, args):
- try:
- o = self.do_get(cls, gl, what, args)
- o.unblock()
- except Exception as e:
- cli.die("Impossible to block user", e)
-
- def do_project_commit_diff(self, cls, gl, what, args):
- try:
- o = self.do_get(cls, gl, what, args)
- return [x['diff'] for x in o.diff()]
- except Exception as e:
- cli.die("Impossible to get commit diff", e)
-
- def do_project_commit_blob(self, cls, gl, what, args):
- try:
- o = self.do_get(cls, gl, what, args)
- return o.blob(args['filepath'])
- except Exception as e:
- cli.die("Impossible to get commit blob", e)
-
- def do_project_commit_builds(self, cls, gl, what, args):
- try:
- o = self.do_get(cls, gl, what, args)
- return o.builds()
- except Exception as e:
- cli.die("Impossible to get commit builds", e)
-
- def do_project_commit_cherrypick(self, cls, gl, what, args):
- try:
- o = self.do_get(cls, gl, what, args)
- o.cherry_pick(branch=args['branch'])
- except Exception as e:
- cli.die("Impossible to cherry-pick commit", e)
-
- def do_project_build_cancel(self, cls, gl, what, args):
- try:
- o = self.do_get(cls, gl, what, args)
- return o.cancel()
- except Exception as e:
- cli.die("Impossible to cancel project build", e)
-
- def do_project_build_retry(self, cls, gl, what, args):
- try:
- o = self.do_get(cls, gl, what, args)
- return o.retry()
- except Exception as e:
- cli.die("Impossible to retry project build", e)
-
- def do_project_build_artifacts(self, cls, gl, what, args):
- try:
- o = self.do_get(cls, gl, what, args)
- return o.artifacts()
- except Exception as e:
- cli.die("Impossible to get project build artifacts", e)
-
- def do_project_build_trace(self, cls, gl, what, args):
- try:
- o = self.do_get(cls, gl, what, args)
- return o.trace()
- except Exception as e:
- cli.die("Impossible to get project build trace", e)
-
- def do_project_issue_subscribe(self, cls, gl, what, args):
- try:
- o = self.do_get(cls, gl, what, args)
- o.subscribe()
- except Exception as e:
- cli.die("Impossible to subscribe to issue", e)
-
- def do_project_issue_unsubscribe(self, cls, gl, what, args):
- try:
- o = self.do_get(cls, gl, what, args)
- o.unsubscribe()
- except Exception as e:
- cli.die("Impossible to subscribe to issue", e)
-
- def do_project_issue_move(self, cls, gl, what, args):
- try:
- o = self.do_get(cls, gl, what, args)
- o.move(args['to_project_id'])
- except Exception as e:
- cli.die("Impossible to move issue", e)
-
- def do_project_merge_request_closesissues(self, cls, gl, what, args):
- try:
- o = self.do_get(cls, gl, what, args)
- return o.closes_issues()
- except Exception as e:
- cli.die("Impossible to list issues closed by merge request", e)
-
- def do_project_merge_request_cancel(self, cls, gl, what, args):
- try:
- o = self.do_get(cls, gl, what, args)
- return o.cancel_merge_when_build_succeeds()
- except Exception as e:
- cli.die("Impossible to cancel merge request", e)
-
- def do_project_merge_request_merge(self, cls, gl, what, args):
- try:
- o = self.do_get(cls, gl, what, args)
- should_remove = args.get('should_remove_source_branch', False)
- build_succeeds = args.get('merged_when_build_succeeds', False)
- return o.merge(
- merge_commit_message=args.get('merge_commit_message', ''),
- should_remove_source_branch=should_remove,
- merged_when_build_succeeds=build_succeeds)
- except Exception as e:
- cli.die("Impossible to validate merge request", e)
-
- def do_project_milestone_issues(self, cls, gl, what, args):
- try:
- o = self.do_get(cls, gl, what, args)
- return o.issues()
- except Exception as e:
- cli.die("Impossible to get milestone issues", e)
-
- def do_user_search(self, cls, gl, what, args):
- try:
- return gl.users.search(args['query'])
- except Exception as e:
- cli.die("Impossible to search users", e)
-
- def do_user_getbyusername(self, cls, gl, what, args):
- try:
- return gl.users.search(args['query'])
- except Exception as e:
- cli.die("Impossible to get user %s" % args['query'], e)
-
- def do_project_upload(self, cls, gl, what, args):
- try:
- project = gl.projects.get(args["id"])
- except Exception as e:
- cli.die("Could not load project '{!r}'".format(args["id"]), e)
-
- try:
- res = project.upload(filename=args["filename"],
- filepath=args["filepath"])
- except Exception as e:
- cli.die("Could not upload file into project", e)
-
- return res
-
-
-def _populate_sub_parser_by_class(cls, sub_parser):
- for action_name in ['list', 'get', 'create', 'update', 'delete']:
- attr = 'can' + action_name.capitalize()
- if not getattr(cls, attr):
- continue
- sub_parser_action = sub_parser.add_parser(action_name)
- [sub_parser_action.add_argument("--%s" % x.replace('_', '-'),
- required=True)
- for x in cls.requiredUrlAttrs]
- sub_parser_action.add_argument("--sudo", required=False)
-
- if action_name == "list":
- [sub_parser_action.add_argument("--%s" % x.replace('_', '-'),
- required=True)
- for x in cls.requiredListAttrs]
- sub_parser_action.add_argument("--page", required=False)
- sub_parser_action.add_argument("--per-page", required=False)
- sub_parser_action.add_argument("--all", required=False,
- action='store_true')
-
- if action_name in ["get", "delete"]:
- if cls not in [gitlab.v3.objects.CurrentUser]:
- if cls.getRequiresId:
- id_attr = cls.idAttr.replace('_', '-')
- sub_parser_action.add_argument("--%s" % id_attr,
- required=True)
- [sub_parser_action.add_argument("--%s" % x.replace('_', '-'),
- required=True)
- for x in cls.requiredGetAttrs if x != cls.idAttr]
-
- if action_name == "get":
- [sub_parser_action.add_argument("--%s" % x.replace('_', '-'),
- required=False)
- for x in cls.optionalGetAttrs]
-
- if action_name == "list":
- [sub_parser_action.add_argument("--%s" % x.replace('_', '-'),
- required=False)
- for x in cls.optionalListAttrs]
-
- if action_name == "create":
- [sub_parser_action.add_argument("--%s" % x.replace('_', '-'),
- required=True)
- for x in cls.requiredCreateAttrs]
- [sub_parser_action.add_argument("--%s" % x.replace('_', '-'),
- required=False)
- for x in cls.optionalCreateAttrs]
-
- if action_name == "update":
- id_attr = cls.idAttr.replace('_', '-')
- sub_parser_action.add_argument("--%s" % id_attr,
- required=True)
-
- attrs = (cls.requiredUpdateAttrs
- if (cls.requiredUpdateAttrs or cls.optionalUpdateAttrs)
- else cls.requiredCreateAttrs)
- [sub_parser_action.add_argument("--%s" % x.replace('_', '-'),
- required=True)
- for x in attrs if x != cls.idAttr]
-
- attrs = (cls.optionalUpdateAttrs
- if (cls.requiredUpdateAttrs or cls.optionalUpdateAttrs)
- else cls.optionalCreateAttrs)
- [sub_parser_action.add_argument("--%s" % x.replace('_', '-'),
- required=False)
- for x in attrs]
-
- if cls in EXTRA_ACTIONS:
- def _add_arg(parser, required, data):
- extra_args = {}
- if isinstance(data, tuple):
- if data[1] is bool:
- extra_args = {'action': 'store_true'}
- data = data[0]
-
- parser.add_argument("--%s" % data, required=required, **extra_args)
-
- for action_name in sorted(EXTRA_ACTIONS[cls]):
- sub_parser_action = sub_parser.add_parser(action_name)
- d = EXTRA_ACTIONS[cls][action_name]
- [_add_arg(sub_parser_action, True, arg)
- for arg in d.get('required', [])]
- [_add_arg(sub_parser_action, False, arg)
- for arg in d.get('optional', [])]
-
-
-def extend_parser(parser):
- subparsers = parser.add_subparsers(title='object', dest='what',
- help="Object to manipulate.")
- subparsers.required = True
-
- # populate argparse for all Gitlab Object
- classes = []
- for cls in gitlab.v3.objects.__dict__.values():
- try:
- if gitlab.base.GitlabObject in inspect.getmro(cls):
- classes.append(cls)
- except AttributeError:
- pass
- classes.sort(key=operator.attrgetter("__name__"))
-
- for cls in classes:
- arg_name = cli.cls_to_what(cls)
- object_group = subparsers.add_parser(arg_name)
-
- object_subparsers = object_group.add_subparsers(
- dest='action', help="Action to execute.")
- _populate_sub_parser_by_class(cls, object_subparsers)
- object_subparsers.required = True
-
- return parser
-
-
-def run(gl, what, action, args, verbose, *fargs, **kwargs):
- try:
- cls = gitlab.v3.objects.__dict__[cli.what_to_cls(what)]
- except ImportError:
- cli.die("Unknown object: %s" % what)
-
- g_cli = GitlabCLI()
-
- method = None
- what = what.replace('-', '_')
- action = action.lower().replace('-', '')
- for test in ["do_%s_%s" % (what, action),
- "do_%s" % action]:
- if hasattr(g_cli, test):
- method = test
- break
-
- if method is None:
- sys.stderr.write("Don't know how to deal with this!\n")
- sys.exit(1)
-
- ret_val = getattr(g_cli, method)(cls, gl, what, args)
-
- if isinstance(ret_val, list):
- for o in ret_val:
- if isinstance(o, gitlab.GitlabObject):
- o.display(verbose)
- print("")
- else:
- print(o)
- elif isinstance(ret_val, dict):
- for k, v in six.iteritems(ret_val):
- print("{} = {}".format(k, v))
- elif isinstance(ret_val, gitlab.base.GitlabObject):
- ret_val.display(verbose)
- elif isinstance(ret_val, six.string_types):
- print(ret_val)
diff --git a/gitlab/v3/objects.py b/gitlab/v3/objects.py
deleted file mode 100644
index dec2933..0000000
--- a/gitlab/v3/objects.py
+++ /dev/null
@@ -1,2389 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# Copyright (C) 2013-2017 Gauvain Pocentek <gauvain@pocentek.net>
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Lesser General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU Lesser General Public License for more details.
-#
-# 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/>.
-
-from __future__ import print_function
-from __future__ import absolute_import
-import base64
-import json
-
-import six
-from six.moves import urllib
-
-import gitlab
-from gitlab.base import * # noqa
-from gitlab.exceptions import * # noqa
-from gitlab import utils
-
-
-class SidekiqManager(object):
- """Manager for the Sidekiq methods.
-
- This manager doesn't actually manage objects but provides helper fonction
- for the sidekiq metrics API.
- """
- def __init__(self, gl):
- """Constructs a Sidekiq manager.
-
- Args:
- gl (gitlab.Gitlab): Gitlab object referencing the GitLab server.
- """
- self.gitlab = gl
-
- def _simple_get(self, url, **kwargs):
- r = self.gitlab._raw_get(url, **kwargs)
- raise_error_from_response(r, GitlabGetError)
- return r.json()
-
- def queue_metrics(self, **kwargs):
- """Returns the registred queues information."""
- return self._simple_get('/sidekiq/queue_metrics', **kwargs)
-
- def process_metrics(self, **kwargs):
- """Returns the registred sidekiq workers."""
- return self._simple_get('/sidekiq/process_metrics', **kwargs)
-
- def job_stats(self, **kwargs):
- """Returns statistics about the jobs performed."""
- return self._simple_get('/sidekiq/job_stats', **kwargs)
-
- def compound_metrics(self, **kwargs):
- """Returns all available metrics and statistics."""
- return self._simple_get('/sidekiq/compound_metrics', **kwargs)
-
-
-class UserEmail(GitlabObject):
- _url = '/users/%(user_id)s/emails'
- canUpdate = False
- shortPrintAttr = 'email'
- requiredUrlAttrs = ['user_id']
- requiredCreateAttrs = ['email']
-
-
-class UserEmailManager(BaseManager):
- obj_cls = UserEmail
-
-
-class UserKey(GitlabObject):
- _url = '/users/%(user_id)s/keys'
- canGet = 'from_list'
- canUpdate = False
- requiredUrlAttrs = ['user_id']
- requiredCreateAttrs = ['title', 'key']
-
-
-class UserKeyManager(BaseManager):
- obj_cls = UserKey
-
-
-class UserProject(GitlabObject):
- _url = '/projects/user/%(user_id)s'
- _constructorTypes = {'owner': 'User', 'namespace': 'Group'}
- canUpdate = False
- canDelete = False
- canList = False
- canGet = False
- requiredUrlAttrs = ['user_id']
- requiredCreateAttrs = ['name']
- optionalCreateAttrs = ['default_branch', 'issues_enabled', 'wall_enabled',
- 'merge_requests_enabled', 'wiki_enabled',
- 'snippets_enabled', 'public', 'visibility_level',
- 'description', 'builds_enabled', 'public_builds',
- 'import_url', 'only_allow_merge_if_build_succeeds']
-
-
-class UserProjectManager(BaseManager):
- obj_cls = UserProject
-
-
-class User(GitlabObject):
- _url = '/users'
- shortPrintAttr = 'username'
- requiredCreateAttrs = ['email', 'username', 'name']
- optionalCreateAttrs = ['password', 'reset_password', 'skype', 'linkedin',
- 'twitter', 'projects_limit', 'extern_uid',
- 'provider', 'bio', 'admin', 'can_create_group',
- 'website_url', 'confirm', 'external',
- 'organization', 'location']
- requiredUpdateAttrs = ['email', 'username', 'name']
- optionalUpdateAttrs = ['password', 'skype', 'linkedin', 'twitter',
- 'projects_limit', 'extern_uid', 'provider', 'bio',
- 'admin', 'can_create_group', 'website_url',
- 'confirm', 'external', 'organization', 'location']
- managers = (
- ('emails', 'UserEmailManager', [('user_id', 'id')]),
- ('keys', 'UserKeyManager', [('user_id', 'id')]),
- ('projects', 'UserProjectManager', [('user_id', 'id')]),
- )
-
- def _data_for_gitlab(self, extra_parameters={}, update=False,
- as_json=True):
- if hasattr(self, 'confirm'):
- self.confirm = str(self.confirm).lower()
- return super(User, self)._data_for_gitlab(extra_parameters)
-
- def block(self, **kwargs):
- """Blocks the user."""
- url = '/users/%s/block' % self.id
- r = self.gitlab._raw_put(url, **kwargs)
- raise_error_from_response(r, GitlabBlockError)
- self.state = 'blocked'
-
- def unblock(self, **kwargs):
- """Unblocks the user."""
- url = '/users/%s/unblock' % self.id
- r = self.gitlab._raw_put(url, **kwargs)
- raise_error_from_response(r, GitlabUnblockError)
- self.state = 'active'
-
- def __eq__(self, other):
- if type(other) is type(self):
- selfdict = self.as_dict()
- otherdict = other.as_dict()
- selfdict.pop('password', None)
- otherdict.pop('password', None)
- return selfdict == otherdict
- return False
-
-
-class UserManager(BaseManager):
- obj_cls = User
-
- def search(self, query, **kwargs):
- """Search users.
-
- Args:
- query (str): The query string to send to GitLab for the search.
- all (bool): If True, return all the items, without pagination
- **kwargs: Additional arguments to send to GitLab.
-
- Returns:
- list(User): A list of matching users.
-
- Raises:
- GitlabConnectionError: If the server cannot be reached.
- GitlabListError: If the server fails to perform the request.
- """
- url = self.obj_cls._url + '?search=' + query
- return self.gitlab._raw_list(url, self.obj_cls, **kwargs)
-
- def get_by_username(self, username, **kwargs):
- """Get a user by its username.
-
- Args:
- username (str): The name of the user.
- **kwargs: Additional arguments to send to GitLab.
-
- Returns:
- User: The matching user.
-
- Raises:
- GitlabConnectionError: If the server cannot be reached.
- GitlabGetError: If the server fails to perform the request.
- """
- url = self.obj_cls._url + '?username=' + username
- results = self.gitlab._raw_list(url, self.obj_cls, **kwargs)
- assert len(results) in (0, 1)
- try:
- return results[0]
- except IndexError:
- raise GitlabGetError('no such user: ' + username)
-
-
-class CurrentUserEmail(GitlabObject):
- _url = '/user/emails'
- canUpdate = False
- shortPrintAttr = 'email'
- requiredCreateAttrs = ['email']
-
-
-class CurrentUserEmailManager(BaseManager):
- obj_cls = CurrentUserEmail
-
-
-class CurrentUserKey(GitlabObject):
- _url = '/user/keys'
- canUpdate = False
- shortPrintAttr = 'title'
- requiredCreateAttrs = ['title', 'key']
-
-
-class CurrentUserKeyManager(BaseManager):
- obj_cls = CurrentUserKey
-
-
-class CurrentUser(GitlabObject):
- _url = '/user'
- canList = False
- canCreate = False
- canUpdate = False
- canDelete = False
- shortPrintAttr = 'username'
- managers = (
- ('emails', 'CurrentUserEmailManager', [('user_id', 'id')]),
- ('keys', 'CurrentUserKeyManager', [('user_id', 'id')]),
- )
-
-
-class ApplicationSettings(GitlabObject):
- _url = '/application/settings'
- _id_in_update_url = False
- getRequiresId = False
- optionalUpdateAttrs = ['after_sign_out_path',
- 'container_registry_token_expire_delay',
- 'default_branch_protection',
- 'default_project_visibility',
- 'default_projects_limit',
- 'default_snippet_visibility',
- 'domain_blacklist',
- 'domain_blacklist_enabled',
- 'domain_whitelist',
- 'enabled_git_access_protocol',
- 'gravatar_enabled',
- 'home_page_url',
- 'max_attachment_size',
- 'repository_storage',
- 'restricted_signup_domains',
- 'restricted_visibility_levels',
- 'session_expire_delay',
- 'sign_in_text',
- 'signin_enabled',
- 'signup_enabled',
- 'twitter_sharing_enabled',
- 'user_oauth_applications']
- canList = False
- canCreate = False
- canDelete = False
-
- def _data_for_gitlab(self, extra_parameters={}, update=False,
- as_json=True):
- data = (super(ApplicationSettings, self)
- ._data_for_gitlab(extra_parameters, update=update,
- as_json=False))
- if not self.domain_whitelist:
- data.pop('domain_whitelist', None)
- return json.dumps(data)
-
-
-class ApplicationSettingsManager(BaseManager):
- obj_cls = ApplicationSettings
-
-
-class BroadcastMessage(GitlabObject):
- _url = '/broadcast_messages'
- requiredCreateAttrs = ['message']
- optionalCreateAttrs = ['starts_at', 'ends_at', 'color', 'font']
- requiredUpdateAttrs = []
- optionalUpdateAttrs = ['message', 'starts_at', 'ends_at', 'color', 'font']
-
-
-class BroadcastMessageManager(BaseManager):
- obj_cls = BroadcastMessage
-
-
-class DeployKey(GitlabObject):
- _url = '/deploy_keys'
- canGet = 'from_list'
- canCreate = False
- canUpdate = False
- canDelete = False
-
-
-class DeployKeyManager(BaseManager):
- obj_cls = DeployKey
-
-
-class NotificationSettings(GitlabObject):
- _url = '/notification_settings'
- _id_in_update_url = False
- getRequiresId = False
- optionalUpdateAttrs = ['level',
- 'notification_email',
- 'new_note',
- 'new_issue',
- 'reopen_issue',
- 'close_issue',
- 'reassign_issue',
- 'new_merge_request',
- 'reopen_merge_request',
- 'close_merge_request',
- 'reassign_merge_request',
- 'merge_merge_request']
- canList = False
- canCreate = False
- canDelete = False
-
-
-class NotificationSettingsManager(BaseManager):
- obj_cls = NotificationSettings
-
-
-class Gitignore(GitlabObject):
- _url = '/templates/gitignores'
- canDelete = False
- canUpdate = False
- canCreate = False
- idAttr = 'name'
-
-
-class GitignoreManager(BaseManager):
- obj_cls = Gitignore
-
-
-class Gitlabciyml(GitlabObject):
- _url = '/templates/gitlab_ci_ymls'
- canDelete = False
- canUpdate = False
- canCreate = False
- idAttr = 'name'
-
-
-class GitlabciymlManager(BaseManager):
- obj_cls = Gitlabciyml
-
-
-class GroupIssue(GitlabObject):
- _url = '/groups/%(group_id)s/issues'
- canGet = 'from_list'
- canCreate = False
- canUpdate = False
- canDelete = False
- requiredUrlAttrs = ['group_id']
- optionalListAttrs = ['state', 'labels', 'milestone', 'order_by', 'sort']
-
-
-class GroupIssueManager(BaseManager):
- obj_cls = GroupIssue
-
-
-class GroupMember(GitlabObject):
- _url = '/groups/%(group_id)s/members'
- canGet = 'from_list'
- requiredUrlAttrs = ['group_id']
- requiredCreateAttrs = ['access_level', 'user_id']
- optionalCreateAttrs = ['expires_at']
- requiredUpdateAttrs = ['access_level']
- optionalCreateAttrs = ['expires_at']
- shortPrintAttr = 'username'
-
- def _update(self, **kwargs):
- self.user_id = self.id
- super(GroupMember, self)._update(**kwargs)
-
-
-class GroupMemberManager(BaseManager):
- obj_cls = GroupMember
-
-
-class GroupNotificationSettings(NotificationSettings):
- _url = '/groups/%(group_id)s/notification_settings'
- requiredUrlAttrs = ['group_id']
-
-
-class GroupNotificationSettingsManager(BaseManager):
- obj_cls = GroupNotificationSettings
-
-
-class GroupAccessRequest(GitlabObject):
- _url = '/groups/%(group_id)s/access_requests'
- canGet = 'from_list'
- canUpdate = False
-
- def approve(self, access_level=gitlab.DEVELOPER_ACCESS, **kwargs):
- """Approve an access request.
-
- Args:
- 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.
- """
-
- url = ('/groups/%(group_id)s/access_requests/%(id)s/approve' %
- {'group_id': self.group_id, 'id': self.id})
- data = {'access_level': access_level}
- r = self.gitlab._raw_put(url, data=data, **kwargs)
- raise_error_from_response(r, GitlabUpdateError, 201)
- self._set_from_dict(r.json())
-
-
-class GroupAccessRequestManager(BaseManager):
- obj_cls = GroupAccessRequest
-
-
-class Hook(GitlabObject):
- _url = '/hooks'
- canUpdate = False
- requiredCreateAttrs = ['url']
- shortPrintAttr = 'url'
-
-
-class HookManager(BaseManager):
- obj_cls = Hook
-
-
-class Issue(GitlabObject):
- _url = '/issues'
- _constructorTypes = {'author': 'User', 'assignee': 'User',
- 'milestone': 'ProjectMilestone'}
- canGet = 'from_list'
- canDelete = False
- canUpdate = False
- canCreate = False
- shortPrintAttr = 'title'
- optionalListAttrs = ['state', 'labels', 'order_by', 'sort']
-
-
-class IssueManager(BaseManager):
- obj_cls = Issue
-
-
-class License(GitlabObject):
- _url = '/licenses'
- canDelete = False
- canUpdate = False
- canCreate = False
- idAttr = 'key'
-
- optionalListAttrs = ['popular']
- optionalGetAttrs = ['project', 'fullname']
-
-
-class LicenseManager(BaseManager):
- obj_cls = License
-
-
-class Snippet(GitlabObject):
- _url = '/snippets'
- _constructorTypes = {'author': 'User'}
- requiredCreateAttrs = ['title', 'file_name', 'content']
- optionalCreateAttrs = ['lifetime', 'visibility_level']
- optionalUpdateAttrs = ['title', 'file_name', 'content', 'visibility_level']
- shortPrintAttr = 'title'
-
- def raw(self, streamed=False, action=None, chunk_size=1024, **kwargs):
- """Return the raw content of a snippet.
-
- Args:
- streamed (bool): If True the data will be processed by chunks of
- `chunk_size` and each chunk is passed to `action` for
- treatment.
- action (callable): Callable responsible of dealing with chunk of
- data.
- chunk_size (int): Size of each chunk.
-
- Returns:
- str: The snippet content.
-
- Raises:
- GitlabConnectionError: If the server cannot be reached.
- GitlabGetError: If the server fails to perform the request.
- """
- url = ("/snippets/%(snippet_id)s/raw" % {'snippet_id': self.id})
- r = self.gitlab._raw_get(url, **kwargs)
- raise_error_from_response(r, GitlabGetError)
- return utils.response_content(r, streamed, action, chunk_size)
-
-
-class SnippetManager(BaseManager):
- obj_cls = Snippet
-
- def public(self, **kwargs):
- """List all the public snippets.
-
- Args:
- all (bool): If True, return all the items, without pagination
- **kwargs: Additional arguments to send to GitLab.
-
- Returns:
- list(gitlab.Gitlab.Snippet): The list of snippets.
- """
- return self.gitlab._raw_list("/snippets/public", Snippet, **kwargs)
-
-
-class Namespace(GitlabObject):
- _url = '/namespaces'
- canGet = 'from_list'
- canUpdate = False
- canDelete = False
- canCreate = False
- optionalListAttrs = ['search']
-
-
-class NamespaceManager(BaseManager):
- obj_cls = Namespace
-
-
-class ProjectBoardList(GitlabObject):
- _url = '/projects/%(project_id)s/boards/%(board_id)s/lists'
- requiredUrlAttrs = ['project_id', 'board_id']
- _constructorTypes = {'label': 'ProjectLabel'}
- requiredCreateAttrs = ['label_id']
- requiredUpdateAttrs = ['position']
-
-
-class ProjectBoardListManager(BaseManager):
- obj_cls = ProjectBoardList
-
-
-class ProjectBoard(GitlabObject):
- _url = '/projects/%(project_id)s/boards'
- requiredUrlAttrs = ['project_id']
- _constructorTypes = {'labels': 'ProjectBoardList'}
- canGet = 'from_list'
- canUpdate = False
- canCreate = False
- canDelete = False
- managers = (
- ('lists', 'ProjectBoardListManager',
- [('project_id', 'project_id'), ('board_id', 'id')]),
- )
-
-
-class ProjectBoardManager(BaseManager):
- obj_cls = ProjectBoard
-
-
-class ProjectBranch(GitlabObject):
- _url = '/projects/%(project_id)s/repository/branches'
- _constructorTypes = {'author': 'User', "committer": "User"}
-
- idAttr = 'name'
- canUpdate = False
- requiredUrlAttrs = ['project_id']
- requiredCreateAttrs = ['branch_name', 'ref']
-
- def protect(self, protect=True, **kwargs):
- """Protects the branch."""
- url = self._url % {'project_id': self.project_id}
- action = 'protect' if protect else 'unprotect'
- url = "%s/%s/%s" % (url, self.name, action)
- r = self.gitlab._raw_put(url, data=None, content_type=None, **kwargs)
- raise_error_from_response(r, GitlabProtectError)
-
- if protect:
- self.protected = protect
- else:
- del self.protected
-
- def unprotect(self, **kwargs):
- """Unprotects the branch."""
- self.protect(False, **kwargs)
-
-
-class ProjectBranchManager(BaseManager):
- obj_cls = ProjectBranch
-
-
-class ProjectBuild(GitlabObject):
- _url = '/projects/%(project_id)s/builds'
- _constructorTypes = {'user': 'User',
- 'commit': 'ProjectCommit',
- 'runner': 'Runner'}
- requiredUrlAttrs = ['project_id']
- canDelete = False
- canUpdate = False
- canCreate = False
-
- def cancel(self, **kwargs):
- """Cancel the build."""
- url = '/projects/%s/builds/%s/cancel' % (self.project_id, self.id)
- r = self.gitlab._raw_post(url)
- raise_error_from_response(r, GitlabBuildCancelError, 201)
-
- def retry(self, **kwargs):
- """Retry the build."""
- url = '/projects/%s/builds/%s/retry' % (self.project_id, self.id)
- r = self.gitlab._raw_post(url)
- raise_error_from_response(r, GitlabBuildRetryError, 201)
-
- def play(self, **kwargs):
- """Trigger a build explicitly."""
- url = '/projects/%s/builds/%s/play' % (self.project_id, self.id)
- r = self.gitlab._raw_post(url)
- raise_error_from_response(r, GitlabBuildPlayError)
-
- def erase(self, **kwargs):
- """Erase the build (remove build artifacts and trace)."""
- url = '/projects/%s/builds/%s/erase' % (self.project_id, self.id)
- r = self.gitlab._raw_post(url)
- raise_error_from_response(r, GitlabBuildEraseError, 201)
-
- def keep_artifacts(self, **kwargs):
- """Prevent artifacts from being delete when expiration is set.
-
- Raises:
- GitlabConnectionError: If the server cannot be reached.
- GitlabCreateError: If the request failed.
- """
- url = ('/projects/%s/builds/%s/artifacts/keep' %
- (self.project_id, self.id))
- r = self.gitlab._raw_post(url)
- raise_error_from_response(r, GitlabGetError, 200)
-
- def artifacts(self, streamed=False, action=None, chunk_size=1024,
- **kwargs):
- """Get the build artifacts.
-
- Args:
- streamed (bool): If True the data will be processed by chunks of
- `chunk_size` and each chunk is passed to `action` for
- treatment.
- action (callable): Callable responsible of dealing with chunk of
- data.
- chunk_size (int): Size of each chunk.
-
- Returns:
- str: The artifacts if `streamed` is False, None otherwise.
-
- Raises:
- GitlabConnectionError: If the server cannot be reached.
- GitlabGetError: If the artifacts are not available.
- """
- url = '/projects/%s/builds/%s/artifacts' % (self.project_id, self.id)
- r = self.gitlab._raw_get(url, streamed=streamed, **kwargs)
- raise_error_from_response(r, GitlabGetError, 200)
- return utils.response_content(r, streamed, action, chunk_size)
-
- def trace(self, streamed=False, action=None, chunk_size=1024, **kwargs):
- """Get the build trace.
-
- Args:
- streamed (bool): If True the data will be processed by chunks of
- `chunk_size` and each chunk is passed to `action` for
- treatment.
- action (callable): Callable responsible of dealing with chunk of
- data.
- chunk_size (int): Size of each chunk.
-
- Returns:
- str: The trace.
-
- Raises:
- GitlabConnectionError: If the server cannot be reached.
- GitlabGetError: If the trace is not available.
- """
- url = '/projects/%s/builds/%s/trace' % (self.project_id, self.id)
- r = self.gitlab._raw_get(url, streamed=streamed, **kwargs)
- raise_error_from_response(r, GitlabGetError, 200)
- return utils.response_content(r, streamed, action, chunk_size)
-
-
-class ProjectBuildManager(BaseManager):
- obj_cls = ProjectBuild
-
-
-class ProjectCommitStatus(GitlabObject):
- _url = '/projects/%(project_id)s/repository/commits/%(commit_id)s/statuses'
- _create_url = '/projects/%(project_id)s/statuses/%(commit_id)s'
- canUpdate = False
- canDelete = False
- requiredUrlAttrs = ['project_id', 'commit_id']
- optionalGetAttrs = ['ref_name', 'stage', 'name', 'all']
- requiredCreateAttrs = ['state']
- optionalCreateAttrs = ['description', 'name', 'context', 'ref',
- 'target_url']
-
-
-class ProjectCommitStatusManager(BaseManager):
- obj_cls = ProjectCommitStatus
-
-
-class ProjectCommitComment(GitlabObject):
- _url = '/projects/%(project_id)s/repository/commits/%(commit_id)s/comments'
- canUpdate = False
- canGet = False
- canDelete = False
- requiredUrlAttrs = ['project_id', 'commit_id']
- requiredCreateAttrs = ['note']
- optionalCreateAttrs = ['path', 'line', 'line_type']
-
-
-class ProjectCommitCommentManager(BaseManager):
- obj_cls = ProjectCommitComment
-
-
-class ProjectCommit(GitlabObject):
- _url = '/projects/%(project_id)s/repository/commits'
- canDelete = False
- canUpdate = False
- requiredUrlAttrs = ['project_id']
- requiredCreateAttrs = ['branch_name', 'commit_message', 'actions']
- optionalCreateAttrs = ['author_email', 'author_name']
- shortPrintAttr = 'title'
- managers = (
- ('comments', 'ProjectCommitCommentManager',
- [('project_id', 'project_id'), ('commit_id', 'id')]),
- ('statuses', 'ProjectCommitStatusManager',
- [('project_id', 'project_id'), ('commit_id', 'id')]),
- )
-
- def diff(self, **kwargs):
- """Generate the commit diff."""
- url = ('/projects/%(project_id)s/repository/commits/%(commit_id)s/diff'
- % {'project_id': self.project_id, 'commit_id': self.id})
- r = self.gitlab._raw_get(url, **kwargs)
- raise_error_from_response(r, GitlabGetError)
-
- return r.json()
-
- def blob(self, filepath, streamed=False, action=None, chunk_size=1024,
- **kwargs):
- """Generate the content of a file for this commit.
-
- Args:
- filepath (str): Path of the file to request.
- streamed (bool): If True the data will be processed by chunks of
- `chunk_size` and each chunk is passed to `action` for
- treatment.
- action (callable): Callable responsible of dealing with chunk of
- data.
- chunk_size (int): Size of each chunk.
-
- Returns:
- str: The content of the file
-
- Raises:
- GitlabConnectionError: If the server cannot be reached.
- GitlabGetError: If the server fails to perform the request.
- """
- url = ('/projects/%(project_id)s/repository/blobs/%(commit_id)s' %
- {'project_id': self.project_id, 'commit_id': self.id})
- url += '?filepath=%s' % filepath
- r = self.gitlab._raw_get(url, streamed=streamed, **kwargs)
- raise_error_from_response(r, GitlabGetError)
- return utils.response_content(r, streamed, action, chunk_size)
-
- def builds(self, **kwargs):
- """List the build for this commit.
-
- Returns:
- list(ProjectBuild): A list of builds.
-
- Raises:
- GitlabConnectionError: If the server cannot be reached.
- GitlabListError: If the server fails to perform the request.
- """
- url = '/projects/%s/repository/commits/%s/builds' % (self.project_id,
- self.id)
- return self.gitlab._raw_list(url, ProjectBuild, **kwargs)
-
- def cherry_pick(self, branch, **kwargs):
- """Cherry-pick a commit into a branch.
-
- Args:
- branch (str): Name of target branch.
-
- Raises:
- GitlabCherryPickError: If the cherry pick could not be applied.
- """
- url = ('/projects/%s/repository/commits/%s/cherry_pick' %
- (self.project_id, self.id))
-
- r = self.gitlab._raw_post(url, data={'project_id': self.project_id,
- 'branch': branch}, **kwargs)
- errors = {400: GitlabCherryPickError}
- raise_error_from_response(r, errors, expected_code=201)
-
-
-class ProjectCommitManager(BaseManager):
- obj_cls = ProjectCommit
-
-
-class ProjectEnvironment(GitlabObject):
- _url = '/projects/%(project_id)s/environments'
- canGet = 'from_list'
- requiredUrlAttrs = ['project_id']
- requiredCreateAttrs = ['name']
- optionalCreateAttrs = ['external_url']
- optionalUpdateAttrs = ['name', 'external_url']
-
-
-class ProjectEnvironmentManager(BaseManager):
- obj_cls = ProjectEnvironment
-
-
-class ProjectKey(GitlabObject):
- _url = '/projects/%(project_id)s/keys'
- canUpdate = False
- requiredUrlAttrs = ['project_id']
- requiredCreateAttrs = ['title', 'key']
-
-
-class ProjectKeyManager(BaseManager):
- obj_cls = ProjectKey
-
- def enable(self, key_id):
- """Enable a deploy key for a project."""
- url = '/projects/%s/deploy_keys/%s/enable' % (self.parent.id, key_id)
- r = self.gitlab._raw_post(url)
- raise_error_from_response(r, GitlabProjectDeployKeyError, 201)
-
- def disable(self, key_id):
- """Disable a deploy key for a project."""
- url = '/projects/%s/deploy_keys/%s/disable' % (self.parent.id, key_id)
- r = self.gitlab._raw_delete(url)
- raise_error_from_response(r, GitlabProjectDeployKeyError, 200)
-
-
-class ProjectEvent(GitlabObject):
- _url = '/projects/%(project_id)s/events'
- canGet = 'from_list'
- canDelete = False
- canUpdate = False
- canCreate = False
- requiredUrlAttrs = ['project_id']
- shortPrintAttr = 'target_title'
-
-
-class ProjectEventManager(BaseManager):
- obj_cls = ProjectEvent
-
-
-class ProjectFork(GitlabObject):
- _url = '/projects/fork/%(project_id)s'
- canUpdate = False
- canDelete = False
- canList = False
- canGet = False
- requiredUrlAttrs = ['project_id']
- optionalCreateAttrs = ['namespace']
-
-
-class ProjectForkManager(BaseManager):
- obj_cls = ProjectFork
-
-
-class ProjectHook(GitlabObject):
- _url = '/projects/%(project_id)s/hooks'
- requiredUrlAttrs = ['project_id']
- requiredCreateAttrs = ['url']
- optionalCreateAttrs = ['push_events', 'issues_events', 'note_events',
- 'merge_requests_events', 'tag_push_events',
- 'build_events', 'enable_ssl_verification', 'token',
- 'pipeline_events']
- shortPrintAttr = 'url'
-
-
-class ProjectHookManager(BaseManager):
- obj_cls = ProjectHook
-
-
-class ProjectIssueNote(GitlabObject):
- _url = '/projects/%(project_id)s/issues/%(issue_id)s/notes'
- _constructorTypes = {'author': 'User'}
- canDelete = False
- requiredUrlAttrs = ['project_id', 'issue_id']
- requiredCreateAttrs = ['body']
- optionalCreateAttrs = ['created_at']
-
- # file attachment settings (see #56)
- description_attr = "body"
- project_id_attr = "project_id"
-
-
-class ProjectIssueNoteManager(BaseManager):
- obj_cls = ProjectIssueNote
-
-
-class ProjectIssue(GitlabObject):
- _url = '/projects/%(project_id)s/issues/'
- _constructorTypes = {'author': 'User', 'assignee': 'User',
- 'milestone': 'ProjectMilestone'}
- optionalListAttrs = ['state', 'labels', 'milestone', 'iid', 'order_by',
- 'sort']
- requiredUrlAttrs = ['project_id']
- requiredCreateAttrs = ['title']
- optionalCreateAttrs = ['description', 'assignee_id', 'milestone_id',
- 'labels', 'created_at', 'due_date']
- optionalUpdateAttrs = ['title', 'description', 'assignee_id',
- 'milestone_id', 'labels', 'created_at',
- 'updated_at', 'state_event', 'due_date']
- shortPrintAttr = 'title'
- managers = (
- ('notes', 'ProjectIssueNoteManager',
- [('project_id', 'project_id'), ('issue_id', 'id')]),
- )
-
- # file attachment settings (see #56)
- description_attr = "description"
- project_id_attr = "project_id"
-
- def subscribe(self, **kwargs):
- """Subscribe to an issue.
-
- Raises:
- GitlabConnectionError: If the server cannot be reached.
- GitlabSubscribeError: If the subscription cannot be done
- """
- url = ('/projects/%(project_id)s/issues/%(issue_id)s/subscription' %
- {'project_id': self.project_id, 'issue_id': self.id})
-
- r = self.gitlab._raw_post(url, **kwargs)
- raise_error_from_response(r, GitlabSubscribeError, 201)
- self._set_from_dict(r.json())
-
- def unsubscribe(self, **kwargs):
- """Unsubscribe an issue.
-
- Raises:
- GitlabConnectionError: If the server cannot be reached.
- GitlabUnsubscribeError: If the unsubscription cannot be done
- """
- url = ('/projects/%(project_id)s/issues/%(issue_id)s/subscription' %
- {'project_id': self.project_id, 'issue_id': self.id})
-
- r = self.gitlab._raw_delete(url, **kwargs)
- raise_error_from_response(r, GitlabUnsubscribeError)
- self._set_from_dict(r.json())
-
- def move(self, to_project_id, **kwargs):
- """Move the issue to another project.
-
- Raises:
- GitlabConnectionError: If the server cannot be reached.
- """
- url = ('/projects/%(project_id)s/issues/%(issue_id)s/move' %
- {'project_id': self.project_id, 'issue_id': self.id})
-
- data = {'to_project_id': to_project_id}
- data.update(**kwargs)
- r = self.gitlab._raw_post(url, data=data)
- raise_error_from_response(r, GitlabUpdateError, 201)
- self._set_from_dict(r.json())
-
- def todo(self, **kwargs):
- """Create a todo for the issue.
-
- Raises:
- GitlabConnectionError: If the server cannot be reached.
- """
- url = ('/projects/%(project_id)s/issues/%(issue_id)s/todo' %
- {'project_id': self.project_id, 'issue_id': self.id})
- r = self.gitlab._raw_post(url, **kwargs)
- raise_error_from_response(r, GitlabTodoError, [201, 304])
-
- def time_stats(self, **kwargs):
- """Get time stats for the issue.
-
- Raises:
- GitlabConnectionError: If the server cannot be reached.
- """
- url = ('/projects/%(project_id)s/issues/%(issue_id)s/time_stats' %
- {'project_id': self.project_id, 'issue_id': self.id})
- r = self.gitlab._raw_get(url, **kwargs)
- raise_error_from_response(r, GitlabGetError)
- return r.json()
-
- def time_estimate(self, **kwargs):
- """Set an estimated time of work for the issue.
-
- Raises:
- GitlabConnectionError: If the server cannot be reached.
- """
- url = ('/projects/%(project_id)s/issues/%(issue_id)s/time_estimate' %
- {'project_id': self.project_id, 'issue_id': self.id})
- r = self.gitlab._raw_post(url, **kwargs)
- raise_error_from_response(r, GitlabTimeTrackingError, 201)
- return r.json()
-
- def reset_time_estimate(self, **kwargs):
- """Resets estimated time for the issue to 0 seconds.
-
- Raises:
- GitlabConnectionError: If the server cannot be reached.
- """
- url = ('/projects/%(project_id)s/issues/%(issue_id)s/'
- 'reset_time_estimate' %
- {'project_id': self.project_id, 'issue_id': self.id})
- r = self.gitlab._raw_post(url, **kwargs)
- raise_error_from_response(r, GitlabTimeTrackingError, 200)
- return r.json()
-
- def add_spent_time(self, **kwargs):
- """Set an estimated time of work for the issue.
-
- Raises:
- GitlabConnectionError: If the server cannot be reached.
- """
- url = ('/projects/%(project_id)s/issues/%(issue_id)s/'
- 'add_spent_time' %
- {'project_id': self.project_id, 'issue_id': self.id})
- r = self.gitlab._raw_post(url, **kwargs)
- raise_error_from_response(r, GitlabTimeTrackingError, 200)
- return r.json()
-
- def reset_spent_time(self, **kwargs):
- """Set an estimated time of work for the issue.
-
- Raises:
- GitlabConnectionError: If the server cannot be reached.
- """
- url = ('/projects/%(project_id)s/issues/%(issue_id)s/'
- 'reset_spent_time' %
- {'project_id': self.project_id, 'issue_id': self.id})
- r = self.gitlab._raw_post(url, **kwargs)
- raise_error_from_response(r, GitlabTimeTrackingError, 200)
- return r.json()
-
-
-class ProjectIssueManager(BaseManager):
- obj_cls = ProjectIssue
-
-
-class ProjectMember(GitlabObject):
- _url = '/projects/%(project_id)s/members'
-
- requiredUrlAttrs = ['project_id']
- requiredCreateAttrs = ['access_level', 'user_id']
- optionalCreateAttrs = ['expires_at']
- requiredUpdateAttrs = ['access_level']
- optionalCreateAttrs = ['expires_at']
- shortPrintAttr = 'username'
-
-
-class ProjectMemberManager(BaseManager):
- obj_cls = ProjectMember
-
-
-class ProjectNote(GitlabObject):
- _url = '/projects/%(project_id)s/notes'
- _constructorTypes = {'author': 'User'}
- canUpdate = False
- canDelete = False
- requiredUrlAttrs = ['project_id']
- requiredCreateAttrs = ['body']
-
-
-class ProjectNoteManager(BaseManager):
- obj_cls = ProjectNote
-
-
-class ProjectNotificationSettings(NotificationSettings):
- _url = '/projects/%(project_id)s/notification_settings'
- requiredUrlAttrs = ['project_id']
-
-
-class ProjectNotificationSettingsManager(BaseManager):
- obj_cls = ProjectNotificationSettings
-
-
-class ProjectTagRelease(GitlabObject):
- _url = '/projects/%(project_id)s/repository/tags/%(tag_name)/release'
- canDelete = False
- canList = False
- requiredUrlAttrs = ['project_id', 'tag_name']
- requiredCreateAttrs = ['description']
- shortPrintAttr = 'description'
-
-
-class ProjectTag(GitlabObject):
- _url = '/projects/%(project_id)s/repository/tags'
- _constructorTypes = {'release': 'ProjectTagRelease',
- 'commit': 'ProjectCommit'}
- idAttr = 'name'
- canGet = 'from_list'
- canUpdate = False
- requiredUrlAttrs = ['project_id']
- requiredCreateAttrs = ['tag_name', 'ref']
- optionalCreateAttrs = ['message']
- shortPrintAttr = 'name'
-
- def set_release_description(self, description):
- """Set the release notes on the tag.
-
- If the release doesn't exist yet, it will be created. If it already
- exists, its description will be updated.
-
- Args:
- description (str): Description of the release.
-
- Raises:
- GitlabConnectionError: If the server cannot be reached.
- GitlabCreateError: If the server fails to create the release.
- GitlabUpdateError: If the server fails to update the release.
- """
- url = '/projects/%s/repository/tags/%s/release' % (self.project_id,
- self.name)
- if self.release is None:
- r = self.gitlab._raw_post(url, data={'description': description})
- raise_error_from_response(r, GitlabCreateError, 201)
- else:
- r = self.gitlab._raw_put(url, data={'description': description})
- raise_error_from_response(r, GitlabUpdateError, 200)
- self.release = ProjectTagRelease(self, r.json())
-
-
-class ProjectTagManager(BaseManager):
- obj_cls = ProjectTag
-
-
-class ProjectMergeRequestDiff(GitlabObject):
- _url = ('/projects/%(project_id)s/merge_requests/'
- '%(merge_request_id)s/versions')
- canCreate = False
- canUpdate = False
- canDelete = False
- requiredUrlAttrs = ['project_id', 'merge_request_id']
-
-
-class ProjectMergeRequestDiffManager(BaseManager):
- obj_cls = ProjectMergeRequestDiff
-
-
-class ProjectMergeRequestNote(GitlabObject):
- _url = '/projects/%(project_id)s/merge_requests/%(merge_request_id)s/notes'
- _constructorTypes = {'author': 'User'}
- requiredUrlAttrs = ['project_id', 'merge_request_id']
- requiredCreateAttrs = ['body']
-
-
-class ProjectMergeRequestNoteManager(BaseManager):
- obj_cls = ProjectMergeRequestNote
-
-
-class ProjectMergeRequest(GitlabObject):
- _url = '/projects/%(project_id)s/merge_requests'
- _constructorTypes = {'author': 'User', 'assignee': 'User'}
- requiredUrlAttrs = ['project_id']
- requiredCreateAttrs = ['source_branch', 'target_branch', 'title']
- optionalCreateAttrs = ['assignee_id', 'description', 'target_project_id',
- 'labels', 'milestone_id', 'remove_source_branch']
- optionalUpdateAttrs = ['target_branch', 'assignee_id', 'title',
- 'description', 'state_event', 'labels',
- 'milestone_id']
- optionalListAttrs = ['iid', 'state', 'order_by', 'sort']
-
- managers = (
- ('notes', 'ProjectMergeRequestNoteManager',
- [('project_id', 'project_id'), ('merge_request_id', 'id')]),
- ('diffs', 'ProjectMergeRequestDiffManager',
- [('project_id', 'project_id'), ('merge_request_id', 'id')]),
- )
-
- def _data_for_gitlab(self, extra_parameters={}, update=False,
- as_json=True):
- data = (super(ProjectMergeRequest, self)
- ._data_for_gitlab(extra_parameters, update=update,
- as_json=False))
- if update:
- # Drop source_branch attribute as it is not accepted by the gitlab
- # server (Issue #76)
- data.pop('source_branch', None)
- return json.dumps(data)
-
- def subscribe(self, **kwargs):
- """Subscribe to a MR.
-
- Raises:
- GitlabConnectionError: If the server cannot be reached.
- GitlabSubscribeError: If the subscription cannot be done
- """
- url = ('/projects/%(project_id)s/merge_requests/%(mr_id)s/'
- 'subscription' %
- {'project_id': self.project_id, 'mr_id': self.id})
-
- r = self.gitlab._raw_post(url, **kwargs)
- raise_error_from_response(r, GitlabSubscribeError, [201, 304])
- if r.status_code == 201:
- self._set_from_dict(r.json())
-
- def unsubscribe(self, **kwargs):
- """Unsubscribe a MR.
-
- Raises:
- GitlabConnectionError: If the server cannot be reached.
- GitlabUnsubscribeError: If the unsubscription cannot be done
- """
- url = ('/projects/%(project_id)s/merge_requests/%(mr_id)s/'
- 'subscription' %
- {'project_id': self.project_id, 'mr_id': self.id})
-
- r = self.gitlab._raw_delete(url, **kwargs)
- raise_error_from_response(r, GitlabUnsubscribeError, [200, 304])
- if r.status_code == 200:
- self._set_from_dict(r.json())
-
- def cancel_merge_when_build_succeeds(self, **kwargs):
- """Cancel merge when build succeeds."""
-
- u = ('/projects/%s/merge_requests/%s/cancel_merge_when_build_succeeds'
- % (self.project_id, self.id))
- r = self.gitlab._raw_put(u, **kwargs)
- errors = {401: GitlabMRForbiddenError,
- 405: GitlabMRClosedError,
- 406: GitlabMROnBuildSuccessError}
- raise_error_from_response(r, errors)
- return ProjectMergeRequest(self, r.json())
-
- def closes_issues(self, **kwargs):
- """List issues closed by the MR.
-
- Returns:
- list (ProjectIssue): List of closed issues
-
- Raises:
- GitlabConnectionError: If the server cannot be reached.
- GitlabGetError: If the server fails to perform the request.
- """
- url = ('/projects/%s/merge_requests/%s/closes_issues' %
- (self.project_id, self.id))
- return self.gitlab._raw_list(url, ProjectIssue, **kwargs)
-
- def commits(self, **kwargs):
- """List the merge request commits.
-
- Returns:
- list (ProjectCommit): List of commits
-
- Raises:
- GitlabConnectionError: If the server cannot be reached.
- GitlabListError: If the server fails to perform the request.
- """
- url = ('/projects/%s/merge_requests/%s/commits' %
- (self.project_id, self.id))
- return self.gitlab._raw_list(url, ProjectCommit, **kwargs)
-
- def changes(self, **kwargs):
- """List the merge request changes.
-
- Returns:
- list (dict): List of changes
-
- Raises:
- GitlabConnectionError: If the server cannot be reached.
- GitlabListError: If the server fails to perform the request.
- """
- url = ('/projects/%s/merge_requests/%s/changes' %
- (self.project_id, self.id))
- r = self.gitlab._raw_get(url, **kwargs)
- raise_error_from_response(r, GitlabListError)
- return r.json()
-
- def merge(self, merge_commit_message=None,
- should_remove_source_branch=False,
- merge_when_build_succeeds=False,
- **kwargs):
- """Accept the merge request.
-
- Args:
- merge_commit_message (bool): Commit message
- should_remove_source_branch (bool): If True, removes the source
- branch
- merge_when_build_succeeds (bool): Wait for the build to succeed,
- then merge
-
- Returns:
- ProjectMergeRequest: The updated MR
- Raises:
- GitlabConnectionError: If the server cannot be reached.
- GitlabMRForbiddenError: If the user doesn't have permission to
- close thr MR
- GitlabMRClosedError: If the MR is already closed
- """
- url = '/projects/%s/merge_requests/%s/merge' % (self.project_id,
- self.id)
- data = {}
- if merge_commit_message:
- data['merge_commit_message'] = merge_commit_message
- if should_remove_source_branch:
- data['should_remove_source_branch'] = True
- if merge_when_build_succeeds:
- data['merge_when_build_succeeds'] = True
-
- r = self.gitlab._raw_put(url, data=data, **kwargs)
- errors = {401: GitlabMRForbiddenError,
- 405: GitlabMRClosedError}
- raise_error_from_response(r, errors)
- self._set_from_dict(r.json())
-
- def todo(self, **kwargs):
- """Create a todo for the merge request.
-
- Raises:
- GitlabConnectionError: If the server cannot be reached.
- """
- url = ('/projects/%(project_id)s/merge_requests/%(mr_id)s/todo' %
- {'project_id': self.project_id, 'mr_id': self.id})
- r = self.gitlab._raw_post(url, **kwargs)
- raise_error_from_response(r, GitlabTodoError, [201, 304])
-
- def time_stats(self, **kwargs):
- """Get time stats for the merge request.
-
- Raises:
- GitlabConnectionError: If the server cannot be reached.
- """
- url = ('/projects/%(project_id)s/merge_requests/%(mr_id)s/time_stats' %
- {'project_id': self.project_id, 'mr_id': self.id})
- r = self.gitlab._raw_get(url, **kwargs)
- raise_error_from_response(r, GitlabGetError)
- return r.json()
-
- def time_estimate(self, **kwargs):
- """Set an estimated time of work for the merge request.
-
- Raises:
- GitlabConnectionError: If the server cannot be reached.
- """
- url = ('/projects/%(project_id)s/merge_requests/%(mr_id)s/'
- 'time_estimate' %
- {'project_id': self.project_id, 'mr_id': self.id})
- r = self.gitlab._raw_post(url, **kwargs)
- raise_error_from_response(r, GitlabTimeTrackingError, 201)
- return r.json()
-
- def reset_time_estimate(self, **kwargs):
- """Resets estimated time for the merge request to 0 seconds.
-
- Raises:
- GitlabConnectionError: If the server cannot be reached.
- """
- url = ('/projects/%(project_id)s/merge_requests/%(mr_id)s/'
- 'reset_time_estimate' %
- {'project_id': self.project_id, 'mr_id': self.id})
- r = self.gitlab._raw_post(url, **kwargs)
- raise_error_from_response(r, GitlabTimeTrackingError, 200)
- return r.json()
-
- def add_spent_time(self, **kwargs):
- """Set an estimated time of work for the merge request.
-
- Raises:
- GitlabConnectionError: If the server cannot be reached.
- """
- url = ('/projects/%(project_id)s/merge_requests/%(mr_id)s/'
- 'add_spent_time' %
- {'project_id': self.project_id, 'mr_id': self.id})
- r = self.gitlab._raw_post(url, **kwargs)
- raise_error_from_response(r, GitlabTimeTrackingError, 200)
- return r.json()
-
- def reset_spent_time(self, **kwargs):
- """Set an estimated time of work for the merge request.
-
- Raises:
- GitlabConnectionError: If the server cannot be reached.
- """
- url = ('/projects/%(project_id)s/merge_requests/%(mr_id)s/'
- 'reset_spent_time' %
- {'project_id': self.project_id, 'mr_id': self.id})
- r = self.gitlab._raw_post(url, **kwargs)
- raise_error_from_response(r, GitlabTimeTrackingError, 200)
- return r.json()
-
-
-class ProjectMergeRequestManager(BaseManager):
- obj_cls = ProjectMergeRequest
-
-
-class ProjectMilestone(GitlabObject):
- _url = '/projects/%(project_id)s/milestones'
- canDelete = False
- requiredUrlAttrs = ['project_id']
- optionalListAttrs = ['iid', 'state']
- requiredCreateAttrs = ['title']
- optionalCreateAttrs = ['description', 'due_date', 'start_date',
- 'state_event']
- optionalUpdateAttrs = requiredCreateAttrs + optionalCreateAttrs
- shortPrintAttr = 'title'
-
- def issues(self, **kwargs):
- url = "/projects/%s/milestones/%s/issues" % (self.project_id, self.id)
- return self.gitlab._raw_list(url, ProjectIssue, **kwargs)
-
- def merge_requests(self, **kwargs):
- """List the merge requests related to this milestone
-
- Returns:
- list (ProjectMergeRequest): List of merge requests
-
- Raises:
- GitlabConnectionError: If the server cannot be reached.
- GitlabListError: If the server fails to perform the request.
- """
- url = ('/projects/%s/milestones/%s/merge_requests' %
- (self.project_id, self.id))
- return self.gitlab._raw_list(url, ProjectMergeRequest, **kwargs)
-
-
-class ProjectMilestoneManager(BaseManager):
- obj_cls = ProjectMilestone
-
-
-class ProjectLabel(GitlabObject):
- _url = '/projects/%(project_id)s/labels'
- _id_in_delete_url = False
- _id_in_update_url = False
- canGet = 'from_list'
- requiredUrlAttrs = ['project_id']
- idAttr = 'name'
- requiredDeleteAttrs = ['name']
- requiredCreateAttrs = ['name', 'color']
- optionalCreateAttrs = ['description', 'priority']
- requiredUpdateAttrs = ['name']
- optionalUpdateAttrs = ['new_name', 'color', 'description', 'priority']
-
- def subscribe(self, **kwargs):
- """Subscribe to a label.
-
- Raises:
- GitlabConnectionError: If the server cannot be reached.
- GitlabSubscribeError: If the subscription cannot be done
- """
- url = ('/projects/%(project_id)s/labels/%(label_id)s/subscription' %
- {'project_id': self.project_id, 'label_id': self.name})
-
- r = self.gitlab._raw_post(url, **kwargs)
- raise_error_from_response(r, GitlabSubscribeError, [201, 304])
- self._set_from_dict(r.json())
-
- def unsubscribe(self, **kwargs):
- """Unsubscribe a label.
-
- Raises:
- GitlabConnectionError: If the server cannot be reached.
- GitlabUnsubscribeError: If the unsubscription cannot be done
- """
- url = ('/projects/%(project_id)s/labels/%(label_id)s/subscription' %
- {'project_id': self.project_id, 'label_id': self.name})
-
- r = self.gitlab._raw_delete(url, **kwargs)
- raise_error_from_response(r, GitlabUnsubscribeError, [200, 304])
- self._set_from_dict(r.json())
-
-
-class ProjectLabelManager(BaseManager):
- obj_cls = ProjectLabel
-
-
-class ProjectFile(GitlabObject):
- _url = '/projects/%(project_id)s/repository/files'
- canList = False
- requiredUrlAttrs = ['project_id']
- requiredGetAttrs = ['file_path', 'ref']
- requiredCreateAttrs = ['file_path', 'branch_name', 'content',
- 'commit_message']
- optionalCreateAttrs = ['encoding']
- requiredDeleteAttrs = ['branch_name', 'commit_message', 'file_path']
- shortPrintAttr = 'file_path'
- getRequiresId = False
-
- def decode(self):
- """Returns the decoded content of the file.
-
- Returns:
- (str): the decoded content.
- """
- return base64.b64decode(self.content)
-
-
-class ProjectFileManager(BaseManager):
- obj_cls = ProjectFile
-
-
-class ProjectPipeline(GitlabObject):
- _url = '/projects/%(project_id)s/pipelines'
- _create_url = '/projects/%(project_id)s/pipeline'
-
- canUpdate = False
- canDelete = False
-
- requiredUrlAttrs = ['project_id']
- requiredCreateAttrs = ['ref']
-
- def retry(self, **kwargs):
- """Retries failed builds in a pipeline.
-
- Raises:
- GitlabConnectionError: If the server cannot be reached.
- GitlabPipelineRetryError: If the retry cannot be done.
- """
- url = ('/projects/%(project_id)s/pipelines/%(id)s/retry' %
- {'project_id': self.project_id, 'id': self.id})
- r = self.gitlab._raw_post(url, data=None, content_type=None, **kwargs)
- raise_error_from_response(r, GitlabPipelineRetryError, 201)
- self._set_from_dict(r.json())
-
- def cancel(self, **kwargs):
- """Cancel builds in a pipeline.
-
- Raises:
- GitlabConnectionError: If the server cannot be reached.
- GitlabPipelineCancelError: If the retry cannot be done.
- """
- url = ('/projects/%(project_id)s/pipelines/%(id)s/cancel' %
- {'project_id': self.project_id, 'id': self.id})
- r = self.gitlab._raw_post(url, data=None, content_type=None, **kwargs)
- raise_error_from_response(r, GitlabPipelineRetryError, 200)
- self._set_from_dict(r.json())
-
-
-class ProjectPipelineManager(BaseManager):
- obj_cls = ProjectPipeline
-
-
-class ProjectSnippetNote(GitlabObject):
- _url = '/projects/%(project_id)s/snippets/%(snippet_id)s/notes'
- _constructorTypes = {'author': 'User'}
- canUpdate = False
- canDelete = False
- requiredUrlAttrs = ['project_id', 'snippet_id']
- requiredCreateAttrs = ['body']
-
-
-class ProjectSnippetNoteManager(BaseManager):
- obj_cls = ProjectSnippetNote
-
-
-class ProjectSnippet(GitlabObject):
- _url = '/projects/%(project_id)s/snippets'
- _constructorTypes = {'author': 'User'}
- requiredUrlAttrs = ['project_id']
- requiredCreateAttrs = ['title', 'file_name', 'code']
- optionalCreateAttrs = ['lifetime', 'visibility_level']
- optionalUpdateAttrs = ['title', 'file_name', 'code', 'visibility_level']
- shortPrintAttr = 'title'
- managers = (
- ('notes', 'ProjectSnippetNoteManager',
- [('project_id', 'project_id'), ('snippet_id', 'id')]),
- )
-
- def content(self, streamed=False, action=None, chunk_size=1024, **kwargs):
- """Return the raw content of a snippet.
-
- Args:
- streamed (bool): If True the data will be processed by chunks of
- `chunk_size` and each chunk is passed to `action` for
- treatment.
- action (callable): Callable responsible of dealing with chunk of
- data.
- chunk_size (int): Size of each chunk.
-
- Returns:
- str: The snippet content
-
- Raises:
- GitlabConnectionError: If the server cannot be reached.
- GitlabGetError: If the server fails to perform the request.
- """
- url = ("/projects/%(project_id)s/snippets/%(snippet_id)s/raw" %
- {'project_id': self.project_id, 'snippet_id': self.id})
- r = self.gitlab._raw_get(url, **kwargs)
- raise_error_from_response(r, GitlabGetError)
- return utils.response_content(r, streamed, action, chunk_size)
-
-
-class ProjectSnippetManager(BaseManager):
- obj_cls = ProjectSnippet
-
-
-class ProjectTrigger(GitlabObject):
- _url = '/projects/%(project_id)s/triggers'
- canUpdate = False
- idAttr = 'token'
- requiredUrlAttrs = ['project_id']
-
-
-class ProjectTriggerManager(BaseManager):
- obj_cls = ProjectTrigger
-
-
-class ProjectVariable(GitlabObject):
- _url = '/projects/%(project_id)s/variables'
- idAttr = 'key'
- requiredUrlAttrs = ['project_id']
- requiredCreateAttrs = ['key', 'value']
-
-
-class ProjectVariableManager(BaseManager):
- obj_cls = ProjectVariable
-
-
-class ProjectService(GitlabObject):
- _url = '/projects/%(project_id)s/services/%(service_name)s'
- canList = False
- canCreate = False
- _id_in_update_url = False
- _id_in_delete_url = False
- getRequiresId = False
- requiredUrlAttrs = ['project_id', 'service_name']
-
- _service_attrs = {
- 'asana': (('api_key', ), ('restrict_to_branch', )),
- 'assembla': (('token', ), ('subdomain', )),
- 'bamboo': (('bamboo_url', 'build_key', 'username', 'password'),
- tuple()),
- 'buildkite': (('token', 'project_url'), ('enable_ssl_verification', )),
- 'campfire': (('token', ), ('subdomain', 'room')),
- 'custom-issue-tracker': (('new_issue_url', 'issues_url',
- 'project_url'),
- ('description', 'title')),
- 'drone-ci': (('token', 'drone_url'), ('enable_ssl_verification', )),
- 'emails-on-push': (('recipients', ), ('disable_diffs',
- 'send_from_committer_email')),
- 'builds-email': (('recipients', ), ('add_pusher',
- 'notify_only_broken_builds')),
- 'pipelines-email': (('recipients', ), ('add_pusher',
- 'notify_only_broken_builds')),
- 'external-wiki': (('external_wiki_url', ), tuple()),
- 'flowdock': (('token', ), tuple()),
- 'gemnasium': (('api_key', 'token', ), tuple()),
- 'hipchat': (('token', ), ('color', 'notify', 'room', 'api_version',
- 'server')),
- 'irker': (('recipients', ), ('default_irc_uri', 'server_port',
- 'server_host', 'colorize_messages')),
- 'jira': (tuple(), (
- # Required fields in GitLab >= 8.14
- 'url', 'project_key',
-
- # Required fields in GitLab < 8.14
- 'new_issue_url', 'project_url', 'issues_url', 'api_url',
- 'description',
-
- # Optional fields
- 'username', 'password', 'jira_issue_transition_id')),
- 'mattermost': (('webhook',), ('username', 'channel')),
- 'pivotaltracker': (('token', ), tuple()),
- 'pushover': (('api_key', 'user_key', 'priority'), ('device', 'sound')),
- 'redmine': (('new_issue_url', 'project_url', 'issues_url'),
- ('description', )),
- 'slack': (('webhook', ), ('username', 'channel')),
- 'teamcity': (('teamcity_url', 'build_type', 'username', 'password'),
- tuple())
- }
-
- def _data_for_gitlab(self, extra_parameters={}, update=False,
- as_json=True):
- data = (super(ProjectService, self)
- ._data_for_gitlab(extra_parameters, update=update,
- as_json=False))
- missing = []
- # Mandatory args
- for attr in self._service_attrs[self.service_name][0]:
- if not hasattr(self, attr):
- missing.append(attr)
- else:
- data[attr] = getattr(self, attr)
-
- if missing:
- raise GitlabUpdateError('Missing attribute(s): %s' %
- ", ".join(missing))
-
- # Optional args
- for attr in self._service_attrs[self.service_name][1]:
- if hasattr(self, attr):
- data[attr] = getattr(self, attr)
-
- return json.dumps(data)
-
-
-class ProjectServiceManager(BaseManager):
- obj_cls = ProjectService
-
- def available(self, **kwargs):
- """List the services known by python-gitlab.
-
- Returns:
- list (str): The list of service code names.
- """
- return list(ProjectService._service_attrs.keys())
-
-
-class ProjectAccessRequest(GitlabObject):
- _url = '/projects/%(project_id)s/access_requests'
- canGet = 'from_list'
- canUpdate = False
-
- def approve(self, access_level=gitlab.DEVELOPER_ACCESS, **kwargs):
- """Approve an access request.
-
- Args:
- 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.
- """
-
- url = ('/projects/%(project_id)s/access_requests/%(id)s/approve' %
- {'project_id': self.project_id, 'id': self.id})
- data = {'access_level': access_level}
- r = self.gitlab._raw_put(url, data=data, **kwargs)
- raise_error_from_response(r, GitlabUpdateError, 201)
- self._set_from_dict(r.json())
-
-
-class ProjectAccessRequestManager(BaseManager):
- obj_cls = ProjectAccessRequest
-
-
-class ProjectDeployment(GitlabObject):
- _url = '/projects/%(project_id)s/deployments'
- canCreate = False
- canUpdate = False
- canDelete = False
-
-
-class ProjectDeploymentManager(BaseManager):
- obj_cls = ProjectDeployment
-
-
-class ProjectRunner(GitlabObject):
- _url = '/projects/%(project_id)s/runners'
- canUpdate = False
- requiredCreateAttrs = ['runner_id']
-
-
-class ProjectRunnerManager(BaseManager):
- obj_cls = ProjectRunner
-
-
-class Project(GitlabObject):
- _url = '/projects'
- _constructorTypes = {'owner': 'User', 'namespace': 'Group'}
- optionalListAttrs = ['search']
- requiredCreateAttrs = ['name']
- optionalListAttrs = ['search']
- optionalCreateAttrs = ['path', 'namespace_id', 'description',
- 'issues_enabled', 'merge_requests_enabled',
- 'builds_enabled', 'wiki_enabled',
- 'snippets_enabled', 'container_registry_enabled',
- 'shared_runners_enabled', 'public',
- 'visibility_level', 'import_url', 'public_builds',
- 'only_allow_merge_if_build_succeeds',
- 'only_allow_merge_if_all_discussions_are_resolved',
- 'lfs_enabled', 'request_access_enabled']
- optionalUpdateAttrs = ['name', 'path', 'default_branch', 'description',
- 'issues_enabled', 'merge_requests_enabled',
- 'builds_enabled', 'wiki_enabled',
- 'snippets_enabled', 'container_registry_enabled',
- 'shared_runners_enabled', 'public',
- 'visibility_level', 'import_url', 'public_builds',
- 'only_allow_merge_if_build_succeeds',
- 'only_allow_merge_if_all_discussions_are_resolved',
- 'lfs_enabled', 'request_access_enabled']
- shortPrintAttr = 'path'
- managers = (
- ('accessrequests', 'ProjectAccessRequestManager',
- [('project_id', 'id')]),
- ('boards', 'ProjectBoardManager', [('project_id', 'id')]),
- ('board_lists', 'ProjectBoardListManager', [('project_id', 'id')]),
- ('branches', 'ProjectBranchManager', [('project_id', 'id')]),
- ('builds', 'ProjectBuildManager', [('project_id', 'id')]),
- ('commits', 'ProjectCommitManager', [('project_id', 'id')]),
- ('deployments', 'ProjectDeploymentManager', [('project_id', 'id')]),
- ('environments', 'ProjectEnvironmentManager', [('project_id', 'id')]),
- ('events', 'ProjectEventManager', [('project_id', 'id')]),
- ('files', 'ProjectFileManager', [('project_id', 'id')]),
- ('forks', 'ProjectForkManager', [('project_id', 'id')]),
- ('hooks', 'ProjectHookManager', [('project_id', 'id')]),
- ('keys', 'ProjectKeyManager', [('project_id', 'id')]),
- ('issues', 'ProjectIssueManager', [('project_id', 'id')]),
- ('labels', 'ProjectLabelManager', [('project_id', 'id')]),
- ('members', 'ProjectMemberManager', [('project_id', 'id')]),
- ('mergerequests', 'ProjectMergeRequestManager',
- [('project_id', 'id')]),
- ('milestones', 'ProjectMilestoneManager', [('project_id', 'id')]),
- ('notes', 'ProjectNoteManager', [('project_id', 'id')]),
- ('notificationsettings', 'ProjectNotificationSettingsManager',
- [('project_id', 'id')]),
- ('pipelines', 'ProjectPipelineManager', [('project_id', 'id')]),
- ('runners', 'ProjectRunnerManager', [('project_id', 'id')]),
- ('services', 'ProjectServiceManager', [('project_id', 'id')]),
- ('snippets', 'ProjectSnippetManager', [('project_id', 'id')]),
- ('tags', 'ProjectTagManager', [('project_id', 'id')]),
- ('triggers', 'ProjectTriggerManager', [('project_id', 'id')]),
- ('variables', 'ProjectVariableManager', [('project_id', 'id')]),
- )
-
- VISIBILITY_PRIVATE = gitlab.VISIBILITY_PRIVATE
- VISIBILITY_INTERNAL = gitlab.VISIBILITY_INTERNAL
- VISIBILITY_PUBLIC = gitlab.VISIBILITY_PUBLIC
-
- def repository_tree(self, path='', ref_name='', **kwargs):
- """Return a list of files in the repository.
-
- Args:
- path (str): Path of the top folder (/ by default)
- ref_name (str): Reference to a commit or branch
-
- Returns:
- str: The json representation of the tree.
-
- Raises:
- GitlabConnectionError: If the server cannot be reached.
- GitlabGetError: If the server fails to perform the request.
- """
- url = "/projects/%s/repository/tree" % (self.id)
- params = []
- if path:
- params.append(urllib.parse.urlencode({'path': path}))
- if ref_name:
- params.append("ref_name=%s" % ref_name)
- if params:
- url += '?' + "&".join(params)
- r = self.gitlab._raw_get(url, **kwargs)
- raise_error_from_response(r, GitlabGetError)
- return r.json()
-
- def repository_blob(self, sha, filepath, streamed=False, action=None,
- chunk_size=1024, **kwargs):
- """Return the content of a file for a commit.
-
- Args:
- sha (str): ID of the commit
- filepath (str): Path of the file to return
- streamed (bool): If True the data will be processed by chunks of
- `chunk_size` and each chunk is passed to `action` for
- treatment.
- action (callable): Callable responsible of dealing with chunk of
- data.
- chunk_size (int): Size of each chunk.
-
- Returns:
- str: The file content
-
- Raises:
- GitlabConnectionError: If the server cannot be reached.
- GitlabGetError: If the server fails to perform the request.
- """
- url = "/projects/%s/repository/blobs/%s" % (self.id, sha)
- url += '?%s' % (urllib.parse.urlencode({'filepath': filepath}))
- r = self.gitlab._raw_get(url, streamed=streamed, **kwargs)
- raise_error_from_response(r, GitlabGetError)
- return utils.response_content(r, streamed, action, chunk_size)
-
- def repository_raw_blob(self, sha, streamed=False, action=None,
- chunk_size=1024, **kwargs):
- """Returns the raw file contents for a blob by blob SHA.
-
- Args:
- sha(str): ID of the blob
- streamed (bool): If True the data will be processed by chunks of
- `chunk_size` and each chunk is passed to `action` for
- treatment.
- action (callable): Callable responsible of dealing with chunk of
- data.
- chunk_size (int): Size of each chunk.
-
- Returns:
- str: The blob content
-
- Raises:
- GitlabConnectionError: If the server cannot be reached.
- GitlabGetError: If the server fails to perform the request.
- """
- url = "/projects/%s/repository/raw_blobs/%s" % (self.id, sha)
- r = self.gitlab._raw_get(url, streamed=streamed, **kwargs)
- raise_error_from_response(r, GitlabGetError)
- return utils.response_content(r, streamed, action, chunk_size)
-
- def repository_compare(self, from_, to, **kwargs):
- """Returns a diff between two branches/commits.
-
- Args:
- from_(str): orig branch/SHA
- to(str): dest branch/SHA
-
- Returns:
- str: The diff
-
- Raises:
- GitlabConnectionError: If the server cannot be reached.
- GitlabGetError: If the server fails to perform the request.
- """
- url = "/projects/%s/repository/compare" % self.id
- url = "%s?from=%s&to=%s" % (url, from_, to)
- r = self.gitlab._raw_get(url, **kwargs)
- raise_error_from_response(r, GitlabGetError)
- return r.json()
-
- def repository_contributors(self):
- """Returns a list of contributors for the project.
-
- Returns:
- list: The contibutors
-
- Raises:
- GitlabConnectionError: If the server cannot be reached.
- GitlabGetError: If the server fails to perform the request.
- """
- url = "/projects/%s/repository/contributors" % self.id
- r = self.gitlab._raw_get(url)
- raise_error_from_response(r, GitlabListError)
- return r.json()
-
- def repository_archive(self, sha=None, streamed=False, action=None,
- chunk_size=1024, **kwargs):
- """Return a tarball of the repository.
-
- Args:
- sha (str): ID of the commit (default branch by default).
- streamed (bool): If True the data will be processed by chunks of
- `chunk_size` and each chunk is passed to `action` for
- treatment.
- action (callable): Callable responsible of dealing with chunk of
- data.
- chunk_size (int): Size of each chunk.
-
- Returns:
- str: The binary data of the archive.
-
- Raises:
- GitlabConnectionError: If the server cannot be reached.
- GitlabGetError: If the server fails to perform the request.
- """
- url = '/projects/%s/repository/archive' % self.id
- if sha:
- url += '?sha=%s' % sha
- r = self.gitlab._raw_get(url, streamed=streamed, **kwargs)
- raise_error_from_response(r, GitlabGetError)
- return utils.response_content(r, streamed, action, chunk_size)
-
- def create_fork_relation(self, forked_from_id):
- """Create a forked from/to relation between existing projects.
-
- Args:
- forked_from_id (int): The ID of the project that was forked from
-
- Raises:
- GitlabConnectionError: If the server cannot be reached.
- GitlabCreateError: If the server fails to perform the request.
- """
- url = "/projects/%s/fork/%s" % (self.id, forked_from_id)
- r = self.gitlab._raw_post(url)
- raise_error_from_response(r, GitlabCreateError, 201)
-
- def delete_fork_relation(self):
- """Delete a forked relation between existing projects.
-
- Raises:
- GitlabConnectionError: If the server cannot be reached.
- GitlabDeleteError: If the server fails to perform the request.
- """
- url = "/projects/%s/fork" % self.id
- r = self.gitlab._raw_delete(url)
- raise_error_from_response(r, GitlabDeleteError)
-
- def star(self, **kwargs):
- """Star a project.
-
- Returns:
- Project: the updated Project
-
- Raises:
- GitlabCreateError: If the action cannot be done
- GitlabConnectionError: If the server cannot be reached.
- """
- url = "/projects/%s/star" % self.id
- r = self.gitlab._raw_post(url, **kwargs)
- raise_error_from_response(r, GitlabCreateError, [201, 304])
- return Project(self.gitlab, r.json()) if r.status_code == 201 else self
-
- def unstar(self, **kwargs):
- """Unstar a project.
-
- Returns:
- Project: the updated Project
-
- Raises:
- GitlabDeleteError: If the action cannot be done
- GitlabConnectionError: If the server cannot be reached.
- """
- url = "/projects/%s/star" % self.id
- r = self.gitlab._raw_delete(url, **kwargs)
- raise_error_from_response(r, GitlabDeleteError, [200, 304])
- return Project(self.gitlab, r.json()) if r.status_code == 200 else self
-
- def archive(self, **kwargs):
- """Archive a project.
-
- Returns:
- Project: the updated Project
-
- Raises:
- GitlabCreateError: If the action cannot be done
- GitlabConnectionError: If the server cannot be reached.
- """
- url = "/projects/%s/archive" % self.id
- r = self.gitlab._raw_post(url, **kwargs)
- raise_error_from_response(r, GitlabCreateError, 201)
- return Project(self.gitlab, r.json()) if r.status_code == 201 else self
-
- def unarchive(self, **kwargs):
- """Unarchive a project.
-
- Returns:
- Project: the updated Project
-
- Raises:
- GitlabDeleteError: If the action cannot be done
- GitlabConnectionError: If the server cannot be reached.
- """
- url = "/projects/%s/unarchive" % self.id
- r = self.gitlab._raw_delete(url, **kwargs)
- raise_error_from_response(r, GitlabCreateError, 201)
- return Project(self.gitlab, r.json()) if r.status_code == 201 else self
-
- def share(self, group_id, group_access, **kwargs):
- """Share the project with a group.
-
- Args:
- group_id (int): ID of the group.
- group_access (int): Access level for the group.
-
- Raises:
- GitlabConnectionError: If the server cannot be reached.
- GitlabCreateError: If the server fails to perform the request.
- """
- url = "/projects/%s/share" % self.id
- data = {'group_id': group_id, 'group_access': group_access}
- r = self.gitlab._raw_post(url, data=data, **kwargs)
- raise_error_from_response(r, GitlabCreateError, 201)
-
- def unshare(self, group_id, **kwargs):
- """Delete a shared project link within a group.
-
- Args:
- group_id (int): ID of the group.
-
- Raises:
- GitlabConnectionError: If the server cannot be reached.
- GitlabDeleteError: If the server fails to perform the request.
- """
- url = "/projects/%s/share/%s" % (self.id, group_id)
- r = self.gitlab._raw_delete(url, **kwargs)
- raise_error_from_response(r, GitlabDeleteError, 204)
-
- def trigger_build(self, ref, token, variables={}, **kwargs):
- """Trigger a CI build.
-
- See https://gitlab.com/help/ci/triggers/README.md#trigger-a-build
-
- Args:
- ref (str): Commit to build; can be a commit SHA, a branch name, ...
- token (str): The trigger token
- variables (dict): Variables passed to the build script
-
- Raises:
- GitlabConnectionError: If the server cannot be reached.
- GitlabCreateError: If the server fails to perform the request.
- """
- url = "/projects/%s/trigger/builds" % self.id
- form = {r'variables[%s]' % k: v for k, v in six.iteritems(variables)}
- data = {'ref': ref, 'token': token}
- data.update(form)
- r = self.gitlab._raw_post(url, data=data, **kwargs)
- raise_error_from_response(r, GitlabCreateError, 201)
-
- # see #56 - add file attachment features
- def upload(self, filename, filedata=None, filepath=None, **kwargs):
- """Upload the specified file into the project.
-
- .. note::
-
- Either ``filedata`` or ``filepath`` *MUST* be specified.
-
- Args:
- filename (str): The name of the file being uploaded
- filedata (bytes): The raw data of the file being uploaded
- filepath (str): The path to a local file to upload (optional)
-
- Raises:
- GitlabConnectionError: If the server cannot be reached
- GitlabUploadError: If the file upload fails
- GitlabUploadError: If ``filedata`` and ``filepath`` are not
- specified
- GitlabUploadError: If both ``filedata`` and ``filepath`` are
- specified
-
- Returns:
- dict: A ``dict`` with the keys:
- * ``alt`` - The alternate text for the upload
- * ``url`` - The direct url to the uploaded file
- * ``markdown`` - Markdown for the uploaded file
- """
- if filepath is None and filedata is None:
- raise GitlabUploadError("No file contents or path specified")
-
- if filedata is not None and filepath is not None:
- raise GitlabUploadError("File contents and file path specified")
-
- if filepath is not None:
- with open(filepath, "rb") as f:
- filedata = f.read()
-
- url = ("/projects/%(id)s/uploads" % {
- "id": self.id,
- })
- r = self.gitlab._raw_post(
- url,
- files={"file": (filename, filedata)},
- )
- # returns 201 status code (created)
- raise_error_from_response(r, GitlabUploadError, expected_code=201)
- data = r.json()
-
- return {
- "alt": data['alt'],
- "url": data['url'],
- "markdown": data['markdown']
- }
-
-
-class Runner(GitlabObject):
- _url = '/runners'
- canCreate = False
- optionalUpdateAttrs = ['description', 'active', 'tag_list']
- optionalListAttrs = ['scope']
-
-
-class RunnerManager(BaseManager):
- obj_cls = Runner
-
- def all(self, scope=None, **kwargs):
- """List all the runners.
-
- Args:
- scope (str): The scope of runners to show, one of: specific,
- shared, active, paused, online
-
- Returns:
- list(Runner): a list of runners matching the scope.
-
- Raises:
- GitlabConnectionError: If the server cannot be reached.
- GitlabListError: If the resource cannot be found
- """
- url = '/runners/all'
- if scope is not None:
- url += '?scope=' + scope
- return self.gitlab._raw_list(url, self.obj_cls, **kwargs)
-
-
-class TeamMember(GitlabObject):
- _url = '/user_teams/%(team_id)s/members'
- canUpdate = False
- requiredUrlAttrs = ['teamd_id']
- requiredCreateAttrs = ['access_level']
- shortPrintAttr = 'username'
-
-
-class Todo(GitlabObject):
- _url = '/todos'
- canGet = 'from_list'
- canUpdate = False
- canCreate = False
- optionalListAttrs = ['action', 'author_id', 'project_id', 'state', 'type']
-
-
-class TodoManager(BaseManager):
- obj_cls = Todo
-
- def delete_all(self, **kwargs):
- """Mark all the todos as done.
-
- Raises:
- GitlabConnectionError: If the server cannot be reached.
- GitlabDeleteError: If the resource cannot be found
-
- Returns:
- The number of todos maked done.
- """
- url = '/todos'
- r = self.gitlab._raw_delete(url, **kwargs)
- raise_error_from_response(r, GitlabDeleteError)
- return int(r.text)
-
-
-class ProjectManager(BaseManager):
- obj_cls = Project
-
- def search(self, query, **kwargs):
- """Search projects by name.
-
- API v3 only.
-
- .. note::
-
- The search is only performed on the project name (not on the
- namespace or the description). To perform a smarter search, use the
- ``search`` argument of the ``list()`` method:
-
- .. code-block:: python
-
- gl.projects.list(search=your_search_string)
-
- Args:
- query (str): The query string to send to GitLab for the search.
- all (bool): If True, return all the items, without pagination
- **kwargs: Additional arguments to send to GitLab.
-
- Returns:
- list(gitlab.Gitlab.Project): A list of matching projects.
- """
- if self.gitlab.api_version == '4':
- raise NotImplementedError("Not supported by v4 API")
-
- return self.gitlab._raw_list("/projects/search/" + query, Project,
- **kwargs)
-
- def all(self, **kwargs):
- """List all the projects (need admin rights).
-
- Args:
- all (bool): If True, return all the items, without pagination
- **kwargs: Additional arguments to send to GitLab.
-
- Returns:
- list(gitlab.Gitlab.Project): The list of projects.
- """
- return self.gitlab._raw_list("/projects/all", Project, **kwargs)
-
- def owned(self, **kwargs):
- """List owned projects.
-
- Args:
- all (bool): If True, return all the items, without pagination
- **kwargs: Additional arguments to send to GitLab.
-
- Returns:
- list(gitlab.Gitlab.Project): The list of owned projects.
- """
- return self.gitlab._raw_list("/projects/owned", Project, **kwargs)
-
- def starred(self, **kwargs):
- """List starred projects.
-
- Args:
- all (bool): If True, return all the items, without pagination
- **kwargs: Additional arguments to send to GitLab.
-
- Returns:
- list(gitlab.Gitlab.Project): The list of starred projects.
- """
- return self.gitlab._raw_list("/projects/starred", Project, **kwargs)
-
-
-class GroupProject(Project):
- _url = '/groups/%(group_id)s/projects'
- canGet = 'from_list'
- canCreate = False
- canDelete = False
- canUpdate = False
- optionalListAttrs = ['archived', 'visibility', 'order_by', 'sort',
- 'search', 'ci_enabled_first']
-
- def __init__(self, *args, **kwargs):
- Project.__init__(self, *args, **kwargs)
-
-
-class GroupProjectManager(ProjectManager):
- obj_cls = GroupProject
-
-
-class Group(GitlabObject):
- _url = '/groups'
- requiredCreateAttrs = ['name', 'path']
- optionalCreateAttrs = ['description', 'visibility_level', 'parent_id',
- 'lfs_enabled', 'request_access_enabled']
- optionalUpdateAttrs = ['name', 'path', 'description', 'visibility_level',
- 'lfs_enabled', 'request_access_enabled']
- shortPrintAttr = 'name'
- managers = (
- ('accessrequests', 'GroupAccessRequestManager', [('group_id', 'id')]),
- ('members', 'GroupMemberManager', [('group_id', 'id')]),
- ('notificationsettings', 'GroupNotificationSettingsManager',
- [('group_id', 'id')]),
- ('projects', 'GroupProjectManager', [('group_id', 'id')]),
- ('issues', 'GroupIssueManager', [('group_id', 'id')]),
- )
-
- GUEST_ACCESS = gitlab.GUEST_ACCESS
- REPORTER_ACCESS = gitlab.REPORTER_ACCESS
- DEVELOPER_ACCESS = gitlab.DEVELOPER_ACCESS
- MASTER_ACCESS = gitlab.MASTER_ACCESS
- OWNER_ACCESS = gitlab.OWNER_ACCESS
-
- VISIBILITY_PRIVATE = gitlab.VISIBILITY_PRIVATE
- VISIBILITY_INTERNAL = gitlab.VISIBILITY_INTERNAL
- VISIBILITY_PUBLIC = gitlab.VISIBILITY_PUBLIC
-
- def transfer_project(self, id, **kwargs):
- """Transfers a project to this new groups.
-
- Args:
- id (int): ID of the project to transfer.
-
- Raises:
- GitlabConnectionError: If the server cannot be reached.
- GitlabTransferProjectError: If the server fails to perform the
- request.
- """
- url = '/groups/%d/projects/%d' % (self.id, id)
- r = self.gitlab._raw_post(url, None, **kwargs)
- raise_error_from_response(r, GitlabTransferProjectError, 201)
-
-
-class GroupManager(BaseManager):
- obj_cls = Group
-
- def search(self, query, **kwargs):
- """Searches groups by name.
-
- Args:
- query (str): The search string
- all (bool): If True, return all the items, without pagination
-
- Returns:
- list(Group): a list of matching groups.
- """
- url = '/groups?search=' + query
- return self.gitlab._raw_list(url, self.obj_cls, **kwargs)
-
-
-class TeamMemberManager(BaseManager):
- obj_cls = TeamMember
-
-
-class TeamProject(GitlabObject):
- _url = '/user_teams/%(team_id)s/projects'
- _constructorTypes = {'owner': 'User', 'namespace': 'Group'}
- canUpdate = False
- requiredCreateAttrs = ['greatest_access_level']
- requiredUrlAttrs = ['team_id']
- shortPrintAttr = 'name'
-
-
-class TeamProjectManager(BaseManager):
- obj_cls = TeamProject
-
-
-class Team(GitlabObject):
- _url = '/user_teams'
- shortPrintAttr = 'name'
- requiredCreateAttrs = ['name', 'path']
- canUpdate = False
- managers = (
- ('members', 'TeamMemberManager', [('team_id', 'id')]),
- ('projects', 'TeamProjectManager', [('team_id', 'id')]),
- )
-
-
-class TeamManager(BaseManager):
- obj_cls = Team
diff --git a/tools/build_test_env.sh b/tools/build_test_env.sh
index 9961333..0b2fc34 100755
--- a/tools/build_test_env.sh
+++ b/tools/build_test_env.sh
@@ -46,8 +46,8 @@ case $PY_VER in
esac
case $API_VER in
- 3|4) ;;
- *) fatal "Wrong API version (3 or 4)";;
+ 4) ;;
+ *) fatal "Wrong API version (4 only)";;
esac
for req in \
diff --git a/tools/cli_test_v3.sh b/tools/cli_test_v3.sh
deleted file mode 100644
index ed433ce..0000000
--- a/tools/cli_test_v3.sh
+++ /dev/null
@@ -1,107 +0,0 @@
-#!/bin/sh
-# Copyright (C) 2015 Gauvain Pocentek <gauvain@pocentek.net>
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Lesser General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU Lesser General Public License for more details.
-#
-# 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/>.
-
-testcase "project creation" '
- OUTPUT=$(try GITLAB project create --name test-project1) || exit 1
- PROJECT_ID=$(pecho "${OUTPUT}" | grep ^id: | cut -d" " -f2)
- OUTPUT=$(try GITLAB project list) || exit 1
- pecho "${OUTPUT}" | grep -q test-project1
-'
-
-testcase "project update" '
- GITLAB project update --id "$PROJECT_ID" --description "My New Description"
-'
-
-testcase "user creation" '
- OUTPUT=$(GITLAB user create --email fake@email.com --username user1 \
- --name "User One" --password fakepassword)
-'
-USER_ID=$(pecho "${OUTPUT}" | grep ^id: | cut -d' ' -f2)
-
-testcase "user get (by id)" '
- GITLAB user get --id $USER_ID >/dev/null 2>&1
-'
-
-testcase "user get (by username)" '
- GITLAB user get-by-username --query user1 >/dev/null 2>&1
-'
-
-testcase "verbose output" '
- OUTPUT=$(try GITLAB -v user list) || exit 1
- pecho "${OUTPUT}" | grep -q avatar-url
-'
-
-testcase "CLI args not in output" '
- OUTPUT=$(try GITLAB -v user list) || exit 1
- pecho "${OUTPUT}" | grep -qv config-file
-'
-
-testcase "adding member to a project" '
- GITLAB project-member create --project-id "$PROJECT_ID" \
- --user-id "$USER_ID" --access-level 40 >/dev/null 2>&1
-'
-
-testcase "file creation" '
- GITLAB project-file create --project-id "$PROJECT_ID" \
- --file-path README --branch-name master --content "CONTENT" \
- --commit-message "Initial commit" >/dev/null 2>&1
-'
-
-testcase "issue creation" '
- OUTPUT=$(GITLAB project-issue create --project-id "$PROJECT_ID" \
- --title "my issue" --description "my issue description")
-'
-ISSUE_ID=$(pecho "${OUTPUT}" | grep ^id: | cut -d' ' -f2)
-
-testcase "note creation" '
- GITLAB project-issue-note create --project-id "$PROJECT_ID" \
- --issue-id "$ISSUE_ID" --body "the body" >/dev/null 2>&1
-'
-
-testcase "branch creation" '
- GITLAB project-branch create --project-id "$PROJECT_ID" \
- --branch-name branch1 --ref master >/dev/null 2>&1
-'
-
-GITLAB project-file create --project-id "$PROJECT_ID" \
- --file-path README2 --branch-name branch1 --content "CONTENT" \
- --commit-message "second commit" >/dev/null 2>&1
-
-testcase "merge request creation" '
- OUTPUT=$(GITLAB project-merge-request create \
- --project-id "$PROJECT_ID" \
- --source-branch branch1 --target-branch master \
- --title "Update README")
-'
-MR_ID=$(pecho "${OUTPUT}" | grep ^id: | cut -d' ' -f2)
-
-testcase "merge request validation" '
- GITLAB project-merge-request merge --project-id "$PROJECT_ID" \
- --id "$MR_ID" >/dev/null 2>&1
-'
-
-testcase "branch deletion" '
- GITLAB project-branch delete --project-id "$PROJECT_ID" \
- --name branch1 >/dev/null 2>&1
-'
-
-testcase "project upload" '
- GITLAB project upload --id "$PROJECT_ID" --filename '$(basename $0)' --filepath '$0'
-'
-
-testcase "project deletion" '
- GITLAB project delete --id "$PROJECT_ID"
-'
diff --git a/tools/python_test_v3.py b/tools/python_test_v3.py
deleted file mode 100644
index c16bb40..0000000
--- a/tools/python_test_v3.py
+++ /dev/null
@@ -1,354 +0,0 @@
-import base64
-import re
-import time
-
-import gitlab
-
-LOGIN = 'root'
-PASSWORD = '5iveL!fe'
-
-SSH_KEY = ("ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDZAjAX8vTiHD7Yi3/EzuVaDChtih"
- "79HyJZ6H9dEqxFfmGA1YnncE0xujQ64TCebhkYJKzmTJCImSVkOu9C4hZgsw6eE76n"
- "+Cg3VwEeDUFy+GXlEJWlHaEyc3HWioxgOALbUp3rOezNh+d8BDwwqvENGoePEBsz5l"
- "a6WP5lTi/HJIjAl6Hu+zHgdj1XVExeH+S52EwpZf/ylTJub0Bl5gHwf/siVE48mLMI"
- "sqrukXTZ6Zg+8EHAIvIQwJ1dKcXe8P5IoLT7VKrbkgAnolS0I8J+uH7KtErZJb5oZh"
- "S4OEwsNpaXMAr+6/wWSpircV2/e7sFLlhlKBC4Iq1MpqlZ7G3p foo@bar")
-DEPLOY_KEY = ("ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDFdRyjJQh+1niBpXqE2I8dzjG"
- "MXFHlRjX9yk/UfOn075IdaockdU58sw2Ai1XIWFpZpfJkW7z+P47ZNSqm1gzeXI"
- "rtKa9ZUp8A7SZe8vH4XVn7kh7bwWCUirqtn8El9XdqfkzOs/+FuViriUWoJVpA6"
- "WZsDNaqINFKIA5fj/q8XQw+BcS92L09QJg9oVUuH0VVwNYbU2M2IRmSpybgC/gu"
- "uWTrnCDMmLItksATifLvRZwgdI8dr+q6tbxbZknNcgEPrI2jT0hYN9ZcjNeWuyv"
- "rke9IepE7SPBT41C+YtUX4dfDZDmczM1cE0YL/krdUCfuZHMa4ZS2YyNd6slufc"
- "vn bar@foo")
-
-# token authentication from config file
-gl = gitlab.Gitlab.from_config(config_files=['/tmp/python-gitlab.cfg'])
-gl.auth()
-assert(isinstance(gl.user, gitlab.v3.objects.CurrentUser))
-
-# settings
-settings = gl.settings.get()
-settings.default_projects_limit = 42
-settings.save()
-settings = gl.settings.get()
-assert(settings.default_projects_limit == 42)
-
-# user manipulations
-new_user = gl.users.create({'email': 'foo@bar.com', 'username': 'foo',
- 'name': 'foo', 'password': 'foo_password'})
-users_list = gl.users.list()
-for user in users_list:
- if user.username == 'foo':
- break
-assert(new_user.username == user.username)
-assert(new_user.email == user.email)
-
-new_user.block()
-new_user.unblock()
-
-foobar_user = gl.users.create(
- {'email': 'foobar@example.com', 'username': 'foobar',
- 'name': 'Foo Bar', 'password': 'foobar_password'})
-
-assert(gl.users.search('foobar')[0].id == foobar_user.id)
-usercmp = lambda x,y: cmp(x.id, y.id)
-expected = sorted([new_user, foobar_user], cmp=usercmp)
-actual = sorted(gl.users.search('foo'), cmp=usercmp)
-assert len(expected) == len(actual)
-assert len(gl.users.search('asdf')) == 0
-
-assert gl.users.get_by_username('foobar').id == foobar_user.id
-assert gl.users.get_by_username('foo').id == new_user.id
-try:
- gl.users.get_by_username('asdf')
-except gitlab.GitlabGetError:
- pass
-else:
- assert False
-
-# SSH keys
-key = new_user.keys.create({'title': 'testkey', 'key': SSH_KEY})
-assert(len(new_user.keys.list()) == 1)
-key.delete()
-assert(len(new_user.keys.list()) == 0)
-
-# emails
-email = new_user.emails.create({'email': 'foo2@bar.com'})
-assert(len(new_user.emails.list()) == 1)
-email.delete()
-assert(len(new_user.emails.list()) == 0)
-
-new_user.delete()
-foobar_user.delete()
-assert(len(gl.users.list()) == 3)
-
-# current user key
-key = gl.user.keys.create({'title': 'testkey', 'key': SSH_KEY})
-assert(len(gl.user.keys.list()) == 1)
-key.delete()
-
-# groups
-user1 = gl.users.create({'email': 'user1@test.com', 'username': 'user1',
- 'name': 'user1', 'password': 'user1_pass'})
-user2 = gl.users.create({'email': 'user2@test.com', 'username': 'user2',
- 'name': 'user2', 'password': 'user2_pass'})
-group1 = gl.groups.create({'name': 'group1', 'path': 'group1'})
-group2 = gl.groups.create({'name': 'group2', 'path': 'group2'})
-
-p_id = gl.groups.search('group2')[0].id
-group3 = gl.groups.create({'name': 'group3', 'path': 'group3', 'parent_id': p_id})
-
-assert(len(gl.groups.list()) == 3)
-assert(len(gl.groups.search("oup1")) == 1)
-assert(group3.parent_id == p_id)
-
-group1.members.create({'access_level': gitlab.Group.OWNER_ACCESS,
- 'user_id': user1.id})
-group1.members.create({'access_level': gitlab.Group.GUEST_ACCESS,
- 'user_id': user2.id})
-
-group2.members.create({'access_level': gitlab.Group.OWNER_ACCESS,
- 'user_id': user2.id})
-
-# Administrator belongs to the groups
-assert(len(group1.members.list()) == 3)
-assert(len(group2.members.list()) == 2)
-
-group1.members.delete(user1.id)
-assert(len(group1.members.list()) == 2)
-member = group1.members.get(user2.id)
-member.access_level = gitlab.Group.OWNER_ACCESS
-member.save()
-member = group1.members.get(user2.id)
-assert(member.access_level == gitlab.Group.OWNER_ACCESS)
-
-group2.members.delete(gl.user.id)
-
-# hooks
-hook = gl.hooks.create({'url': 'http://whatever.com'})
-assert(len(gl.hooks.list()) == 1)
-hook.delete()
-assert(len(gl.hooks.list()) == 0)
-
-# projects
-admin_project = gl.projects.create({'name': 'admin_project'})
-gr1_project = gl.projects.create({'name': 'gr1_project',
- 'namespace_id': group1.id})
-gr2_project = gl.projects.create({'name': 'gr2_project',
- 'namespace_id': group2.id})
-sudo_project = gl.projects.create({'name': 'sudo_project'}, sudo=user1.name)
-
-assert(len(gl.projects.all()) == 4)
-assert(len(gl.projects.owned()) == 2)
-assert(len(gl.projects.list(search="admin")) == 1)
-
-# test pagination
-l1 = gl.projects.list(per_page=1, page=1)
-l2 = gl.projects.list(per_page=1, page=2)
-assert(len(l1) == 1)
-assert(len(l2) == 1)
-assert(l1[0].id != l2[0].id)
-
-# project content (files)
-admin_project.files.create({'file_path': 'README',
- 'branch_name': 'master',
- 'content': 'Initial content',
- 'commit_message': 'Initial commit'})
-readme = admin_project.files.get(file_path='README', ref='master')
-readme.content = base64.b64encode("Improved README")
-time.sleep(2)
-readme.save(branch_name="master", commit_message="new commit")
-readme.delete(commit_message="Removing README", branch_name="master")
-
-admin_project.files.create({'file_path': 'README.rst',
- 'branch_name': 'master',
- 'content': 'Initial content',
- 'commit_message': 'New commit'})
-readme = admin_project.files.get(file_path='README.rst', ref='master')
-assert(readme.decode() == 'Initial content')
-
-data = {
- 'branch_name': 'master',
- 'commit_message': 'blah blah blah',
- 'actions': [
- {
- 'action': 'create',
- 'file_path': 'blah',
- 'content': 'blah'
- }
- ]
-}
-admin_project.commits.create(data)
-
-tree = admin_project.repository_tree()
-assert(len(tree) == 2)
-assert(tree[0]['name'] == 'README.rst')
-blob = admin_project.repository_blob('master', 'README.rst')
-assert(blob == 'Initial content')
-archive1 = admin_project.repository_archive()
-archive2 = admin_project.repository_archive('master')
-assert(archive1 == archive2)
-
-# project file uploads
-filename = "test.txt"
-file_contents = "testing contents"
-uploaded_file = admin_project.upload(filename, file_contents)
-assert(uploaded_file["alt"] == filename)
-assert(uploaded_file["url"].startswith("/uploads/"))
-assert(uploaded_file["url"].endswith("/" + filename))
-assert(uploaded_file["markdown"] == "[{}]({})".format(
- uploaded_file["alt"],
- uploaded_file["url"],
-))
-
-# deploy keys
-deploy_key = admin_project.keys.create({'title': 'foo@bar', 'key': DEPLOY_KEY})
-project_keys = admin_project.keys.list()
-assert(len(project_keys) == 1)
-sudo_project.keys.enable(deploy_key.id)
-assert(len(sudo_project.keys.list()) == 1)
-sudo_project.keys.disable(deploy_key.id)
-assert(len(sudo_project.keys.list()) == 0)
-
-# labels
-label1 = admin_project.labels.create({'name': 'label1', 'color': '#778899'})
-label1 = admin_project.labels.get('label1')
-assert(len(admin_project.labels.list()) == 1)
-label1.new_name = 'label1updated'
-label1.save()
-assert(label1.name == 'label1updated')
-label1.subscribe()
-assert(label1.subscribed == True)
-label1.unsubscribe()
-assert(label1.subscribed == False)
-label1.delete()
-
-# milestones
-m1 = admin_project.milestones.create({'title': 'milestone1'})
-assert(len(admin_project.milestones.list()) == 1)
-m1.due_date = '2020-01-01T00:00:00Z'
-m1.save()
-m1.state_event = 'close'
-m1.save()
-m1 = admin_project.milestones.get(1)
-assert(m1.state == 'closed')
-
-# issues
-issue1 = admin_project.issues.create({'title': 'my issue 1',
- 'milestone_id': m1.id})
-issue2 = admin_project.issues.create({'title': 'my issue 2'})
-issue3 = admin_project.issues.create({'title': 'my issue 3'})
-assert(len(admin_project.issues.list()) == 3)
-issue3.state_event = 'close'
-issue3.save()
-assert(len(admin_project.issues.list(state='closed')) == 1)
-assert(len(admin_project.issues.list(state='opened')) == 2)
-assert(len(admin_project.issues.list(milestone='milestone1')) == 1)
-assert(m1.issues()[0].title == 'my issue 1')
-
-# tags
-tag1 = admin_project.tags.create({'tag_name': 'v1.0', 'ref': 'master'})
-assert(len(admin_project.tags.list()) == 1)
-tag1.set_release_description('Description 1')
-tag1.set_release_description('Description 2')
-assert(tag1.release.description == 'Description 2')
-tag1.delete()
-
-# triggers
-tr1 = admin_project.triggers.create({})
-assert(len(admin_project.triggers.list()) == 1)
-tr1 = admin_project.triggers.get(tr1.token)
-tr1.delete()
-
-# variables
-v1 = admin_project.variables.create({'key': 'key1', 'value': 'value1'})
-assert(len(admin_project.variables.list()) == 1)
-v1.value = 'new_value1'
-v1.save()
-v1 = admin_project.variables.get(v1.key)
-assert(v1.value == 'new_value1')
-v1.delete()
-
-# branches and merges
-to_merge = admin_project.branches.create({'branch_name': 'branch1',
- 'ref': 'master'})
-admin_project.files.create({'file_path': 'README2.rst',
- 'branch_name': 'branch1',
- 'content': 'Initial content',
- 'commit_message': 'New commit in new branch'})
-mr = admin_project.mergerequests.create({'source_branch': 'branch1',
- 'target_branch': 'master',
- 'title': 'MR readme2'})
-ret = mr.merge()
-admin_project.branches.delete('branch1')
-
-try:
- mr.merge()
-except gitlab.GitlabMRClosedError:
- pass
-
-# stars
-admin_project = admin_project.star()
-assert(admin_project.star_count == 1)
-admin_project = admin_project.unstar()
-assert(admin_project.star_count == 0)
-
-# project boards
-#boards = admin_project.boards.list()
-#assert(len(boards))
-#board = boards[0]
-#lists = board.lists.list()
-#begin_size = len(lists)
-#last_list = lists[-1]
-#last_list.position = 0
-#last_list.save()
-#last_list.delete()
-#lists = board.lists.list()
-#assert(len(lists) == begin_size - 1)
-
-# namespaces
-ns = gl.namespaces.list()
-assert(len(ns) != 0)
-ns = gl.namespaces.list(search='root')[0]
-assert(ns.kind == 'user')
-
-# broadcast messages
-msg = gl.broadcastmessages.create({'message': 'this is the message'})
-msg.color = '#444444'
-msg.save()
-msg = gl.broadcastmessages.list()[0]
-assert(msg.color == '#444444')
-msg = gl.broadcastmessages.get(1)
-assert(msg.color == '#444444')
-msg.delete()
-assert(len(gl.broadcastmessages.list()) == 0)
-
-# notification settings
-settings = gl.notificationsettings.get()
-settings.level = gitlab.NOTIFICATION_LEVEL_WATCH
-settings.save()
-settings = gl.notificationsettings.get()
-assert(settings.level == gitlab.NOTIFICATION_LEVEL_WATCH)
-
-# services
-service = admin_project.services.get(service_name='asana')
-service.active = True
-service.api_key = 'whatever'
-service.save()
-service = admin_project.services.get(service_name='asana')
-assert(service.active == True)
-
-# snippets
-snippets = gl.snippets.list()
-assert(len(snippets) == 0)
-snippet = gl.snippets.create({'title': 'snippet1', 'file_name': 'snippet1.py',
- 'content': 'import gitlab'})
-snippet = gl.snippets.get(1)
-snippet.title = 'updated_title'
-snippet.save()
-snippet = gl.snippets.get(1)
-assert(snippet.title == 'updated_title')
-content = snippet.raw()
-assert(content == 'import gitlab')
-snippet.delete()
-assert(len(gl.snippets.list()) == 0)
diff --git a/tools/python_test_v4.py b/tools/python_test_v4.py
index 407a03c..885492b 100644
--- a/tools/python_test_v4.py
+++ b/tools/python_test_v4.py
@@ -214,12 +214,12 @@ assert(len(gl.groups.list(search='oup1')) == 1)
assert(group3.parent_id == p_id)
assert(group2.subgroups.list()[0].id == group3.id)
-group1.members.create({'access_level': gitlab.Group.OWNER_ACCESS,
+group1.members.create({'access_level': gitlab.const.OWNER_ACCESS,
'user_id': user1.id})
-group1.members.create({'access_level': gitlab.Group.GUEST_ACCESS,
+group1.members.create({'access_level': gitlab.const.GUEST_ACCESS,
'user_id': user2.id})
-group2.members.create({'access_level': gitlab.Group.OWNER_ACCESS,
+group2.members.create({'access_level': gitlab.const.OWNER_ACCESS,
'user_id': user2.id})
# Administrator belongs to the groups
@@ -229,10 +229,10 @@ assert(len(group2.members.list()) == 2)
group1.members.delete(user1.id)
assert(len(group1.members.list()) == 2)
member = group1.members.get(user2.id)
-member.access_level = gitlab.Group.OWNER_ACCESS
+member.access_level = gitlab.const.OWNER_ACCESS
member.save()
member = group1.members.get(user2.id)
-assert(member.access_level == gitlab.Group.OWNER_ACCESS)
+assert(member.access_level == gitlab.const.OWNER_ACCESS)
group2.members.delete(gl.user.id)
diff --git a/tox.ini b/tox.ini
index 5f01e78..f5aaeef 100644
--- a/tox.ini
+++ b/tox.ini
@@ -34,14 +34,8 @@ commands =
coverage report --omit=*tests*
coverage html --omit=*tests*
-[testenv:cli_func_v3]
-commands = {toxinidir}/tools/functional_tests.sh -a 3
-
[testenv:cli_func_v4]
commands = {toxinidir}/tools/functional_tests.sh -a 4
-[testenv:py_func_v3]
-commands = {toxinidir}/tools/py_functional_tests.sh -a 3
-
[testenv:py_func_v4]
commands = {toxinidir}/tools/py_functional_tests.sh -a 4