summaryrefslogtreecommitdiff
path: root/gitlab
diff options
context:
space:
mode:
Diffstat (limited to 'gitlab')
-rw-r--r--gitlab/__init__.py38
-rw-r--r--gitlab/base.py2
-rw-r--r--gitlab/config.py6
-rw-r--r--gitlab/exceptions.py4
-rw-r--r--gitlab/mixins.py5
-rw-r--r--gitlab/tests/test_base.py3
-rw-r--r--gitlab/tests/test_gitlab.py6
-rw-r--r--gitlab/tests/test_gitlabobject.py44
-rw-r--r--gitlab/tests/test_manager.py3
-rw-r--r--gitlab/tests/test_mixins.py86
-rw-r--r--gitlab/v3/objects.py15
-rw-r--r--gitlab/v4/objects.py416
12 files changed, 422 insertions, 206 deletions
diff --git a/gitlab/__init__.py b/gitlab/__init__.py
index aac4837..c909f9f 100644
--- a/gitlab/__init__.py
+++ b/gitlab/__init__.py
@@ -34,7 +34,7 @@ from gitlab.exceptions import * # noqa
from gitlab.v3.objects import * # noqa
__title__ = 'python-gitlab'
-__version__ = '1.1.0'
+__version__ = '1.2.0'
__author__ = 'Gauvain Pocentek'
__email__ = 'gauvain@pocentek.net'
__license__ = 'LGPL3'
@@ -73,7 +73,7 @@ class Gitlab(object):
def __init__(self, url, private_token=None, oauth_token=None, email=None,
password=None, ssl_verify=True, http_username=None,
- http_password=None, timeout=None, api_version='3',
+ http_password=None, timeout=None, api_version='4',
session=None):
self._api_version = str(api_version)
@@ -125,6 +125,9 @@ class Gitlab(object):
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':
@@ -144,6 +147,12 @@ class Gitlab(object):
manager = getattr(objects, cls_name)(self)
setattr(self, var_name, manager)
+ def __enter__(self):
+ return self
+
+ def __exit__(self, *args):
+ self.session.close()
+
def __getstate__(self):
state = self.__dict__.copy()
state.pop('_objects')
@@ -640,8 +649,22 @@ class Gitlab(object):
return parsed._replace(path=new_path).geturl()
url = self._build_url(path)
- params = query_data.copy()
- params.update(kwargs)
+
+ def copy_dict(dest, src):
+ for k, v in src.items():
+ if isinstance(v, dict):
+ # Transform dict values in new attributes. For example:
+ # custom_attributes: {'foo', 'bar'} =>
+ # custom_attributes['foo']: 'bar'
+ for dict_k, dict_v in v.items():
+ dest['%s[%s]' % (k, dict_k)] = dict_v
+ else:
+ dest[k] = v
+
+ params = {}
+ copy_dict(params, query_data)
+ copy_dict(params, kwargs)
+
opts = self._get_session_opts(content_type='application/json')
# don't set the content-type header when uploading files
@@ -661,8 +684,9 @@ class Gitlab(object):
files=files, **opts)
prepped = self.session.prepare_request(req)
prepped.url = sanitized_url(prepped.url)
- result = self.session.send(prepped, stream=streamed, verify=verify,
- timeout=timeout)
+ settings = self.session.merge_environment_settings(
+ prepped.url, {}, streamed, verify, None)
+ result = self.session.send(prepped, timeout=timeout, **settings)
if 200 <= result.status_code < 300:
return result
@@ -743,7 +767,7 @@ class Gitlab(object):
if get_all is True:
return list(GitlabList(self, url, query_data, **kwargs))
- if 'page' in kwargs or 'per_page' in kwargs or as_list is True:
+ if 'page' in kwargs or as_list is True:
# pagination requested, we return a list
return list(GitlabList(self, url, query_data, get_next=False,
**kwargs))
diff --git a/gitlab/base.py b/gitlab/base.py
index ec5f698..fd79c53 100644
--- a/gitlab/base.py
+++ b/gitlab/base.py
@@ -764,7 +764,7 @@ class RESTManager(object):
if self._parent is None or not hasattr(self, '_from_parent_attrs'):
return path
- data = {self_attr: getattr(self._parent, parent_attr)
+ data = {self_attr: getattr(self._parent, parent_attr, None)
for self_attr, parent_attr in self._from_parent_attrs.items()}
self._parent_attrs = data
return path % data
diff --git a/gitlab/config.py b/gitlab/config.py
index 9cf208c..0f4c424 100644
--- a/gitlab/config.py
+++ b/gitlab/config.py
@@ -128,7 +128,11 @@ class GitlabConfigParser(object):
except Exception:
pass
- self.api_version = '3'
+ self.api_version = '4'
+ try:
+ self.api_version = self._config.get('global', 'api_version')
+ except Exception:
+ pass
try:
self.api_version = self._config.get(self.gitlab_id, 'api_version')
except Exception:
diff --git a/gitlab/exceptions.py b/gitlab/exceptions.py
index 9a423dd..5825d23 100644
--- a/gitlab/exceptions.py
+++ b/gitlab/exceptions.py
@@ -193,6 +193,10 @@ class GitlabHousekeepingError(GitlabOperationError):
pass
+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.
diff --git a/gitlab/mixins.py b/gitlab/mixins.py
index c9243ed..cb35efc 100644
--- a/gitlab/mixins.py
+++ b/gitlab/mixins.py
@@ -242,7 +242,7 @@ class SetMixin(object):
GitlabSetError: If an error occured
Returns:
- UserCustomAttribute: The created/updated user attribute
+ obj: The created/updated attribute
"""
path = '%s/%s' % (self.path, key.replace('/', '%2F'))
data = {'value': value}
@@ -303,6 +303,9 @@ class SaveMixin(object):
GitlabUpdateError: If the server cannot perform the request
"""
updated_data = self._get_updated_data()
+ # Nothing to update. Server fails if sent an empty dict.
+ if not updated_data:
+ return
# call the manager
obj_id = self.get_id()
diff --git a/gitlab/tests/test_base.py b/gitlab/tests/test_base.py
index 31dd967..36cb63b 100644
--- a/gitlab/tests/test_base.py
+++ b/gitlab/tests/test_base.py
@@ -61,9 +61,6 @@ class TestRESTManager(unittest.TestCase):
mgr = MGR(FakeGitlab(), parent=Parent())
self.assertEqual(mgr._computed_path, '/tests/42/cases')
- self.assertRaises(AttributeError, MGR, FakeGitlab(),
- parent=BrokenParent())
-
def test_path_property(self):
class MGR(base.RESTManager):
_path = '/tests'
diff --git a/gitlab/tests/test_gitlab.py b/gitlab/tests/test_gitlab.py
index d33df99..1a1f3d8 100644
--- a/gitlab/tests/test_gitlab.py
+++ b/gitlab/tests/test_gitlab.py
@@ -53,7 +53,7 @@ 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)
+ ssl_verify=True, api_version=3)
@urlmatch(scheme="http", netloc="localhost", path="/api/v3/known_path",
method="get")
@@ -454,7 +454,7 @@ 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)
+ ssl_verify=True, api_version=3)
def test_list(self):
@urlmatch(scheme="http", netloc="localhost",
@@ -938,7 +938,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)
+ ssl_verify=True, api_version=3)
def test_pickability(self):
original_gl_objects = self.gl._objects
diff --git a/gitlab/tests/test_gitlabobject.py b/gitlab/tests/test_gitlabobject.py
index f7fd187..844ba9e 100644
--- a/gitlab/tests/test_gitlabobject.py
+++ b/gitlab/tests/test_gitlabobject.py
@@ -34,7 +34,7 @@ from httmock import urlmatch # noqa
from gitlab import * # noqa
-@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'}
@@ -42,7 +42,7 @@ def resp_get_project(url, request):
return response(200, content, headers, None, 5, request)
-@urlmatch(scheme="http", netloc="localhost", path="/api/v3/projects",
+@urlmatch(scheme="http", netloc="localhost", path="/api/v4/projects",
method="get")
def resp_list_project(url, request):
headers = {'content-type': 'application/json'}
@@ -50,7 +50,7 @@ def resp_list_project(url, request):
return response(200, content, headers, None, 5, request)
-@urlmatch(scheme="http", netloc="localhost", path="/api/v3/issues/1",
+@urlmatch(scheme="http", netloc="localhost", path="/api/v4/issues/1",
method="get")
def resp_get_issue(url, request):
headers = {'content-type': 'application/json'}
@@ -58,7 +58,7 @@ def resp_get_issue(url, request):
return response(200, content, headers, None, 5, request)
-@urlmatch(scheme="http", netloc="localhost", path="/api/v3/users/1",
+@urlmatch(scheme="http", netloc="localhost", path="/api/v4/users/1",
method="put")
def resp_update_user(url, request):
headers = {'content-type': 'application/json'}
@@ -67,7 +67,7 @@ def resp_update_user(url, request):
return response(200, content, headers, None, 5, request)
-@urlmatch(scheme="http", netloc="localhost", path="/api/v3/projects",
+@urlmatch(scheme="http", netloc="localhost", path="/api/v4/projects",
method="post")
def resp_create_project(url, request):
headers = {'content-type': 'application/json'}
@@ -75,7 +75,7 @@ def resp_create_project(url, request):
return response(201, content, headers, None, 5, request)
-@urlmatch(scheme="http", netloc="localhost", path="/api/v3/groups/2/members",
+@urlmatch(scheme="http", netloc="localhost", path="/api/v4/groups/2/members",
method="post")
def resp_create_groupmember(url, request):
headers = {'content-type': 'application/json'}
@@ -84,14 +84,14 @@ def resp_create_groupmember(url, request):
@urlmatch(scheme="http", netloc="localhost",
- path="/api/v3/projects/2/snippets/3", method="get")
+ 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/v3/groups/1",
+@urlmatch(scheme="http", netloc="localhost", path="/api/v4/groups/1",
method="delete")
def resp_delete_group(url, request):
headers = {'content-type': 'application/json'}
@@ -100,7 +100,7 @@ def resp_delete_group(url, request):
@urlmatch(scheme="http", netloc="localhost",
- path="/api/v3/groups/2/projects/3",
+ path="/api/v4/groups/2/projects/3",
method="post")
def resp_transfer_project(url, request):
headers = {'content-type': 'application/json'}
@@ -109,7 +109,7 @@ def resp_transfer_project(url, request):
@urlmatch(scheme="http", netloc="localhost",
- path="/api/v3/groups/2/projects/3",
+ path="/api/v4/groups/2/projects/3",
method="post")
def resp_transfer_project_fail(url, request):
headers = {'content-type': 'application/json'}
@@ -118,7 +118,7 @@ def resp_transfer_project_fail(url, request):
@urlmatch(scheme="http", netloc="localhost",
- path="/api/v3/projects/2/repository/branches/branchname/protect",
+ path="/api/v4/projects/2/repository/branches/branchname/protect",
method="put")
def resp_protect_branch(url, request):
headers = {'content-type': 'application/json'}
@@ -127,7 +127,7 @@ def resp_protect_branch(url, request):
@urlmatch(scheme="http", netloc="localhost",
- path="/api/v3/projects/2/repository/branches/branchname/unprotect",
+ path="/api/v4/projects/2/repository/branches/branchname/unprotect",
method="put")
def resp_unprotect_branch(url, request):
headers = {'content-type': 'application/json'}
@@ -136,7 +136,7 @@ def resp_unprotect_branch(url, request):
@urlmatch(scheme="http", netloc="localhost",
- path="/api/v3/projects/2/repository/branches/branchname/protect",
+ path="/api/v4/projects/2/repository/branches/branchname/protect",
method="put")
def resp_protect_branch_fail(url, request):
headers = {'content-type': 'application/json'}
@@ -157,7 +157,7 @@ class TestGitlabObject(unittest.TestCase):
data = json.loads(json_str)
self.assertIn("id", data)
self.assertEqual(data["username"], "testname")
- self.assertEqual(data["gitlab"]["url"], "http://localhost/api/v3")
+ self.assertEqual(data["gitlab"]["url"], "http://localhost/api/v4")
def test_pickability(self):
gl_object = CurrentUser(self.gl, data={"username": "testname"})
@@ -381,7 +381,7 @@ class TestProjectCommit(unittest.TestCase):
self.obj = ProjectCommit(self.gl, data={"id": 3, "project_id": 2})
@urlmatch(scheme="http", netloc="localhost",
- path="/api/v3/projects/2/repository/commits/3/diff",
+ path="/api/v4/projects/2/repository/commits/3/diff",
method="get")
def resp_diff(self, url, request):
headers = {'content-type': 'application/json'}
@@ -389,7 +389,7 @@ class TestProjectCommit(unittest.TestCase):
return response(200, content, headers, None, 5, request)
@urlmatch(scheme="http", netloc="localhost",
- path="/api/v3/projects/2/repository/commits/3/diff",
+ path="/api/v4/projects/2/repository/commits/3/diff",
method="get")
def resp_diff_fail(self, url, request):
headers = {'content-type': 'application/json'}
@@ -397,7 +397,7 @@ class TestProjectCommit(unittest.TestCase):
return response(400, content, headers, None, 5, request)
@urlmatch(scheme="http", netloc="localhost",
- path="/api/v3/projects/2/repository/blobs/3",
+ path="/api/v4/projects/2/repository/blobs/3",
method="get")
def resp_blob(self, url, request):
headers = {'content-type': 'application/json'}
@@ -405,7 +405,7 @@ class TestProjectCommit(unittest.TestCase):
return response(200, content, headers, None, 5, request)
@urlmatch(scheme="http", netloc="localhost",
- path="/api/v3/projects/2/repository/blobs/3",
+ path="/api/v4/projects/2/repository/blobs/3",
method="get")
def resp_blob_fail(self, url, request):
headers = {'content-type': 'application/json'}
@@ -440,7 +440,7 @@ class TestProjectSnippet(unittest.TestCase):
self.obj = ProjectSnippet(self.gl, data={"id": 3, "project_id": 2})
@urlmatch(scheme="http", netloc="localhost",
- path="/api/v3/projects/2/snippets/3/raw",
+ path="/api/v4/projects/2/snippets/3/raw",
method="get")
def resp_content(self, url, request):
headers = {'content-type': 'application/json'}
@@ -448,7 +448,7 @@ class TestProjectSnippet(unittest.TestCase):
return response(200, content, headers, None, 5, request)
@urlmatch(scheme="http", netloc="localhost",
- path="/api/v3/projects/2/snippets/3/raw",
+ path="/api/v4/projects/2/snippets/3/raw",
method="get")
def resp_content_fail(self, url, request):
headers = {'content-type': 'application/json'}
@@ -474,7 +474,7 @@ class TestSnippet(unittest.TestCase):
self.obj = Snippet(self.gl, data={"id": 3})
@urlmatch(scheme="http", netloc="localhost",
- path="/api/v3/snippets/3/raw",
+ path="/api/v4/snippets/3/raw",
method="get")
def resp_content(self, url, request):
headers = {'content-type': 'application/json'}
@@ -482,7 +482,7 @@ class TestSnippet(unittest.TestCase):
return response(200, content, headers, None, 5, request)
@urlmatch(scheme="http", netloc="localhost",
- path="/api/v3/snippets/3/raw",
+ path="/api/v4/snippets/3/raw",
method="get")
def resp_content_fail(self, url, request):
headers = {'content-type': 'application/json'}
diff --git a/gitlab/tests/test_manager.py b/gitlab/tests/test_manager.py
index 5cd3130..c6ef299 100644
--- a/gitlab/tests/test_manager.py
+++ b/gitlab/tests/test_manager.py
@@ -52,7 +52,8 @@ 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)
+ password="testpassword", ssl_verify=True,
+ api_version=3)
def test_set_parent_args(self):
@urlmatch(scheme="http", netloc="localhost", path="/api/v3/fake",
diff --git a/gitlab/tests/test_mixins.py b/gitlab/tests/test_mixins.py
index e78c757..c51322a 100644
--- a/gitlab/tests/test_mixins.py
+++ b/gitlab/tests/test_mixins.py
@@ -434,89 +434,3 @@ class TestMixinMethods(unittest.TestCase):
self.assertIsInstance(obj, FakeObject)
self.assertEqual(obj.key, 'foo')
self.assertEqual(obj.value, 'bar')
-
-
-class TestExceptions(unittest.TestCase):
- def setUp(self):
- self.gl = Gitlab("http://localhost", private_token="private_token",
- api_version=4)
-
- def test_get_mixin(self):
- class M(GetMixin, FakeManager):
- pass
-
- m = M(self.gl)
- self.assertRaises(GitlabGetError, m.get, 1)
-
- def test_get_without_id_mixin(self):
- class M(GetWithoutIdMixin, FakeManager):
- pass
-
- m = M(self.gl)
- self.assertRaises(GitlabGetError, m.get)
-
- def test_list_mixin(self):
- class M(ListMixin, FakeManager):
- pass
-
- m = M(self.gl)
- self.assertRaises(GitlabListError, m.list)
-
- def test_get_from_list_mixin(self):
- class M(GetFromListMixin, FakeManager):
- pass
-
- m = M(self.gl)
- self.assertRaises(GitlabListError, m.list)
- self.assertRaises(GitlabGetError, m.get, 1)
-
- def test_create_mixin(self):
- class M(CreateMixin, FakeManager):
- pass
-
- m = M(self.gl)
- self.assertRaises(GitlabCreateError, m.create, {})
-
- def test_update_mixin(self):
- class M(UpdateMixin, FakeManager):
- pass
-
- m = M(self.gl)
- self.assertRaises(GitlabUpdateError, m.update, 1, {})
-
- def test_set_mixin(self):
- class M(SetMixin, FakeManager):
- pass
-
- m = M(self.gl)
- self.assertRaises(GitlabSetError, m.set, 'foo', 'bar')
-
- def test_delete_mixin(self):
- class M(DeleteMixin, FakeManager):
- pass
-
- m = M(self.gl)
- self.assertRaises(GitlabDeleteError, m.delete, 1)
-
- def test_object_mixin(self):
- class M(UpdateMixin, DeleteMixin, FakeManager):
- pass
-
- class O(SaveMixin, ObjectDeleteMixin, AccessRequestMixin,
- SubscribableMixin, TodoMixin, TimeTrackingMixin, RESTObject):
- pass
-
- mgr = M(self.gl)
- obj = O(mgr, {'id': 42, 'foo': 'bar'})
- obj.foo = 'baz'
- self.assertRaises(GitlabUpdateError, obj.save)
- self.assertRaises(GitlabDeleteError, obj.delete)
- self.assertRaises(GitlabUpdateError, obj.approve)
- self.assertRaises(GitlabSubscribeError, obj.subscribe)
- self.assertRaises(GitlabUnsubscribeError, obj.unsubscribe)
- self.assertRaises(GitlabTodoError, obj.todo)
- self.assertRaises(GitlabTimeTrackingError, obj.time_stats)
- self.assertRaises(GitlabTimeTrackingError, obj.time_estimate, '1d')
- self.assertRaises(GitlabTimeTrackingError, obj.reset_time_estimate)
- self.assertRaises(GitlabTimeTrackingError, obj.add_spent_time, '1d')
- self.assertRaises(GitlabTimeTrackingError, obj.reset_spent_time)
diff --git a/gitlab/v3/objects.py b/gitlab/v3/objects.py
index 014714e..0db9dfd 100644
--- a/gitlab/v3/objects.py
+++ b/gitlab/v3/objects.py
@@ -934,7 +934,7 @@ class ProjectIssue(GitlabObject):
{'project_id': self.project_id, 'issue_id': self.id})
r = self.gitlab._raw_post(url, **kwargs)
- raise_error_from_response(r, GitlabSubscribeError)
+ raise_error_from_response(r, GitlabSubscribeError, 201)
self._set_from_dict(r.json())
def unsubscribe(self, **kwargs):
@@ -1496,18 +1496,6 @@ class ProjectFileManager(BaseManager):
obj_cls = ProjectFile
-class ProjectPipelineSchedule(GitlabObject):
- _url = '/projects/%(project_id)s/pipeline_schedules'
- _create_url = '/projects/%(project_id)s/pipeline_schedules'
-
- requiredUrlAttrs = ['project_id']
- requiredCreateAttrs = ['description', 'ref', 'cron']
-
-
-class ProjectPipelineSchedulesManager(BaseManager):
- obj_cls = ProjectPipelineSchedule
-
-
class ProjectPipeline(GitlabObject):
_url = '/projects/%(project_id)s/pipelines'
_create_url = '/projects/%(project_id)s/pipeline'
@@ -1815,7 +1803,6 @@ class Project(GitlabObject):
('notificationsettings', 'ProjectNotificationSettingsManager',
[('project_id', 'id')]),
('pipelines', 'ProjectPipelineManager', [('project_id', 'id')]),
- ('pipeline_schedules', 'ProjectPipelineSchedulesManager', [('project_id', 'id')]),
('runners', 'ProjectRunnerManager', [('project_id', 'id')]),
('services', 'ProjectServiceManager', [('project_id', 'id')]),
('snippets', 'ProjectSnippetManager', [('project_id', 'id')]),
diff --git a/gitlab/v4/objects.py b/gitlab/v4/objects.py
index 17e987c..aff5e17 100644
--- a/gitlab/v4/objects.py
+++ b/gitlab/v4/objects.py
@@ -112,6 +112,17 @@ class SidekiqManager(RESTManager):
return self.gitlab.http_get('/sidekiq/compound_metrics', **kwargs)
+class Event(RESTObject):
+ _id_attr = None
+ _short_print_attr = 'target_title'
+
+
+class EventManager(ListMixin, RESTManager):
+ _path = '/events'
+ _obj_cls = Event
+ _list_filters = ('action', 'target_type', 'before', 'after', 'sort')
+
+
class UserActivities(RESTObject):
_id_attr = 'username'
@@ -143,6 +154,16 @@ class UserEmailManager(RetrieveMixin, CreateMixin, DeleteMixin, RESTManager):
_create_attrs = (('email', ), tuple())
+class UserEvent(Event):
+ pass
+
+
+class UserEventManager(EventManager):
+ _path = '/users/%(user_id)s/events'
+ _obj_cls = UserEvent
+ _from_parent_attrs = {'user_id': 'id'}
+
+
class UserGPGKey(ObjectDeleteMixin, RESTObject):
pass
@@ -181,7 +202,7 @@ class UserProject(RESTObject):
pass
-class UserProjectManager(CreateMixin, RESTManager):
+class UserProjectManager(ListMixin, CreateMixin, RESTManager):
_path = '/projects/user/%(user_id)s'
_obj_cls = UserProject
_from_parent_attrs = {'user_id': 'id'}
@@ -192,6 +213,31 @@ class UserProjectManager(CreateMixin, RESTManager):
'public', 'visibility', 'description', 'builds_enabled',
'public_builds', 'import_url', 'only_allow_merge_if_build_succeeds')
)
+ _list_filters = ('archived', 'visibility', 'order_by', 'sort', 'search',
+ 'simple', 'owned', 'membership', 'starred', 'statistics',
+ 'with_issues_enabled', 'with_merge_requests_enabled')
+
+ def list(self, **kwargs):
+ """Retrieve a list of objects.
+
+ Args:
+ all (bool): If True, return all the items, without pagination
+ per_page (int): Number of items to retrieve per request
+ page (int): ID of the page to return (starts with page 1)
+ as_list (bool): If set to False and no pagination option is
+ defined, return a generator instead of a list
+ **kwargs: Extra options to send to the Gitlab server (e.g. sudo)
+
+ Returns:
+ list: The list of objects, or a generator if `as_list` is False
+
+ Raises:
+ GitlabAuthenticationError: If authentication is not correct
+ GitlabListError: If the server cannot perform the request
+ """
+
+ path = '/users/%s/projects' % self._parent.id
+ return ListMixin.list(self, path=path, **kwargs)
class User(SaveMixin, ObjectDeleteMixin, RESTObject):
@@ -199,6 +245,7 @@ class User(SaveMixin, ObjectDeleteMixin, RESTObject):
_managers = (
('customattributes', 'UserCustomAttributeManager'),
('emails', 'UserEmailManager'),
+ ('events', 'UserEventManager'),
('gpgkeys', 'UserGPGKeyManager'),
('impersonationtokens', 'UserImpersonationTokenManager'),
('keys', 'UserKeyManager'),
@@ -253,7 +300,7 @@ class UserManager(CRUDMixin, RESTManager):
_obj_cls = User
_list_filters = ('active', 'blocked', 'username', 'extern_uid', 'provider',
- 'external', 'search')
+ 'external', 'search', 'custom_attributes')
_create_attrs = (
tuple(),
('email', 'username', 'name', 'password', 'reset_password', 'skype',
@@ -400,6 +447,38 @@ class DockerfileManager(RetrieveMixin, RESTManager):
_obj_cls = Dockerfile
+class Feature(RESTObject):
+ _id_attr = 'name'
+
+
+class FeatureManager(ListMixin, RESTManager):
+ _path = '/features/'
+ _obj_cls = Feature
+
+ @exc.on_http_error(exc.GitlabSetError)
+ def set(self, name, value, feature_group=None, user=None, **kwargs):
+ """Create or update the object.
+
+ Args:
+ name (str): The value to set for the object
+ value (bool/int): The value to set for the object
+ feature_group (str): A feature group name
+ user (str): A GitLab username
+ **kwargs: Extra options to send to the server (e.g. sudo)
+
+ Raises:
+ GitlabAuthenticationError: If authentication is not correct
+ GitlabSetError: If an error occured
+
+ Returns:
+ obj: The created/updated attribute
+ """
+ path = '%s/%s' % (self.path, name.replace('/', '%2F'))
+ data = {'value': value, 'feature_group': feature_group, 'user': user}
+ server_data = self.gitlab.http_post(path, post_data=data, **kwargs)
+ return self._obj_cls(self, server_data)
+
+
class Gitignore(RESTObject):
_id_attr = 'name'
@@ -429,6 +508,17 @@ class GroupAccessRequestManager(GetFromListMixin, CreateMixin, DeleteMixin,
_from_parent_attrs = {'group_id': 'id'}
+class GroupCustomAttribute(ObjectDeleteMixin, RESTObject):
+ _id_attr = 'key'
+
+
+class GroupCustomAttributeManager(RetrieveMixin, SetMixin, DeleteMixin,
+ RESTManager):
+ _path = '/groups/%(group_id)s/custom_attributes'
+ _obj_cls = GroupCustomAttribute
+ _from_parent_attrs = {'group_id': 'id'}
+
+
class GroupIssue(RESTObject):
pass
@@ -470,6 +560,11 @@ class GroupMilestone(SaveMixin, ObjectDeleteMixin, RESTObject):
"""List issues related to this milestone.
Args:
+ all (bool): If True, return all the items, without pagination
+ per_page (int): Number of items to retrieve per request
+ page (int): ID of the page to return (starts with page 1)
+ as_list (bool): If set to False and no pagination option is
+ defined, return a generator instead of a list
**kwargs: Extra options to send to the server (e.g. sudo)
Raises:
@@ -494,6 +589,11 @@ class GroupMilestone(SaveMixin, ObjectDeleteMixin, RESTObject):
"""List the merge requests related to this milestone.
Args:
+ all (bool): If True, return all the items, without pagination
+ per_page (int): Number of items to retrieve per request
+ page (int): ID of the page to return (starts with page 1)
+ as_list (bool): If set to False and no pagination option is
+ defined, return a generator instead of a list
**kwargs: Extra options to send to the server (e.g. sudo)
Raises:
@@ -544,6 +644,18 @@ class GroupProjectManager(GetFromListMixin, RESTManager):
'ci_enabled_first')
+class GroupSubgroup(RESTObject):
+ pass
+
+
+class GroupSubgroupManager(GetFromListMixin, RESTManager):
+ _path = '/groups/%(group_id)s/subgroups'
+ _obj_cls = GroupSubgroup
+ _from_parent_attrs = {'group_id': 'id'}
+ _list_filters = ('skip_groups', 'all_available', 'search', 'order_by',
+ 'sort', 'statistics', 'owned')
+
+
class GroupVariable(SaveMixin, ObjectDeleteMixin, RESTObject):
_id_attr = 'key'
@@ -560,11 +672,13 @@ class Group(SaveMixin, ObjectDeleteMixin, RESTObject):
_short_print_attr = 'name'
_managers = (
('accessrequests', 'GroupAccessRequestManager'),
+ ('customattributes', 'GroupCustomAttributeManager'),
+ ('issues', 'GroupIssueManager'),
('members', 'GroupMemberManager'),
('milestones', 'GroupMilestoneManager'),
('notificationsettings', 'GroupNotificationSettingsManager'),
('projects', 'GroupProjectManager'),
- ('issues', 'GroupIssueManager'),
+ ('subgroups', 'GroupSubgroupManager'),
('variables', 'GroupVariableManager'),
)
@@ -588,6 +702,8 @@ class Group(SaveMixin, ObjectDeleteMixin, RESTObject):
class GroupManager(CRUDMixin, RESTManager):
_path = '/groups'
_obj_cls = Group
+ _list_filters = ('skip_groups', 'all_available', 'search', 'order_by',
+ 'sort', 'statistics', 'owned', 'custom_attributes')
_create_attrs = (
('name', 'path'),
('description', 'visibility', 'parent_id', 'lfs_enabled',
@@ -698,6 +814,15 @@ class NamespaceManager(GetFromListMixin, RESTManager):
_list_filters = ('search', )
+class PagesDomain(RESTObject):
+ _id_attr = 'domain'
+
+
+class PagesDomainManager(ListMixin, RESTManager):
+ _path = '/pages/domains'
+ _obj_cls = PagesDomain
+
+
class ProjectBoardList(SaveMixin, ObjectDeleteMixin, RESTObject):
pass
@@ -773,6 +898,17 @@ class ProjectBranchManager(NoUpdateMixin, RESTManager):
_create_attrs = (('branch', 'ref'), tuple())
+class ProjectCustomAttribute(ObjectDeleteMixin, RESTObject):
+ _id_attr = 'key'
+
+
+class ProjectCustomAttributeManager(RetrieveMixin, SetMixin, DeleteMixin,
+ RESTManager):
+ _path = '/projects/%(project_id)s/custom_attributes'
+ _obj_cls = ProjectCustomAttribute
+ _from_parent_attrs = {'project_id': 'id'}
+
+
class ProjectJob(RESTObject):
@cli.register_custom_action('ProjectJob')
@exc.on_http_error(exc.GitlabJobCancelError)
@@ -1047,12 +1183,11 @@ class ProjectKeyManager(NoUpdateMixin, RESTManager):
self.gitlab.http_post(path, **kwargs)
-class ProjectEvent(RESTObject):
- _id_attr = None
- _short_print_attr = 'target_title'
+class ProjectEvent(Event):
+ pass
-class ProjectEventManager(ListMixin, RESTManager):
+class ProjectEventManager(EventManager):
_path = '/projects/%(project_id)s/events'
_obj_cls = ProjectEvent
_from_parent_attrs = {'project_id': 'id'}
@@ -1091,10 +1226,35 @@ class ProjectHookManager(CRUDMixin, RESTManager):
)
-class ProjectIssueNote(SaveMixin, ObjectDeleteMixin, RESTObject):
+class ProjectIssueAwardEmoji(ObjectDeleteMixin, RESTObject):
+ pass
+
+
+class ProjectIssueAwardEmojiManager(NoUpdateMixin, RESTManager):
+ _path = '/projects/%(project_id)s/issues/%(issue_iid)s/award_emoji'
+ _obj_cls = ProjectIssueAwardEmoji
+ _from_parent_attrs = {'project_id': 'project_id', 'issue_iid': 'iid'}
+ _create_attrs = (('name', ), tuple())
+
+
+class ProjectIssueNoteAwardEmoji(ObjectDeleteMixin, RESTObject):
pass
+class ProjectIssueNoteAwardEmojiManager(NoUpdateMixin, RESTManager):
+ _path = ('/projects/%(project_id)s/issues/%(issue_iid)s'
+ '/notes/%(note_id)s/award_emoji')
+ _obj_cls = ProjectIssueNoteAwardEmoji
+ _from_parent_attrs = {'project_id': 'project_id',
+ 'issue_iid': 'issue_iid',
+ 'note_id': 'id'}
+ _create_attrs = (('name', ), tuple())
+
+
+class ProjectIssueNote(SaveMixin, ObjectDeleteMixin, RESTObject):
+ _managers = (('awardemojis', 'ProjectIssueNoteAwardEmojiManager'),)
+
+
class ProjectIssueNoteManager(CRUDMixin, RESTManager):
_path = '/projects/%(project_id)s/issues/%(issue_iid)s/notes'
_obj_cls = ProjectIssueNote
@@ -1107,7 +1267,10 @@ class ProjectIssue(SubscribableMixin, TodoMixin, TimeTrackingMixin, SaveMixin,
ObjectDeleteMixin, RESTObject):
_short_print_attr = 'title'
_id_attr = 'iid'
- _managers = (('notes', 'ProjectIssueNoteManager'), )
+ _managers = (
+ ('notes', 'ProjectIssueNoteManager'),
+ ('awardemojis', 'ProjectIssueAwardEmojiManager'),
+ )
@cli.register_custom_action('ProjectIssue')
@exc.on_http_error(exc.GitlabUpdateError)
@@ -1196,6 +1359,18 @@ class ProjectNotificationSettingsManager(NotificationSettingsManager):
_from_parent_attrs = {'project_id': 'id'}
+class ProjectPagesDomain(SaveMixin, ObjectDeleteMixin, RESTObject):
+ _id_attr = 'domain'
+
+
+class ProjectPagesDomainManager(CRUDMixin, RESTManager):
+ _path = '/projects/%(project_id)s/pages/domains'
+ _obj_cls = ProjectPagesDomain
+ _from_parent_attrs = {'project_id': 'id'}
+ _create_attrs = (('domain', ), ('certificate', 'key'))
+ _update_attrs = (tuple(), ('certificate', 'key'))
+
+
class ProjectTag(ObjectDeleteMixin, RESTObject):
_id_attr = 'name'
_short_print_attr = 'name'
@@ -1243,6 +1418,17 @@ class ProjectTagManager(NoUpdateMixin, RESTManager):
_create_attrs = (('tag_name', 'ref'), ('message',))
+class ProjectMergeRequestAwardEmoji(ObjectDeleteMixin, RESTObject):
+ pass
+
+
+class ProjectMergeRequestAwardEmojiManager(NoUpdateMixin, RESTManager):
+ _path = '/projects/%(project_id)s/merge_requests/%(mr_iid)s/award_emoji'
+ _obj_cls = ProjectMergeRequestAwardEmoji
+ _from_parent_attrs = {'project_id': 'project_id', 'mr_iid': 'iid'}
+ _create_attrs = (('name', ), tuple())
+
+
class ProjectMergeRequestDiff(RESTObject):
pass
@@ -1253,10 +1439,24 @@ class ProjectMergeRequestDiffManager(RetrieveMixin, RESTManager):
_from_parent_attrs = {'project_id': 'project_id', 'mr_iid': 'iid'}
-class ProjectMergeRequestNote(SaveMixin, ObjectDeleteMixin, RESTObject):
+class ProjectMergeRequestNoteAwardEmoji(ObjectDeleteMixin, RESTObject):
pass
+class ProjectMergeRequestNoteAwardEmojiManager(NoUpdateMixin, RESTManager):
+ _path = ('/projects/%(project_id)s/merge_requests/%(mr_iid)s'
+ '/notes/%(note_id)s/award_emoji')
+ _obj_cls = ProjectMergeRequestNoteAwardEmoji
+ _from_parent_attrs = {'project_id': 'project_id',
+ 'mr_iid': 'issue_iid',
+ 'note_id': 'id'}
+ _create_attrs = (('name', ), tuple())
+
+
+class ProjectMergeRequestNote(SaveMixin, ObjectDeleteMixin, RESTObject):
+ _managers = (('awardemojis', 'ProjectMergeRequestNoteAwardEmojiManager'),)
+
+
class ProjectMergeRequestNoteManager(CRUDMixin, RESTManager):
_path = '/projects/%(project_id)s/merge_requests/%(mr_iid)s/notes'
_obj_cls = ProjectMergeRequestNote
@@ -1270,8 +1470,9 @@ class ProjectMergeRequest(SubscribableMixin, TodoMixin, TimeTrackingMixin,
_id_attr = 'iid'
_managers = (
+ ('awardemojis', 'ProjectMergeRequestAwardEmojiManager'),
+ ('diffs', 'ProjectMergeRequestDiffManager'),
('notes', 'ProjectMergeRequestNoteManager'),
- ('diffs', 'ProjectMergeRequestDiffManager')
)
@cli.register_custom_action('ProjectMergeRequest')
@@ -1299,6 +1500,11 @@ class ProjectMergeRequest(SubscribableMixin, TodoMixin, TimeTrackingMixin,
"""List issues that will close on merge."
Args:
+ all (bool): If True, return all the items, without pagination
+ per_page (int): Number of items to retrieve per request
+ page (int): ID of the page to return (starts with page 1)
+ as_list (bool): If set to False and no pagination option is
+ defined, return a generator instead of a list
**kwargs: Extra options to send to the server (e.g. sudo)
Raises:
@@ -1321,6 +1527,11 @@ class ProjectMergeRequest(SubscribableMixin, TodoMixin, TimeTrackingMixin,
"""List the merge request commits.
Args:
+ all (bool): If True, return all the items, without pagination
+ per_page (int): Number of items to retrieve per request
+ page (int): ID of the page to return (starts with page 1)
+ as_list (bool): If set to False and no pagination option is
+ defined, return a generator instead of a list
**kwargs: Extra options to send to the server (e.g. sudo)
Raises:
@@ -1392,6 +1603,30 @@ class ProjectMergeRequest(SubscribableMixin, TodoMixin, TimeTrackingMixin,
**kwargs)
self._update_attrs(server_data)
+ @cli.register_custom_action('ProjectMergeRequest')
+ @exc.on_http_error(exc.GitlabListError)
+ def participants(self, **kwargs):
+ """List the merge request participants.
+
+ Args:
+ all (bool): If True, return all the items, without pagination
+ per_page (int): Number of items to retrieve per request
+ page (int): ID of the page to return (starts with page 1)
+ as_list (bool): If set to False and no pagination option is
+ defined, return a generator instead of a list
+ **kwargs: Extra options to send to the server (e.g. sudo)
+
+ Raises:
+ GitlabAuthenticationError: If authentication is not correct
+ GitlabListError: If the list could not be retrieved
+
+ Returns:
+ RESTObjectList: The list of participants
+ """
+
+ path = '%s/%s/participants' % (self.manager.path, self.get_id())
+ return self.manager.gitlab.http_get(path, **kwargs)
+
class ProjectMergeRequestManager(CRUDMixin, RESTManager):
_path = '/projects/%(project_id)s/merge_requests'
@@ -1423,6 +1658,11 @@ class ProjectMilestone(SaveMixin, ObjectDeleteMixin, RESTObject):
"""List issues related to this milestone.
Args:
+ all (bool): If True, return all the items, without pagination
+ per_page (int): Number of items to retrieve per request
+ page (int): ID of the page to return (starts with page 1)
+ as_list (bool): If set to False and no pagination option is
+ defined, return a generator instead of a list
**kwargs: Extra options to send to the server (e.g. sudo)
Raises:
@@ -1447,6 +1687,11 @@ class ProjectMilestone(SaveMixin, ObjectDeleteMixin, RESTObject):
"""List the merge requests related to this milestone.
Args:
+ all (bool): If True, return all the items, without pagination
+ per_page (int): Number of items to retrieve per request
+ page (int): ID of the page to return (starts with page 1)
+ as_list (bool): If set to False and no pagination option is
+ defined, return a generator instead of a list
**kwargs: Extra options to send to the server (e.g. sudo)
Raises:
@@ -1625,9 +1870,10 @@ class ProjectFileManager(GetMixin, CreateMixin, UpdateMixin, DeleteMixin,
"""
self._check_missing_create_attrs(data)
- file_path = data.pop('file_path').replace('/', '%2F')
+ new_data = data.copy()
+ file_path = new_data.pop('file_path').replace('/', '%2F')
path = '%s/%s' % (self.path, file_path)
- server_data = self.gitlab.http_post(path, post_data=data, **kwargs)
+ server_data = self.gitlab.http_post(path, post_data=new_data, **kwargs)
return self._obj_cls(self, server_data)
@exc.on_http_error(exc.GitlabUpdateError)
@@ -1706,22 +1952,8 @@ class ProjectFileManager(GetMixin, CreateMixin, UpdateMixin, DeleteMixin,
return utils.response_content(result, streamed, action, chunk_size)
-class ProjectPipelineJob(ProjectJob):
- pass
-
-
-class ProjectPipelineJobsManager(ListMixin, RESTManager):
- _path = '/projects/%(project_id)s/pipelines/%(pipeline_id)s/jobs'
- _obj_cls = ProjectPipelineJob
- _from_parent_attrs = {'project_id': 'project_id',
- 'pipeline_id' : 'id'}
- _list_filters = ('scope',)
-
-
class ProjectPipeline(RESTObject):
- _managers = (
- ('jobs', 'ProjectPipelineJobsManager'),
- )
+ _managers = (('jobs', 'ProjectPipelineJobManager'), )
@cli.register_custom_action('ProjectPipeline')
@exc.on_http_error(exc.GitlabPipelineCancelError)
@@ -1784,65 +2016,79 @@ class ProjectPipelineScheduleVariable(SaveMixin, ObjectDeleteMixin, RESTObject):
_id_attr = 'key'
-class ProjectPipelineScheduleVariableManager(CRUDMixin, RESTManager):
- _path = '/projects/%(project_id)s/pipeline_schedules/%(pipeline_schedule_id)s/variables'
+class ProjectPipelineScheduleVariableManager(CreateMixin, UpdateMixin,
+ DeleteMixin, RESTManager):
+ _path = ('/projects/%(project_id)s/pipeline_schedules/'
+ '%(pipeline_schedule_id)s/variables')
_obj_cls = ProjectPipelineScheduleVariable
_from_parent_attrs = {'project_id': 'project_id',
'pipeline_schedule_id' : 'id'}
- _create_attrs = (('pipeline_schedule_id', 'key', 'value'), tuple())
_create_attrs = (('key', 'value'), tuple())
+ _update_attrs = (('key', 'value'), tuple())
- def list(self):
- array = []
- if 'variables' in self._parent._attrs:
- for variable in self._parent._attrs['variables']:
- schedule_variable = self._obj_cls(self, variable)
- array.append(schedule_variable)
- else:
- obj = self._parent.manager.get(self._parent.id)
- for variable in obj._attrs['variables']:
- schedule_variable = self._obj_cls(self, variable)
- array.append(schedule_variable)
- return array
+class ProjectPipelineSchedule(SaveMixin, ObjectDeleteMixin, RESTObject):
+ _managers = (('variables', 'ProjectPipelineScheduleVariableManager'),)
+ @cli.register_custom_action('ProjectPipelineSchedule')
+ @exc.on_http_error(exc.GitlabOwnershipError)
+ def take_ownership(self, **kwargs):
+ """Update the owner of a pipeline schedule.
-class ProjectPipelineSchedule(RESTObject):
- _managers = (
- ('variables', 'ProjectPipelineScheduleVariableManager'),
- )
+ Args:
+ **kwargs: Extra options to send to the server (e.g. sudo)
+ Raises:
+ GitlabAuthenticationError: If authentication is not correct
+ GitlabOwnershipError: If the request failed
+ """
+ path = '%s/%s/take_ownership' % (self.manager.path, self.get_id())
+ server_data = self.manager.gitlab.http_post(path, **kwargs)
+ self._update_attrs(server_data)
-class ProjectPipelineSchedulesManager(RetrieveMixin, CreateMixin, RESTManager):
+
+class ProjectPipelineScheduleManager(CRUDMixin, RESTManager):
_path = '/projects/%(project_id)s/pipeline_schedules'
_obj_cls = ProjectPipelineSchedule
_from_parent_attrs = {'project_id': 'id'}
_create_attrs = (('description', 'ref', 'cron'),
('cron_timezone', 'active'))
+ _update_attrs = (tuple(),
+ ('description', 'ref', 'cron', 'cron_timezone', 'active'))
- def create(self, data, **kwargs):
- """Creates a new object.
- Args:
- data (dict): Parameters to send to the server to create the
- resource
- **kwargs: Extra options to send to the server (e.g. sudo)
+class ProjectSnippetNote(SaveMixin, ObjectDeleteMixin, RESTObject):
+ pass
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabCreateError: If the server cannot perform the request
- Returns:
- RESTObject: A new instance of the managed object class build with
- the data sent by the server
- """
- return CreateMixin.create(self, data, path=self.path, **kwargs)
+class ProjectPipelineJob(ProjectJob):
+ pass
-class ProjectSnippetNote(SaveMixin, ObjectDeleteMixin, RESTObject):
+class ProjectPipelineJobManager(GetFromListMixin, RESTManager):
+ _path = '/projects/%(project_id)s/pipelines/%(pipeline_id)s/jobs'
+ _obj_cls = ProjectPipelineJob
+ _from_parent_attrs = {'project_id': 'project_id', 'pipeline_id': 'id'}
+
+
+class ProjectSnippetNoteAwardEmoji(ObjectDeleteMixin, RESTObject):
pass
+class ProjectSnippetNoteAwardEmojiManager(NoUpdateMixin, RESTManager):
+ _path = ('/projects/%(project_id)s/snippets/%(snippet_id)s'
+ '/notes/%(note_id)s/award_emoji')
+ _obj_cls = ProjectSnippetNoteAwardEmoji
+ _from_parent_attrs = {'project_id': 'project_id',
+ 'snippet_id': 'snippet_id',
+ 'note_id': 'id'}
+ _create_attrs = (('name', ), tuple())
+
+
+class ProjectSnippetNote(SaveMixin, ObjectDeleteMixin, RESTObject):
+ _managers = (('awardemojis', 'ProjectSnippetNoteAwardEmojiManager'),)
+
+
class ProjectSnippetNoteManager(CRUDMixin, RESTManager):
_path = '/projects/%(project_id)s/snippets/%(snippet_id)s/notes'
_obj_cls = ProjectSnippetNote
@@ -1852,10 +2098,24 @@ class ProjectSnippetNoteManager(CRUDMixin, RESTManager):
_update_attrs = (('body', ), tuple())
+class ProjectSnippetAwardEmoji(ObjectDeleteMixin, RESTObject):
+ pass
+
+
+class ProjectSnippetAwardEmojiManager(NoUpdateMixin, RESTManager):
+ _path = '/projects/%(project_id)s/snippets/%(snippet_id)s/award_emoji'
+ _obj_cls = ProjectSnippetAwardEmoji
+ _from_parent_attrs = {'project_id': 'project_id', 'snippet_id': 'id'}
+ _create_attrs = (('name', ), tuple())
+
+
class ProjectSnippet(SaveMixin, ObjectDeleteMixin, RESTObject):
_url = '/projects/%(project_id)s/snippets'
_short_print_attr = 'title'
- _managers = (('notes', 'ProjectSnippetNoteManager'), )
+ _managers = (
+ ('awardemojis', 'ProjectSnippetAwardEmojiManager'),
+ ('notes', 'ProjectSnippetNoteManager'),
+ )
@cli.register_custom_action('ProjectSnippet')
@exc.on_http_error(exc.GitlabGetError)
@@ -1895,8 +2155,17 @@ class ProjectSnippetManager(CRUDMixin, RESTManager):
class ProjectTrigger(SaveMixin, ObjectDeleteMixin, RESTObject):
@cli.register_custom_action('ProjectTrigger')
+ @exc.on_http_error(exc.GitlabOwnershipError)
def take_ownership(self, **kwargs):
- """Update the owner of a trigger."""
+ """Update the owner of a trigger.
+
+ Args:
+ **kwargs: Extra options to send to the server (e.g. sudo)
+
+ Raises:
+ GitlabAuthenticationError: If authentication is not correct
+ GitlabOwnershipError: If the request failed
+ """
path = '%s/%s/take_ownership' % (self.manager.path, self.get_id())
server_data = self.manager.gitlab.http_post(path, **kwargs)
self._update_attrs(server_data)
@@ -2094,6 +2363,7 @@ class Project(SaveMixin, ObjectDeleteMixin, RESTObject):
('branches', 'ProjectBranchManager'),
('jobs', 'ProjectJobManager'),
('commits', 'ProjectCommitManager'),
+ ('customattributes', 'ProjectCustomAttributeManager'),
('deployments', 'ProjectDeploymentManager'),
('environments', 'ProjectEnvironmentManager'),
('events', 'ProjectEventManager'),
@@ -2108,9 +2378,10 @@ class Project(SaveMixin, ObjectDeleteMixin, RESTObject):
('milestones', 'ProjectMilestoneManager'),
('notes', 'ProjectNoteManager'),
('notificationsettings', 'ProjectNotificationSettingsManager'),
+ ('pagesdomains', 'ProjectPagesDomainManager'),
('pipelines', 'ProjectPipelineManager'),
('protectedbranches', 'ProjectProtectedBranchManager'),
- ('pipeline_schedules', 'ProjectPipelineSchedulesManager'),
+ ('pipelineschedules', 'ProjectPipelineScheduleManager'),
('runners', 'ProjectRunnerManager'),
('services', 'ProjectServiceManager'),
('snippets', 'ProjectSnippetManager'),
@@ -2129,6 +2400,11 @@ class Project(SaveMixin, ObjectDeleteMixin, RESTObject):
Args:
path (str): Path of the top folder (/ by default)
ref (str): Reference to a commit or branch
+ all (bool): If True, return all the items, without pagination
+ per_page (int): Number of items to retrieve per request
+ page (int): ID of the page to return (starts with page 1)
+ as_list (bool): If set to False and no pagination option is
+ defined, return a generator instead of a list
**kwargs: Extra options to send to the server (e.g. sudo)
Raises:
@@ -2223,6 +2499,11 @@ class Project(SaveMixin, ObjectDeleteMixin, RESTObject):
"""Return a list of contributors for the project.
Args:
+ all (bool): If True, return all the items, without pagination
+ per_page (int): Number of items to retrieve per request
+ page (int): ID of the page to return (starts with page 1)
+ as_list (bool): If set to False and no pagination option is
+ defined, return a generator instead of a list
**kwargs: Extra options to send to the server (e.g. sudo)
Raises:
@@ -2499,7 +2780,8 @@ class ProjectManager(CRUDMixin, RESTManager):
)
_list_filters = ('search', 'owned', 'starred', 'archived', 'visibility',
'order_by', 'sort', 'simple', 'membership', 'statistics',
- 'with_issues_enabled', 'with_merge_requests_enabled')
+ 'with_issues_enabled', 'with_merge_requests_enabled',
+ 'custom_attributes')
class Runner(SaveMixin, ObjectDeleteMixin, RESTObject):