Skip to content

Commit 69df3d8

Browse files
authored
Merge pull request #7675 from saadmk11/update-oauth-services
2 parents 2df6a4e + bf8a167 commit 69df3d8

14 files changed

+235
-130
lines changed

readthedocs/api/v2/serializers.py

+1
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,7 @@ class RemoteRepositorySerializer(serializers.ModelSerializer):
186186

187187
class Meta:
188188
model = RemoteRepository
189+
# TODO: Remove json field after it is removed from RemoteRepository
189190
exclude = ('json', 'users')
190191

191192
def get_matches(self, obj):

readthedocs/api/v2/views/model_views.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -397,10 +397,10 @@ def get_queryset(self):
397397
)
398398

399399
query = query.filter(
400-
account__provider__in=[
400+
remote_repository_relations__account__provider__in=[
401401
service.adapter.provider_id for service in registry
402402
],
403-
)
403+
).distinct()
404404

405405
# optimizes for the RemoteOrganizationSerializer
406406
query = query.select_related('organization')

readthedocs/oauth/admin.py

+6-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,11 @@
44

55
from django.contrib import admin
66

7-
from .models import RemoteOrganization, RemoteRepository
7+
from .models import (
8+
RemoteOrganization,
9+
RemoteRepository,
10+
RemoteRepositoryRelation,
11+
)
812

913

1014
class RemoteRepositoryAdmin(admin.ModelAdmin):
@@ -22,4 +26,5 @@ class RemoteOrganizationAdmin(admin.ModelAdmin):
2226

2327

2428
admin.site.register(RemoteRepository, RemoteRepositoryAdmin)
29+
admin.site.register(RemoteRepositoryRelation)
2530
admin.site.register(RemoteOrganization, RemoteOrganizationAdmin)

readthedocs/oauth/migrations/0011_add_remote_relation_model.py renamed to readthedocs/oauth/migrations/0012_add_remote_relation_model.py

+9-9
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ class Migration(migrations.Migration):
1212

1313
dependencies = [
1414
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
15-
('oauth', '0010_index_full_name'),
15+
('oauth', '0011_add_default_branch'),
1616
]
1717

1818
operations = [
@@ -40,15 +40,15 @@ class Migration(migrations.Migration):
4040
'user',
4141
models.ForeignKey(
4242
on_delete=django.db.models.deletion.CASCADE,
43-
related_name='remote_relations',
43+
related_name='remote_repository_relations',
4444
to=settings.AUTH_USER_MODEL
4545
),
4646
),
4747
(
4848
'remoterepository',
4949
models.ForeignKey(
5050
on_delete=django.db.models.deletion.CASCADE,
51-
related_name='remote_relations',
51+
related_name='remote_repository_relations',
5252
to='oauth.RemoteRepository'
5353
),
5454
),
@@ -64,10 +64,6 @@ class Migration(migrations.Migration):
6464
verbose_name='Users'
6565
),
6666
),
67-
migrations.AlterUniqueTogether(
68-
name='remoterepositoryrelation',
69-
unique_together={('remoterepository', 'user')},
70-
),
7167
],
7268
),
7369
migrations.AddField(
@@ -76,12 +72,16 @@ class Migration(migrations.Migration):
7672
field=models.ForeignKey(
7773
blank=True,
7874
null=True,
79-
on_delete=django.db.models.deletion.SET_NULL,
80-
related_name='remote_relations',
75+
on_delete=django.db.models.deletion.CASCADE,
76+
related_name='remote_repository_relations',
8177
to='socialaccount.SocialAccount',
8278
verbose_name='Connected account'
8379
),
8480
),
81+
migrations.AlterUniqueTogether(
82+
name='remoterepositoryrelation',
83+
unique_together={('remoterepository', 'account')},
84+
),
8585
migrations.AddField(
8686
model_name='remoterepositoryrelation',
8787
name='admin',

readthedocs/oauth/migrations/0012_data_migration_for_remote_relation_model.py renamed to readthedocs/oauth/migrations/0013_data_migration_for_remote_relation_model.py

+7-6
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,10 @@
1010
log = logging.getLogger(__name__)
1111

1212

13-
def move_data_to_remote_relations(apps, schema_editor):
13+
def move_data_to_remote_repository_relations(apps, schema_editor):
1414
RemoteRepositoryRelation = apps.get_model('oauth', 'RemoteRepositoryRelation')
1515

16-
def remote_relations_generator(relations, batch_size):
16+
def remote_repository_relations_generator(relations, batch_size):
1717
for relation in relations.iterator(chunk_size=batch_size):
1818
relation.account_id = relation.remoterepository.account_id
1919
relation.admin = relation.remoterepository.admin
@@ -42,14 +42,15 @@ def remote_relations_generator(relations, batch_size):
4242
)
4343

4444
batch_size = 1000
45-
remote_relations = remote_relations_generator(
45+
46+
remote_repository_relations = remote_repository_relations_generator(
4647
relations_queryset, batch_size
4748
)
4849

4950
# Follows Example from django docs
5051
# https://docs.djangoproject.com/en/2.2/ref/models/querysets/#bulk-create
5152
while True:
52-
batch = list(islice(remote_relations, batch_size))
53+
batch = list(islice(remote_repository_relations, batch_size))
5354

5455
if not batch:
5556
break
@@ -68,9 +69,9 @@ def remote_relations_generator(relations, batch_size):
6869
class Migration(migrations.Migration):
6970

7071
dependencies = [
71-
('oauth', '0011_add_remote_relation_model'),
72+
('oauth', '0012_add_remote_relation_model'),
7273
]
7374

7475
operations = [
75-
migrations.RunPython(move_data_to_remote_relations),
76+
migrations.RunPython(move_data_to_remote_repository_relations),
7677
]

readthedocs/oauth/models.py

+14-14
Original file line numberDiff line numberDiff line change
@@ -180,15 +180,6 @@ class Meta:
180180
def __str__(self):
181181
return 'Remote repository: {}'.format(self.html_url)
182182

183-
def get_serialized(self, key=None, default=None):
184-
try:
185-
data = json.loads(self.json)
186-
if key is not None:
187-
return data.get(key, default)
188-
return data
189-
except ValueError:
190-
pass
191-
192183
@property
193184
def clone_fuzzy_url(self):
194185
"""Try to match against several permutations of project URL."""
@@ -221,24 +212,33 @@ def matches(self, user):
221212
class RemoteRepositoryRelation(TimeStampedModel):
222213
remoterepository = models.ForeignKey(
223214
RemoteRepository,
224-
related_name='remote_relations',
215+
related_name='remote_repository_relations',
225216
on_delete=models.CASCADE
226217
)
227218
user = models.ForeignKey(
228219
User,
229-
related_name='remote_relations',
220+
related_name='remote_repository_relations',
230221
on_delete=models.CASCADE
231222
)
232223
account = models.ForeignKey(
233224
SocialAccount,
234225
verbose_name=_('Connected account'),
235-
related_name='remote_relations',
226+
related_name='remote_repository_relations',
236227
null=True,
237228
blank=True,
238-
on_delete=models.SET_NULL,
229+
on_delete=models.CASCADE,
239230
)
240231
admin = models.BooleanField(_('Has admin privilege'), default=False)
241232
json = JSONField(_('Serialized API response'))
242233

243234
class Meta:
244-
unique_together = (('remoterepository', 'user'),)
235+
unique_together = (('remoterepository', 'account'),)
236+
237+
def get_serialized(self, key=None, default=None):
238+
try:
239+
data = self.json
240+
if key is not None:
241+
return data.get(key, default)
242+
return data
243+
except ValueError:
244+
pass

readthedocs/oauth/services/base.py

+2-4
Original file line numberDiff line numberDiff line change
@@ -202,10 +202,8 @@ def sync(self):
202202
all_remote_repositories = remote_repositories + remote_repositories_organizations
203203
repository_full_names = [r.full_name for r in all_remote_repositories if r is not None]
204204
(
205-
self.user.oauth_repositories
206-
.exclude(
207-
Q(full_name__in=repository_full_names) | Q(project__isnull=False)
208-
)
205+
self.user.remote_repository_relations
206+
.exclude(remoterepository__full_name__in=repository_full_names)
209207
.filter(account=self.account)
210208
.delete()
211209
)

readthedocs/oauth/services/bitbucket.py

+28-14
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,11 @@
1414
from readthedocs.builds import utils as build_utils
1515
from readthedocs.integrations.models import Integration
1616

17-
from ..models import RemoteOrganization, RemoteRepository
17+
from ..models import (
18+
RemoteOrganization,
19+
RemoteRepository,
20+
RemoteRepositoryRelation,
21+
)
1822
from .base import Service, SyncServiceError
1923

2024

@@ -57,16 +61,18 @@ def sync_repositories(self):
5761
resp = self.paginate(
5862
'https://bitbucket.org/api/2.0/repositories/?role=admin',
5963
)
60-
admin_repos = (
61-
RemoteRepository.objects.filter(
62-
users=self.user,
63-
full_name__in=[r['full_name'] for r in resp],
64+
admin_repo_relations = (
65+
RemoteRepositoryRelation.objects.filter(
66+
user=self.user,
6467
account=self.account,
68+
remoterepository__full_name__in=[
69+
r['full_name'] for r in resp
70+
]
6571
)
6672
)
67-
for repo in admin_repos:
68-
repo.admin = True
69-
repo.save()
73+
for remote_relation in admin_repo_relations:
74+
remote_relation.admin = True
75+
remote_relation.save()
7076
except (TypeError, ValueError):
7177
pass
7278

@@ -120,13 +126,18 @@ def create_repository(self, fields, privacy=None, organization=None):
120126
"""
121127
privacy = privacy or settings.DEFAULT_PRIVACY_LEVEL
122128
if any([
123-
(privacy == 'private'),
124-
(fields['is_private'] is False and privacy == 'public'),
129+
(privacy == 'private'),
130+
(fields['is_private'] is False and privacy == 'public'),
125131
]):
126132
repo, _ = RemoteRepository.objects.get_or_create(
127-
full_name=fields['full_name'],
128-
account=self.account,
133+
full_name=fields['full_name']
134+
)
135+
remote_relation, _ = RemoteRepositoryRelation.objects.get_or_create(
136+
remoterepository=repo,
137+
user=self.user,
138+
account=self.account
129139
)
140+
130141
if repo.organization and repo.organization != organization:
131142
log.debug(
132143
'Not importing %s because mismatched orgs',
@@ -135,7 +146,6 @@ def create_repository(self, fields, privacy=None, organization=None):
135146
return None
136147

137148
repo.organization = organization
138-
repo.users.add(self.user)
139149
repo.name = fields['name']
140150
repo.description = fields['description']
141151
repo.private = fields['is_private']
@@ -163,8 +173,12 @@ def create_repository(self, fields, privacy=None, organization=None):
163173
if not repo.avatar_url:
164174
repo.avatar_url = self.default_user_avatar_url
165175

166-
repo.json = json.dumps(fields)
167176
repo.save()
177+
178+
remote_relation.account = self.account
179+
remote_relation.json = fields
180+
remote_relation.save()
181+
168182
return repo
169183

170184
log.debug(

readthedocs/oauth/services/github.py

+28-22
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
from allauth.socialaccount.providers.github.views import GitHubOAuth2Adapter
99

1010
from django.conf import settings
11-
from django.db.models import Q
1211
from django.urls import reverse
1312
from requests.exceptions import RequestException
1413

@@ -20,7 +19,11 @@
2019
)
2120
from readthedocs.integrations.models import Integration
2221

23-
from ..models import RemoteOrganization, RemoteRepository
22+
from ..models import (
23+
RemoteOrganization,
24+
RemoteRepository,
25+
RemoteRepositoryRelation,
26+
)
2427
from .base import Service, SyncServiceError
2528

2629
log = logging.getLogger(__name__)
@@ -97,21 +100,19 @@ def create_repository(self, fields, privacy=None, organization=None):
97100
"""
98101
privacy = privacy or settings.DEFAULT_PRIVACY_LEVEL
99102
if any([
100-
(privacy == 'private'),
101-
(fields['private'] is False and privacy == 'public'),
103+
(privacy == 'private'),
104+
(fields['private'] is False and privacy == 'public'),
102105
]):
103-
try:
104-
repo = RemoteRepository.objects.get(
105-
full_name=fields['full_name'],
106-
users=self.user,
107-
account=self.account,
108-
)
109-
except RemoteRepository.DoesNotExist:
110-
repo = RemoteRepository.objects.create(
111-
full_name=fields['full_name'],
112-
account=self.account,
113-
)
114-
repo.users.add(self.user)
106+
107+
repo, _ = RemoteRepository.objects.get_or_create(
108+
full_name=fields['full_name']
109+
)
110+
remote_relation, _ = RemoteRepositoryRelation.objects.get_or_create(
111+
remoterepository=repo,
112+
user=self.user,
113+
account=self.account
114+
)
115+
115116
if repo.organization and repo.organization != organization:
116117
log.debug(
117118
'Not importing %s because mismatched orgs',
@@ -125,19 +126,24 @@ def create_repository(self, fields, privacy=None, organization=None):
125126
repo.ssh_url = fields['ssh_url']
126127
repo.html_url = fields['html_url']
127128
repo.private = fields['private']
129+
repo.vcs = 'git'
130+
repo.avatar_url = fields.get('owner', {}).get('avatar_url')
131+
repo.default_branch = fields.get('default_branch')
132+
128133
if repo.private:
129134
repo.clone_url = fields['ssh_url']
130135
else:
131136
repo.clone_url = fields['clone_url']
132-
repo.admin = fields.get('permissions', {}).get('admin', False)
133-
repo.vcs = 'git'
134-
repo.default_branch = fields.get('default_branch')
135-
repo.account = self.account
136-
repo.avatar_url = fields.get('owner', {}).get('avatar_url')
137+
137138
if not repo.avatar_url:
138139
repo.avatar_url = self.default_user_avatar_url
139-
repo.json = json.dumps(fields)
140+
140141
repo.save()
142+
143+
remote_relation.json = fields
144+
remote_relation.admin = fields.get('permissions', {}).get('admin', False)
145+
remote_relation.save()
146+
141147
return repo
142148

143149
log.debug(

0 commit comments

Comments
 (0)