@@ -63,24 +63,26 @@ def sync_repositories(self):
63
63
"https://bitbucket.org/api/2.0/repositories/" ,
64
64
role = "admin" ,
65
65
)
66
- admin_repo_relations = RemoteRepositoryRelation .objects .filter (
66
+ RemoteRepositoryRelation .objects .filter (
67
67
user = self .user ,
68
68
account = self .account ,
69
69
remote_repository__vcs_provider = self .vcs_provider_slug ,
70
70
remote_repository__remote_id__in = [r ["uuid" ] for r in resp ],
71
- )
72
- for remote_repository_relation in admin_repo_relations :
73
- remote_repository_relation .admin = True
74
- remote_repository_relation .save ()
71
+ ).update (admin = True )
75
72
except (TypeError , ValueError ):
76
- pass
73
+ log . warning ( "Error syncing Bitbucket admin repositories" )
77
74
78
75
return remote_ids
79
76
80
77
def sync_organizations (self ):
81
- """Sync Bitbucket workspaces(our RemoteOrganization) and workspace repositories."""
78
+ """
79
+ Sync Bitbucket workspaces (organizations).
80
+
81
+ This method only creates the relationships between the
82
+ organizations and the user, as all the repositories
83
+ are already created in the sync_repositories method.
84
+ """
82
85
organization_remote_ids = []
83
- repository_remote_ids = []
84
86
85
87
try :
86
88
workspaces = self .paginate (
@@ -89,18 +91,8 @@ def sync_organizations(self):
89
91
)
90
92
for workspace in workspaces :
91
93
remote_organization = self .create_organization (workspace )
92
- repos = self .paginate (workspace ["links" ]["repositories" ]["href" ])
93
-
94
+ remote_organization .get_remote_organization_relation (self .user , self .account )
94
95
organization_remote_ids .append (remote_organization .remote_id )
95
-
96
- for repo in repos :
97
- remote_repository = self .create_repository (
98
- repo ,
99
- organization = remote_organization ,
100
- )
101
- if remote_repository :
102
- repository_remote_ids .append (remote_repository .remote_id )
103
-
104
96
except ValueError :
105
97
log .warning ("Error syncing Bitbucket organizations" )
106
98
raise SyncServiceError (
@@ -109,9 +101,9 @@ def sync_organizations(self):
109
101
)
110
102
)
111
103
112
- return organization_remote_ids , repository_remote_ids
104
+ return organization_remote_ids , []
113
105
114
- def create_repository (self , fields , privacy = None , organization = None ):
106
+ def create_repository (self , fields , privacy = None ):
115
107
"""
116
108
Update or create a repository from Bitbucket API response.
117
109
@@ -136,42 +128,15 @@ def create_repository(self, fields, privacy=None, organization=None):
136
128
repo , _ = RemoteRepository .objects .get_or_create (
137
129
remote_id = fields ["uuid" ], vcs_provider = self .vcs_provider_slug
138
130
)
139
- repo . get_remote_repository_relation ( self .user , self . account )
131
+ self ._update_repository_from_fields ( repo , fields )
140
132
141
- if repo .organization and repo .organization != organization :
142
- log .debug (
143
- "Not importing repository because mismatched orgs." ,
144
- repository = fields ["name" ],
145
- )
146
- return None
147
-
148
- repo .organization = organization
149
- repo .name = fields ["name" ]
150
- repo .full_name = fields ["full_name" ]
151
- repo .description = fields ["description" ]
152
- repo .private = fields ["is_private" ]
153
-
154
- # Default to HTTPS, use SSH for private repositories
155
- clone_urls = {u ["name" ]: u ["href" ] for u in fields ["links" ]["clone" ]}
156
- repo .clone_url = self .https_url_pattern .sub (
157
- "https://bitbucket.org/" ,
158
- clone_urls .get ("https" ),
133
+ # The repositories API doesn't return the admin status of the user,
134
+ # so we default to False, and then update it later using another API call.
135
+ remote_repository_relation = repo .get_remote_repository_relation (
136
+ self .user , self .account
159
137
)
160
- repo .ssh_url = clone_urls .get ("ssh" )
161
- if repo .private :
162
- repo .clone_url = repo .ssh_url
163
-
164
- repo .html_url = fields ["links" ]["html" ]["href" ]
165
- repo .vcs = fields ["scm" ]
166
- mainbranch = fields .get ("mainbranch" ) or {}
167
- repo .default_branch = mainbranch .get ("name" )
168
-
169
- avatar_url = fields ["links" ]["avatar" ]["href" ] or ""
170
- repo .avatar_url = re .sub (r"\/16\/$" , r"/32/" , avatar_url )
171
- if not repo .avatar_url :
172
- repo .avatar_url = self .default_user_avatar_url
173
-
174
- repo .save ()
138
+ remote_repository_relation .admin = False
139
+ remote_repository_relation .save ()
175
140
176
141
return repo
177
142
@@ -180,17 +145,59 @@ def create_repository(self, fields, privacy=None, organization=None):
180
145
repository = fields ["name" ],
181
146
)
182
147
148
+ def _update_repository_from_fields (self , repo , fields ):
149
+ # All repositories are created under a workspace,
150
+ # which we consider an organization.
151
+ organization = self .create_organization (fields ["workspace" ])
152
+ repo .organization = organization
153
+ repo .name = fields ["name" ]
154
+ repo .full_name = fields ["full_name" ]
155
+ repo .description = fields ["description" ]
156
+ repo .private = fields ["is_private" ]
157
+
158
+ # Default to HTTPS, use SSH for private repositories
159
+ clone_urls = {u ["name" ]: u ["href" ] for u in fields ["links" ]["clone" ]}
160
+ repo .clone_url = self .https_url_pattern .sub (
161
+ "https://bitbucket.org/" ,
162
+ clone_urls .get ("https" ),
163
+ )
164
+ repo .ssh_url = clone_urls .get ("ssh" )
165
+ if repo .private :
166
+ repo .clone_url = repo .ssh_url
167
+
168
+ repo .html_url = fields ["links" ]["html" ]["href" ]
169
+ repo .vcs = fields ["scm" ]
170
+ mainbranch = fields .get ("mainbranch" ) or {}
171
+ repo .default_branch = mainbranch .get ("name" )
172
+
173
+ avatar_url = fields ["links" ]["avatar" ]["href" ] or ""
174
+ repo .avatar_url = re .sub (r"\/16\/$" , r"/32/" , avatar_url )
175
+ if not repo .avatar_url :
176
+ repo .avatar_url = self .default_user_avatar_url
177
+
178
+ repo .save ()
179
+
183
180
def create_organization (self , fields ):
184
181
"""
185
182
Update or create remote organization from Bitbucket API response.
186
183
187
184
:param fields: dictionary response of data from API
188
185
:rtype: RemoteOrganization
186
+
187
+ .. note::
188
+
189
+ This method caches organizations by their remote ID to avoid
190
+ unnecessary database queries, specially when creating
191
+ multiple repositories that belong to the same organization.
189
192
"""
193
+ organization_id = fields ["uuid" ]
194
+ if organization_id in self ._organizations_cache :
195
+ return self ._organizations_cache [organization_id ]
196
+
190
197
organization , _ = RemoteOrganization .objects .get_or_create (
191
- remote_id = fields ["uuid" ], vcs_provider = self .vcs_provider_slug
198
+ remote_id = organization_id ,
199
+ vcs_provider = self .vcs_provider_slug ,
192
200
)
193
- organization .get_remote_organization_relation (self .user , self .account )
194
201
195
202
organization .slug = fields .get ("slug" )
196
203
organization .name = fields .get ("name" )
@@ -201,6 +208,7 @@ def create_organization(self, fields):
201
208
202
209
organization .save ()
203
210
211
+ self ._organizations_cache [organization_id ] = organization
204
212
return organization
205
213
206
214
def get_next_url_to_paginate (self , response ):
0 commit comments