6
6
from ..service import register_target , RepositoryService
7
7
from ...exceptions import ResourceError , ResourceExistsError , ResourceNotFoundError
8
8
9
- import bitbucket .bitbucket as bitbucket
10
- from requests import Request , Session
11
- import json
9
+ from pybitbucket .bitbucket import Client
10
+ from pybitbucket .auth import BasicAuthenticator
11
+ from pybitbucket .user import User
12
+ from pybitbucket .repository import Repository , RepositoryPayload , RepositoryForkPolicy
13
+ try :
14
+ from pybitbucket .repository import RepositoryForkPayload
15
+ except ImportError :
16
+ RepositoryForkPayload = None
12
17
13
- '''
14
- Extension of the bitbucket module implementation to add support for the extra
15
- features the original implementation lacked. This is a temporary measure, up
16
- until a PR is crafted for the original code.
17
- '''
18
-
19
- bitbucket .URLS .update ({
20
- 'GET_REPO' : 'repositories/%(username)s/%(repo_slug)s/' ,
21
- 'DELETE_REPO' : 'repositories/%(accountname)s/%(repo_slug)s' ,
22
- 'FORK_REPO' : 'repositories/%(username)s/%(repo_slug)s/fork' ,
23
- })
24
-
25
- class Bitbucket (bitbucket .Bitbucket ):
26
- def __init__ (self , * args , ** kwarg ):
27
- super (Bitbucket , self ).__init__ (self )
28
- self .session = Session ()
29
- # XXX monkey patching of requests within bitbucket module
30
- self .requests = self .session
31
-
32
- def get (self , user = None , repo_slug = None ):
33
- """ Get a single repository on Bitbucket and return it."""
34
- username = user or self .username
35
- repo_slug = repo_slug or self .repo_slug or ''
36
- url = self .url ('GET_REPO' , username = username , repo_slug = repo_slug )
37
- return self .dispatch ('GET' , url , auth = self .auth )
38
-
39
- def delete (self , user , repo_slug ):
40
- url = self .url ('DELETE_REPO' , username = user , accountname = user , repo_slug = repo_slug )
41
- return self .dispatch ('DELETE' , url , auth = self .auth )
42
-
43
- def fork (self , user , repo_slug , new_name = None ):
44
- url = self .url ('FORK_REPO' , username = user , repo_slug = repo_slug )
45
- new_repo = new_name or repo_slug
46
- return self .dispatch ('POST' , url , name = new_repo , auth = self .auth )
47
-
48
- def dispatch (self , method , url , auth = None , params = None , ** kwargs ):
49
- """ Send HTTP request, with given method,
50
- credentials and data to the given URL,
51
- and return the success and the result on success.
52
- """
53
- r = Request (
54
- method = method ,
55
- url = url ,
56
- auth = auth ,
57
- params = params ,
58
- data = kwargs )
59
- resp = self .session .send (r .prepare ())
60
- status = resp .status_code
61
- text = resp .text
62
- error = resp .reason
63
- if status >= 200 and status < 300 :
64
- if text :
65
- try :
66
- return (True , json .loads (text ))
67
- except TypeError :
68
- pass
69
- except ValueError :
70
- pass
71
- return (True , text )
72
- elif status >= 300 and status < 400 :
73
- return (
74
- False ,
75
- 'Unauthorized access, '
76
- 'please check your credentials.' )
77
- elif status == 404 :
78
- return (False , dict (message = 'Service not found' , reason = error , code = status ))
79
- elif status == 400 :
80
- return (False , dict (message = 'Bad request sent to server.' , reason = error , code = status ))
81
- elif status == 401 :
82
- return (False , dict (message = 'Not enough privileges.' , reason = error , code = status ))
83
- elif status == 403 :
84
- return (False , dict (message = 'Not authorized.' , reason = error , code = status ))
85
- elif status == 402 or status >= 405 :
86
- return (False , dict (message = 'Request error.' , reason = error , code = status ))
87
- elif status >= 500 and status < 600 :
88
- return (False , dict (message = 'Server error.' , reason = error , code = status ))
89
- else :
90
- return (False , dict (message = 'Unidentified error.' , reason = error , code = status ))
91
18
19
+ from requests import Request , Session
20
+ from requests .exceptions import HTTPError
21
+ import json
92
22
93
23
@register_target ('bb' , 'bitbucket' )
94
24
class BitbucketService (RepositoryService ):
95
25
fqdn = 'bitbucket.org'
96
26
97
27
def __init__ (self , * args , ** kwarg ):
98
- self .bb = Bitbucket ()
28
+ self .bb = Client ()
99
29
super (BitbucketService , self ).__init__ (* args , ** kwarg )
100
30
101
31
def connect (self ):
102
32
if not self ._privatekey :
103
33
raise ConnectionError ('Could not connect to BitBucket. Please configure .gitconfig with your bitbucket credentials.' )
104
34
if not ':' in self ._privatekey :
105
35
raise ConnectionError ('Could not connect to BitBucket. Please setup your private key with login:password' )
106
- self .bb .username , self .bb .password = self ._privatekey .split (':' )
107
- self .username = self .bb .username
108
- result , _ = self .bb .get_user ()
109
- if not result :
110
- raise ConnectionError ('Could not connect to BitBucket. Not authorized, wrong credentials.' )
36
+ self .bb .config = BasicAuthenticator (* self ._privatekey .split (':' )+ ['z+git-repo+pub@m0g.net' ])
37
+ self .bb .session = self .bb .config .session
38
+ try :
39
+ User .find_current_user (client = self .bb )
40
+ except HTTPError as err :
41
+ raise ConnectionError ('Could not connect to BitBucket. Not authorized, wrong credentials.' ) from err
111
42
112
43
def create (self , user , repo , add = False ):
113
- success , result = self .bb .repository .create (repo , scm = 'git' , public = True )
114
- if not success and result ['code' ] == 400 :
115
- raise ResourceExistsError ('Project {} already exists on this account.' .format (repo ))
116
- elif not success :
117
- raise ResourceError ("Couldn't complete creation: {message} (error #{code}: {reason})" .format (** result ))
118
- if add :
119
- self .add (user = user , repo = repo , tracking = self .name )
44
+ try :
45
+ repo = Repository .create (
46
+ payload = RepositoryPayload (dict (
47
+ fork_policy = RepositoryForkPolicy .ALLOW_FORKS ,
48
+ is_private = False )),
49
+ repository_name = repo ,
50
+ owner = user ,
51
+ client = self .bb
52
+ )
53
+ if add :
54
+ self .add (user = user , repo = repo , tracking = self .name )
55
+ except HTTPError as err :
56
+ if err .response .status_code == 400 :
57
+ raise ResourceExistsError ('Project {} already exists on this account.' .format (repo )) from err
58
+ raise ResourceError ("Couldn't complete creation: {}" .format (err )) from err
120
59
121
60
def fork (self , user , repo ):
122
- success , result = self .bb .fork (user , repo )
123
- if not success :
124
- raise ResourceError ("Couldn't complete fork: {message} (error #{code}: {reason})" .format (** result ))
125
- return '/' .join ([result ['owner' ], result ['slug' ]])
61
+ if not RepositoryForkPayload :
62
+ raise NotImplementedError ('Feature not yet supported by the underlying library.' )
63
+ try :
64
+ result = Repository .find_repository_by_name_and_owner (repo , owner = user , client = self .bb ).fork (
65
+ payload = RepositoryForkPayload (dict (
66
+ name = repo ,
67
+ fork_policy = RepositoryForkPolicy .ALLOW_FORKS ,
68
+ is_private = False
69
+ )),
70
+ repository_name = repo ,
71
+ owner = user ,
72
+ client = self .bb
73
+ )
74
+ except HTTPError as err :
75
+ raise ResourceError ("Couldn't complete creation: {}" .format (err )) from err
76
+ return '/' .join ([result .owner , result .slug ])
126
77
127
78
def delete (self , repo , user = None ):
128
79
if not user :
129
80
user = self .user
130
- success , result = self .bb .delete (user , repo )
131
- if not success and result ['code' ] == 404 :
132
- raise ResourceNotFoundError ("Cannot delete: repository {}/{} does not exists." .format (user , repo ))
133
- elif not success :
134
- raise ResourceError ("Couldn't complete deletion: {message} (error #{code}: {reason})" .format (** result ))
81
+ try :
82
+ Repository .find_repository_by_name_and_owner (repo , owner = user , client = self .bb ).delete ()
83
+ except HTTPError as err :
84
+ if err .response .status_code == 404 :
85
+ raise ResourceNotFoundError ("Cannot delete: repository {}/{} does not exists." .format (user , repo )) from err
86
+ raise ResourceError ("Couldn't complete creation: {}" .format (err )) from err
135
87
136
88
def get_repository (self , user , repo ):
137
- if user != self .user :
138
- result , repo_list = self .bb .repository .public (user )
139
- else :
140
- result , repo_list = self .bb .repository .all ()
141
- if not result :
142
- raise ResourceError ("Couldn't list repositories: {message} (error #{code}: {reason})" .format (** repo_list ))
143
- for r in repo_list :
144
- if r ['name' ] == repo :
145
- return r
146
- #raise ResourceNotFoundError('Cannot retrieve repository: {}/{} does not exists.'.format(user, repo))
89
+ try :
90
+ return Repository .find_repository_by_name_and_owner (repo , owner = user , client = self .bb )
91
+ except HTTPError as err :
92
+ raise ResourceNotFoundError ('Cannot retrieve repository: {}/{} does not exists.' .format (user , repo ))
147
93
148
94
@classmethod
149
95
def get_auth_token (cls , login , password , prompt = None ):
@@ -152,9 +98,9 @@ def get_auth_token(cls, login, password, prompt=None):
152
98
153
99
@property
154
100
def user (self ):
155
- ret , user = self . bb . get_user ()
156
- if ret :
157
- return user [ 'username' ]
158
- raise ResourceError ("Could not retrieve username : {message} (error #{code}: {reason} " .format (** result ))
101
+ try :
102
+ return User . find_current_user ( client = self . bb ). username
103
+ except HTTPError as err :
104
+ raise ResourceError ("Couldn't complete creation : {} " .format (err )) from err
159
105
160
106
0 commit comments