From ea4c099532993cdb3ea547fcbd931127c03fdffa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mika=20M=C3=A4enp=C3=A4=C3=A4?= Date: Wed, 15 Oct 2014 15:56:28 +0300 Subject: [PATCH] Moved url attributes to separate list. Added list for delete attributes. --- gitlab.py | 177 +++++++++++++++++++++++++++++++----------------------- 1 file changed, 102 insertions(+), 75 deletions(-) diff --git a/gitlab.py b/gitlab.py index 43ba54397..7acd728e2 100644 --- a/gitlab.py +++ b/gitlab.py @@ -19,6 +19,8 @@ import requests import sys +from itertools import chain + __title__ = 'python-gitlab' __version__ = '0.7' __author__ = 'Gauvain Pocentek' @@ -197,7 +199,8 @@ def rawDelete(self, path): def list(self, obj_class, **kwargs): missing = [] - for k in obj_class.requiredListAttrs: + for k in chain(obj_class.requiredUrlAttrs, + obj_class.requiredListAttrs): if k not in kwargs: missing.append(k) if missing: @@ -205,13 +208,16 @@ def list(self, obj_class, **kwargs): ", ".join(missing)) url = self.constructUrl(id_=None, obj=obj_class, parameters=kwargs) - args = _sanitize_dict(kwargs) - if args: - url += "?%s" % ("&".join( - ["%s=%s" % (k, v) for k, v in args.items()])) + + # Remove attributes that are used in url so that there is only + # url-parameters left + params = kwargs.copy() + for attribute in obj_class.requiredUrlAttrs: + del params[attribute] try: - r = requests.get(url, headers=self.headers, verify=self.ssl_verify, + r = requests.get(url, params=kwargs, headers=self.headers, + verify=self.ssl_verify, timeout=self.timeout) except: raise GitlabConnectionError( @@ -227,7 +233,7 @@ def list(self, obj_class, **kwargs): for key in ['page', 'per_page']: if key in cls_kwargs: del cls_kwargs[key] - + return [cls(self, item, **cls_kwargs) for item in r.json() if item is not None] elif r.status_code == 401: raise GitlabAuthenticationError(r.json()['message']) @@ -236,7 +242,8 @@ def list(self, obj_class, **kwargs): def get(self, obj_class, id=None, **kwargs): missing = [] - for k in obj_class.requiredGetAttrs: + for k in chain(obj_class.requiredUrlAttrs, + obj_class.requiredGetAttrs): if k not in kwargs: missing.append(k) if missing: @@ -245,9 +252,15 @@ def get(self, obj_class, id=None, **kwargs): url = self.constructUrl(id_=id, obj=obj_class, parameters=kwargs) + # Remove attributes that are used in url so that there is only + # url-parameters left + params = kwargs.copy() + for attribute in obj_class.requiredUrlAttrs: + del params[attribute] + try: - r = requests.get(url, headers=self.headers, verify=self.ssl_verify, - timeout=self.timeout) + r = requests.get(url, params=params, headers=self.headers, + verify=self.ssl_verify, timeout=self.timeout) except: raise GitlabConnectionError( "Can't connect to GitLab server (%s)" % self._url) @@ -262,10 +275,25 @@ def get(self, obj_class, id=None, **kwargs): raise GitlabGetError('%d: %s' % (r.status_code, r.text)) def delete(self, obj): - url = self.constructUrl(id_=obj.id, obj=obj, parameters=obj.__dict__) + params = obj.__dict__.copy() + missing = [] + for k in chain(obj.requiredUrlAttrs, obj.requiredDeleteAttrs): + if k not in params: + missing.append(k) + if missing: + raise GitlabDeleteError('Missing attribute(s): %s' % + ", ".join(missing)) + + url = self.constructUrl(id_=obj.id, obj=obj, parameters=params) + + # Remove attributes that are used in url so that there is only + # url-parameters left + for attribute in obj.requiredUrlAttrs: + del params[attribute] try: r = requests.delete(url, + params=params, headers=self.headers, verify=self.ssl_verify, timeout=self.timeout) @@ -283,7 +311,7 @@ def delete(self, obj): def create(self, obj): missing = [] - for k in obj.requiredCreateAttrs: + for k in chain(obj.requiredUrlAttrs, obj.requiredCreateAttrs): if k not in obj.__dict__: missing.append(k) if missing: @@ -313,8 +341,14 @@ def create(self, obj): raise GitlabCreateError('%d: %s' % (r.status_code, r.text)) def update(self, obj): + missing = [] + for k in chain(obj.requiredUrlAttrs, obj.requiredCreateAttrs): + if k not in obj.__dict__: + missing.append(k) + if missing: + raise GitlabUpdateError('Missing attribute(s): %s' % + ", ".join(missing)) url = self.constructUrl(id_=obj.id, obj=obj, parameters=obj.__dict__) - # build a dict of data that can really be sent to server d = {} for k, v in obj.__dict__.items(): @@ -491,8 +525,10 @@ class GitlabObject(object): canCreate = True canUpdate = True canDelete = True + requiredUrlAttrs = [] requiredListAttrs = [] requiredGetAttrs = [] + requiredDeleteAttrs = [] requiredCreateAttrs = [] optionalCreateAttrs = [] idAttr = 'id' @@ -642,19 +678,20 @@ def json(self): class UserKey(GitlabObject): _url = '/users/%(user_id)s/keys' canGet = False - canList = True canUpdate = False - canDelete = True - requiredCreateAttrs = ['user_id', 'title', 'key'] + requiredUrlAttrs = ['user_id'] + requiredCreateAttrs = ['title', 'key'] class User(GitlabObject): _url = '/users' shortPrintAttr = 'username' - requiredCreateAttrs = ['email', 'password', 'username', 'name'] - optionalCreateAttrs = ['skype', 'linkedin', 'twitter', 'projects_limit', - 'extern_uid', 'provider', 'bio', 'admin', - 'can_create_group'] + # FIXME: password is required for create but not for update + requiredCreateAttrs = ['email', 'username', 'name'] + optionalCreateAttrs = ['password', 'skype', 'linkedin', 'twitter', + 'projects_limit', 'extern_uid', 'provider', + 'bio', 'admin', 'can_create_group', 'website_url'] + def Key(self, id=None, **kwargs): return self._getListOrObject(UserKey, id, @@ -684,13 +721,14 @@ class GroupMember(GitlabObject): _url = '/groups/%(group_id)s/members' canGet = False canUpdate = False - requiredCreateAttrs = ['group_id', 'user_id', 'access_level'] - requiredDeleteAttrs = ['group_id', 'user_id'] + requiredUrlAttrs = ['group_id'] + requiredCreateAttrs = ['access_level', 'user_id'] shortPrintAttr = 'username' class Group(GitlabObject): _url = '/groups' + canUpdate = False _constructorTypes = {'projects': 'Project'} requiredCreateAttrs = ['name', 'path'] shortPrintAttr = 'name' @@ -733,12 +771,12 @@ class Issue(GitlabObject): class ProjectBranch(GitlabObject): _url = '/projects/%(project_id)s/repository/branches' + _constructorTypes = {'author': 'User', "committer": "User"} + idAttr = 'name' canUpdate = False - requiredGetAttrs = ['project_id'] - requiredListAttrs = ['project_id'] - requiredCreateAttrs = ['project_id', 'branch_name', 'ref'] - requiredDeleteAttrs = ['project_id'] + requiredUrlAttrs = ['project_id'] + requiredCreateAttrs = ['branch_name', 'ref'] _constructorTypes = {'commit': 'ProjectCommit'} def protect(self, protect=True): @@ -764,7 +802,7 @@ class ProjectCommit(GitlabObject): canDelete = False canUpdate = False canCreate = False - requiredListAttrs = ['project_id'] + requiredUrlAttrs = ['project_id'] shortPrintAttr = 'title' def diff(self): @@ -790,9 +828,8 @@ def blob(self, filepath): class ProjectKey(GitlabObject): _url = '/projects/%(project_id)s/keys' canUpdate = False - requiredListAttrs = ['project_id'] - requiredGetAttrs = ['project_id'] - requiredCreateAttrs = ['project_id', 'title', 'key'] + requiredUrlAttrs = ['project_id'] + requiredCreateAttrs = ['title', 'key'] class ProjectEvent(GitlabObject): @@ -801,15 +838,16 @@ class ProjectEvent(GitlabObject): canDelete = False canUpdate = False canCreate = False - requiredListAttrs = ['project_id'] + requiredUrlAttrs = ['project_id'] shortPrintAttr = 'target_title' class ProjectHook(GitlabObject): _url = '/projects/%(project_id)s/hooks' - requiredListAttrs = ['project_id'] - requiredGetAttrs = ['project_id'] - requiredCreateAttrs = ['project_id', 'url'] + requiredUrlAttrs = ['project_id'] + requiredCreateAttrs = ['url'] + optionalCreateAttrs = ['push_events', 'issues_events', + 'merge_requests_events', 'tag_push_events'] shortPrintAttr = 'url' @@ -818,9 +856,8 @@ class ProjectIssueNote(GitlabObject): _constructorTypes = {'author': 'User'} canUpdate = False canDelete = False - requiredListAttrs = ['project_id', 'issue_id'] - requiredGetAttrs = ['project_id', 'issue_id'] - requiredCreateAttrs = ['project_id', 'body'] + requiredUrlAttrs = ['project_id', 'issue_id'] + requiredCreateAttrs = ['body'] class ProjectIssue(GitlabObject): @@ -828,11 +865,11 @@ class ProjectIssue(GitlabObject): _constructorTypes = {'author': 'User', 'assignee': 'User', 'milestone': 'ProjectMilestone'} canDelete = False - requiredListAttrs = ['project_id'] - requiredGetAttrs = ['project_id'] - requiredCreateAttrs = ['project_id', 'title'] + requiredUrlAttrs = ['project_id'] + requiredCreateAttrs = ['title'] optionalCreateAttrs = ['description', 'assignee_id', 'milestone_id', 'labels'] + shortPrintAttr = 'title' def Note(self, id=None, **kwargs): @@ -844,9 +881,8 @@ def Note(self, id=None, **kwargs): class ProjectMember(GitlabObject): _url = '/projects/%(project_id)s/members' - requiredListAttrs = ['project_id'] - requiredGetAttrs = ['project_id'] - requiredCreateAttrs = ['project_id', 'user_id', 'access_level'] + requiredUrlAttrs = ['project_id'] + requiredCreateAttrs = ['access_level', 'user_id'] shortPrintAttr = 'username' @@ -855,9 +891,8 @@ class ProjectNote(GitlabObject): _constructorTypes = {'author': 'User'} canUpdate = False canDelete = False - requiredListAttrs = ['project_id'] - requiredGetAttrs = ['project_id'] - requiredCreateAttrs = ['project_id', 'body'] + requiredUrlAttrs = ['project_id'] + requiredCreateAttrs = ['body'] class ProjectTag(GitlabObject): @@ -866,29 +901,27 @@ class ProjectTag(GitlabObject): canGet = False canDelete = False canUpdate = False - canCreate = False - requiredListAttrs = ['project_id'] + requiredUrlAttrs = ['project_id'] + requiredCreateAttrs = ['tag_name', 'ref'] + optionalCreateattrs = ['message'] shortPrintAttr = 'name' class ProjectMergeRequestNote(GitlabObject): _url = '/projects/%(project_id)s/merge_requests/%(merge_request_id)s/notes' _constructorTypes = {'author': 'User'} - canGet = False - canCreate = False canUpdate = False canDelete = False - requiredListAttrs = ['project_id', 'merge_request_id'] + requiredUrlAttrs = ['project_id', 'merge_request_id'] + requiredCreateAttrs = ['body'] class ProjectMergeRequest(GitlabObject): _url = '/projects/%(project_id)s/merge_requests' _constructorTypes = {'author': 'User', 'assignee': 'User'} canDelete = False - requiredListAttrs = ['project_id'] - requiredGetAttrs = ['project_id'] - requiredCreateAttrs = ['project_id', 'source_branch', - 'target_branch', 'title'] + requiredUrlAttrs = ['project_id'] + requiredCreateAttrs = ['source_branch', 'target_branch', 'title'] optionalCreateAttrs = ['assignee_id'] def Note(self, id=None, **kwargs): @@ -901,9 +934,8 @@ def Note(self, id=None, **kwargs): class ProjectMilestone(GitlabObject): _url = '/projects/%(project_id)s/milestones' canDelete = False - requiredListAttrs = ['project_id'] - requiredGetAttrs = ['project_id'] - requiredCreateAttrs = ['project_id', 'title'] + requiredUrlAttrs = ['project_id'] + requiredCreateAttrs = ['title'] optionalCreateAttrs = ['description', 'due_date', 'state_event'] shortPrintAttr = 'title' @@ -913,17 +945,15 @@ class ProjectSnippetNote(GitlabObject): _constructorTypes = {'author': 'User'} canUpdate = False canDelete = False - requiredListAttrs = ['project_id', 'snippet_id'] - requiredGetAttrs = ['project_id', 'snippet_id'] - requiredCreateAttrs = ['project_id', 'snippet_id', 'body'] + requiredUrlAttrs = ['project_id', 'snippet_id'] + requiredCreateAttrs = ['body'] class ProjectSnippet(GitlabObject): _url = '/projects/%(project_id)s/snippets' _constructorTypes = {'author': 'User'} - requiredListAttrs = ['project_id'] - requiredGetAttrs = ['project_id'] - requiredCreateAttrs = ['project_id', 'title', 'file_name', 'code'] + requiredUrlAttrs = ['project_id'] + requiredCreateAttrs = ['title', 'file_name', 'code'] optionalCreateAttrs = ['lifetime'] shortPrintAttr = 'title' @@ -951,7 +981,8 @@ class UserProject(GitlabObject): canDelete = False canList = False canGet = False - requiredCreateAttrs = ['name', 'user_id'] + requiredUrlAttrs = ['user_id'] + requiredCreateAttrs = ['name'] optionalCreateAttrs = ['default_branch', 'issues_enabled', 'wall_enabled', 'merge_requests_enabled', 'wiki_enabled', 'snippets_enabled', 'public', 'visibility_level', @@ -962,12 +993,12 @@ class Project(GitlabObject): _url = '/projects' _constructorTypes = {'owner': 'User', 'namespace': 'Group'} canUpdate = False - canDelete = False requiredCreateAttrs = ['name'] optionalCreateAttrs = ['default_branch', 'issues_enabled', 'wall_enabled', 'merge_requests_enabled', 'wiki_enabled', 'snippets_enabled', 'public', 'visibility_level', - 'namespace_id', 'description'] + 'namespace_id', 'description', 'path', 'import_url'] + shortPrintAttr = 'path' def Branch(self, id=None, **kwargs): @@ -1086,10 +1117,8 @@ def delete_file(self, path, branch, message): class TeamMember(GitlabObject): _url = '/user_teams/%(team_id)s/members' canUpdate = False - requiredCreateAttrs = ['team_id', 'user_id', 'access_level'] - requiredDeleteAttrs = ['team_id'] - requiredGetAttrs = ['team_id'] - requiredListAttrs = ['team_id'] + requiredUrlAttrs = ['teamd_id'] + requiredCreateAttrs = ['access_level'] shortPrintAttr = 'username' @@ -1097,10 +1126,8 @@ class TeamProject(GitlabObject): _url = '/user_teams/%(team_id)s/projects' _constructorTypes = {'owner': 'User', 'namespace': 'Group'} canUpdate = False - requiredCreateAttrs = ['team_id', 'project_id', 'greatest_access_level'] - requiredDeleteAttrs = ['team_id', 'project_id'] - requiredGetAttrs = ['team_id'] - requiredListAttrs = ['team_id'] + requiredCreateAttrs = ['greatest_access_level'] + requiredUrlAttrs = ['team_id'] shortPrintAttr = 'name'