diff options
-rw-r--r-- | docs/switching-to-v4.rst | 10 | ||||
-rw-r--r-- | gitlab/__init__.py | 24 | ||||
-rw-r--r-- | gitlab/mixins.py | 2 | ||||
-rw-r--r-- | gitlab/v4/objects.py | 153 | ||||
-rwxr-xr-x | tools/build_test_env.sh | 2 | ||||
-rwxr-xr-x | tools/py_functional_tests.sh | 2 | ||||
-rw-r--r-- | tools/python_test_v3.py (renamed from tools/python_test.py) | 0 | ||||
-rw-r--r-- | tools/python_test_v4.py | 341 |
8 files changed, 489 insertions, 45 deletions
diff --git a/docs/switching-to-v4.rst b/docs/switching-to-v4.rst index fcec8a8..fb2b978 100644 --- a/docs/switching-to-v4.rst +++ b/docs/switching-to-v4.rst @@ -115,11 +115,17 @@ following important changes in the python API: + :attr:`~gitlab.Gitlab.http_put` + :attr:`~gitlab.Gitlab.http_delete` +* The users ``get_by_username`` method has been removed. It doesn't exist in + the GitLab API. You can use the ``username`` filter attribute when listing to + get a similar behavior: + + .. code-block:: python + + user = list(gl.users.list(username='jdoe'))[0] + Undergoing work =============== -* The ``delete()`` method for objects is not yet available. For now you need to - use ``manager.delete(obj.id)``. * The ``page`` and ``per_page`` arguments for listing don't behave as they used to. Their behavior will be restored. diff --git a/gitlab/__init__.py b/gitlab/__init__.py index 6a55fee..617f50c 100644 --- a/gitlab/__init__.py +++ b/gitlab/__init__.py @@ -645,12 +645,32 @@ class Gitlab(object): Raises: GitlabHttpError: When the return code is not 2xx """ + + def sanitized_url(url): + parsed = six.moves.urllib.parse.urlparse(url) + new_path = parsed.path.replace('.', '%2E') + return parsed._replace(path=new_path).geturl() + url = self._build_url(path) params = query_data.copy() params.update(kwargs) opts = self._get_session_opts(content_type='application/json') - result = self.session.request(verb, url, json=post_data, - params=params, stream=streamed, **opts) + verify = opts.pop('verify') + timeout = opts.pop('timeout') + + # Requests assumes that `.` should not be encoded as %2E and will make + # changes to urls using this encoding. Using a prepped request we can + # get the desired behavior. + # The Requests behavior is right but it seems that web servers don't + # always agree with this decision (this is the case with a default + # gitlab installation) + req = requests.Request(verb, url, json=post_data, params=params, + **opts) + prepped = self.session.prepare_request(req) + prepped.url = sanitized_url(prepped.url) + result = self.session.send(prepped, stream=streamed, verify=verify, + timeout=timeout) + if 200 <= result.status_code < 300: return result diff --git a/gitlab/mixins.py b/gitlab/mixins.py index cc9eb51..5876d58 100644 --- a/gitlab/mixins.py +++ b/gitlab/mixins.py @@ -152,7 +152,7 @@ class CreateMixin(object): **kwargs: Extra options to send to the Gitlab server (e.g. sudo) Returns: - RESTObject: a new instance of the managed object class build with + RESTObject: a new instance of the managed object class built with the data sent by the server Raises: diff --git a/gitlab/v4/objects.py b/gitlab/v4/objects.py index 9de18ee..b94d84a 100644 --- a/gitlab/v4/objects.py +++ b/gitlab/v4/objects.py @@ -126,7 +126,7 @@ class UserKey(ObjectDeleteMixin, RESTObject): class UserKeyManager(GetFromListMixin, CreateMixin, DeleteMixin, RESTManager): - _path = '/users/%(user_id)s/emails' + _path = '/users/%(user_id)s/keys' _obj_cls = UserKey _from_parent_attrs = {'user_id': 'id'} _create_attrs = (('title', 'key'), tuple()) @@ -842,8 +842,8 @@ class ProjectKeyManager(NoUpdateMixin, RESTManager): GitlabAuthenticationError: If authentication is not correct GitlabProjectDeployKeyError: If the key could not be enabled """ - path = '%s/%s/enable' % (self.manager.path, key_id) - self.manager.gitlab.http_post(path, **kwargs) + path = '%s/%s/enable' % (self.path, key_id) + self.gitlab.http_post(path, **kwargs) class ProjectEvent(RESTObject): @@ -999,17 +999,19 @@ class ProjectTag(ObjectDeleteMixin, RESTObject): data = {'description': description} if self.release is None: try: - result = self.manager.gitlab.http_post(path, post_data=data, - **kwargs) + server_data = self.manager.gitlab.http_post(path, + post_data=data, + **kwargs) except exc.GitlabHttpError as e: raise exc.GitlabCreateError(e.response_code, e.error_message) else: try: - result = self.manager.gitlab.http_put(path, post_data=data, - **kwargs) + server_data = self.manager.gitlab.http_put(path, + post_data=data, + **kwargs) except exc.GitlabHttpError as e: raise exc.GitlabUpdateError(e.response_code, e.error_message) - self.release = result.json() + self.release = server_data class ProjectTagManager(GetFromListMixin, CreateMixin, DeleteMixin, @@ -1223,8 +1225,7 @@ class ProjectMilestone(SaveMixin, ObjectDeleteMixin, RESTObject): return RESTObjectList(manager, ProjectMergeRequest, data_list) -class ProjectMilestoneManager(RetrieveMixin, CreateMixin, DeleteMixin, - RESTManager): +class ProjectMilestoneManager(CRUDMixin, RESTManager): _path = '/projects/%(project_id)s/milestones' _obj_cls = ProjectMilestone _from_parent_attrs = {'project_id': 'id'} @@ -1239,6 +1240,26 @@ class ProjectLabel(SubscribableMixin, SaveMixin, ObjectDeleteMixin, RESTObject): _id_attr = 'name' + # Update without ID, but we need an ID to get from list. + @exc.on_http_error(exc.GitlabUpdateError) + def save(self, **kwargs): + """Saves the changes made to the object to the server. + + The object is updated to match what the server returns. + + Args: + **kwargs: Extra options to send to the server (e.g. sudo) + + Raises: + GitlabAuthenticationError: If authentication is not correct. + GitlabUpdateError: If the server cannot perform the request. + """ + updated_data = self._get_updated_data() + + # call the manager + server_data = self.manager.update(None, updated_data, **kwargs) + self._update_attrs(server_data) + class ProjectLabelManager(GetFromListMixin, CreateMixin, UpdateMixin, DeleteMixin, RESTManager): @@ -1262,27 +1283,7 @@ class ProjectLabelManager(GetFromListMixin, CreateMixin, UpdateMixin, GitlabAuthenticationError: If authentication is not correct. GitlabDeleteError: If the server cannot perform the request. """ - self.gitlab.http_delete(path, query_data={'name': self.name}, **kwargs) - - # Update without ID, but we need an ID to get from list. - @exc.on_http_error(exc.GitlabUpdateError) - def save(self, **kwargs): - """Saves the changes made to the object to the server. - - The object is updated to match what the server returns. - - Args: - **kwargs: Extra options to send to the server (e.g. sudo) - - Raises: - GitlabAuthenticationError: If authentication is not correct. - GitlabUpdateError: If the server cannot perform the request. - """ - updated_data = self._get_updated_data() - - # call the manager - server_data = self.manager.update(None, updated_data, **kwargs) - self._update_attrs(server_data) + self.gitlab.http_delete(self.path, query_data={'name': name}, **kwargs) class ProjectFile(SaveMixin, ObjectDeleteMixin, RESTObject): @@ -1297,6 +1298,38 @@ class ProjectFile(SaveMixin, ObjectDeleteMixin, RESTObject): """ return base64.b64decode(self.content) + def save(self, branch, commit_message, **kwargs): + """Save the changes made to the file to the server. + + The object is updated to match what the server returns. + + Args: + branch (str): Branch in which the file will be updated + commit_message (str): Message to send with the commit + **kwargs: Extra options to send to the server (e.g. sudo) + + Raise: + GitlabAuthenticationError: If authentication is not correct + GitlabUpdateError: If the server cannot perform the request + """ + self.branch = branch + self.commit_message = commit_message + super(ProjectFile, self).save(**kwargs) + + def delete(self, branch, commit_message, **kwargs): + """Delete the file from the server. + + Args: + branch (str): Branch from which the file will be removed + commit_message (str): Commit message for the deletion + **kwargs: Extra options to send to the server (e.g. sudo) + + Raises: + GitlabAuthenticationError: If authentication is not correct + GitlabDeleteError: If the server cannot perform the request + """ + self.manager.delete(self.get_id(), branch, commit_message, **kwargs) + class ProjectFileManager(GetMixin, CreateMixin, UpdateMixin, DeleteMixin, RESTManager): @@ -1308,11 +1341,12 @@ class ProjectFileManager(GetMixin, CreateMixin, UpdateMixin, DeleteMixin, _update_attrs = (('file_path', 'branch', 'content', 'commit_message'), ('encoding', 'author_email', 'author_name')) - def get(self, file_path, **kwargs): + def get(self, file_path, ref, **kwargs): """Retrieve a single file. Args: - id (int or str): ID of the object to retrieve + file_path (str): Path of the file to retrieve + ref (str): Name of the branch, tag or commit **kwargs: Extra options to send to the Gitlab server (e.g. sudo) Raises: @@ -1323,7 +1357,49 @@ class ProjectFileManager(GetMixin, CreateMixin, UpdateMixin, DeleteMixin, object: The generated RESTObject """ file_path = file_path.replace('/', '%2F') - return GetMixin.get(self, file_path, **kwargs) + return GetMixin.get(self, file_path, ref=ref, **kwargs) + + @exc.on_http_error(exc.GitlabCreateError) + def create(self, data, **kwargs): + """Create a new object. + + Args: + data (dict): parameters to send to the server to create the + resource + **kwargs: Extra options to send to the Gitlab server (e.g. sudo) + + Returns: + RESTObject: a new instance of the managed object class built with + the data sent by the server + + Raises: + GitlabAuthenticationError: If authentication is not correct + GitlabCreateError: If the server cannot perform the request + """ + + self._check_missing_create_attrs(data) + file_path = data.pop('file_path') + path = '%s/%s' % (self.path, file_path) + server_data = self.gitlab.http_post(path, post_data=data, **kwargs) + return self._obj_cls(self, server_data) + + @exc.on_http_error(exc.GitlabDeleteError) + def delete(self, file_path, branch, commit_message, **kwargs): + """Delete a file on the server. + + Args: + file_path (str): Path of the file to remove + branch (str): Branch from which the file will be removed + commit_message (str): Commit message for the deletion + **kwargs: Extra options to send to the Gitlab server (e.g. sudo) + + Raises: + GitlabAuthenticationError: If authentication is not correct + GitlabDeleteError: If the server cannot perform the request + """ + path = '%s/%s' % (self.path, file_path.replace('/', '%2F')) + data = {'branch': branch, 'commit_message': commit_message} + self.gitlab.http_delete(path, query_data=data, **kwargs) @exc.on_http_error(exc.GitlabGetError) def raw(self, file_path, ref, streamed=False, action=None, chunk_size=1024, @@ -1348,7 +1424,7 @@ class ProjectFileManager(GetMixin, CreateMixin, UpdateMixin, DeleteMixin, Returns: str: The file content """ - file_path = file_path.replace('/', '%2F') + file_path = file_path.replace('/', '%2F').replace('.', '%2E') path = '%s/%s/raw' % (self.path, file_path) query_data = {'ref': ref} result = self.gitlab.http_get(path, query_data=query_data, @@ -1489,8 +1565,8 @@ class ProjectVariableManager(CRUDMixin, RESTManager): _path = '/projects/%(project_id)s/variables' _obj_cls = ProjectVariable _from_parent_attrs = {'project_id': 'id'} - _create_attrs = (('key', 'vaule'), tuple()) - _update_attrs = (('key', 'vaule'), tuple()) + _create_attrs = (('key', 'value'), tuple()) + _update_attrs = (('key', 'value'), tuple()) class ProjectService(GitlabObject): @@ -2016,7 +2092,8 @@ class ProjectManager(CRUDMixin, RESTManager): 'request_access_enabled') ) _list_filters = ('search', 'owned', 'starred', 'archived', 'visibility', - 'order_by', 'sort', 'simple', 'membership', 'statistics') + 'order_by', 'sort', 'simple', 'membership', 'statistics', + 'with_issues_enabled', 'with_merge_requests_enabled') class GroupProject(Project): diff --git a/tools/build_test_env.sh b/tools/build_test_env.sh index 96d341a..35a54c6 100755 --- a/tools/build_test_env.sh +++ b/tools/build_test_env.sh @@ -154,6 +154,6 @@ log "Installing into virtualenv..." try pip install -e . log "Pausing to give GitLab some time to finish starting up..." -sleep 20 +sleep 30 log "Test environment initialized." diff --git a/tools/py_functional_tests.sh b/tools/py_functional_tests.sh index 0d00c5f..75bb761 100755 --- a/tools/py_functional_tests.sh +++ b/tools/py_functional_tests.sh @@ -18,4 +18,4 @@ setenv_script=$(dirname "$0")/build_test_env.sh || exit 1 BUILD_TEST_ENV_AUTO_CLEANUP=true . "$setenv_script" "$@" || exit 1 -try python "$(dirname "$0")"/python_test.py +try python "$(dirname "$0")"/python_test_v${API_VER}.py diff --git a/tools/python_test.py b/tools/python_test_v3.py index 62d6421..62d6421 100644 --- a/tools/python_test.py +++ b/tools/python_test_v3.py diff --git a/tools/python_test_v4.py b/tools/python_test_v4.py new file mode 100644 index 0000000..ec3f0d3 --- /dev/null +++ b/tools/python_test_v4.py @@ -0,0 +1,341 @@ +import base64 +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") + +# login/password authentication +gl = gitlab.Gitlab('http://localhost:8080', email=LOGIN, password=PASSWORD) +gl.auth() +token_from_auth = gl.private_token + +# token authentication from config file +gl = gitlab.Gitlab.from_config(config_files=['/tmp/python-gitlab.cfg']) +assert(token_from_auth == gl.private_token) +gl.auth() +assert(isinstance(gl.user, gitlab.v4.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.list(search='foobar').next().id == foobar_user.id +usercmp = lambda x,y: cmp(x.id, y.id) +expected = sorted([new_user, foobar_user], cmp=usercmp) +actual = sorted(list(gl.users.list(search='foo')), cmp=usercmp) +assert len(expected) == len(actual) +assert len(gl.users.list(search='asdf')) == 0 + +# 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() +assert(len(gl.user.keys.list()) == 0) + +# 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.list(search='group2').next().id +group3 = gl.groups.create({'name': 'group3', 'path': 'group3', 'parent_id': p_id}) + +assert(len(gl.groups.list()) == 3) +assert(len(gl.groups.list(search='1')) == 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.list(owned=True)) == 2) +assert(len(gl.projects.list(search="admin")) == 1) + +# test pagination +# FIXME => we should return lists, not RESTObjectList +#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': '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="master", commit_message="new commit") +readme.delete(commit_message="Removing README", branch="master") + +admin_project.files.create({'file_path': 'README.rst', + 'branch': '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': '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_id = tree[0]['id'] +blob = admin_project.repository_raw_blob(blob_id) +assert(blob == 'Initial content') +archive1 = admin_project.repository_archive() +archive2 = admin_project.repository_archive('master') +assert(archive1 == archive2) + +# deploy keys +deploy_key = admin_project.keys.create({'title': 'foo@bar', 'key': DEPLOY_KEY}) +project_keys = list(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.delete(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().next().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({'description': 'trigger1'}) +assert(len(admin_project.triggers.list()) == 1) +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': 'branch1', + 'ref': 'master'}) +admin_project.files.create({'file_path': 'README2.rst', + 'branch': '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'}) +mr.merge() +admin_project.branches.delete('branch1') + +try: + mr.merge() +except gitlab.GitlabMRClosedError: + pass + +# stars +admin_project.star() +assert(admin_project.star_count == 1) +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(all=True) +assert(len(ns) != 0) +ns = gl.namespaces.list(search='root', all=True)[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(all=True)[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 +# NOT IMPLEMENTED YET +#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(all=True) +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.content() +assert(content == 'import gitlab') +snippet.delete() +assert(len(gl.snippets.list()) == 0) |