forked from readthedocs/readthedocs.org
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathserializers.py
329 lines (246 loc) · 9.65 KB
/
serializers.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
"""Defines serializers for each of our models."""
from allauth.socialaccount.models import SocialAccount
from rest_framework import serializers
from readthedocs.api.v2.utils import normalize_build_command
from readthedocs.builds.constants import EXTERNAL
from readthedocs.builds.models import Build, BuildCommandResult, Version
from readthedocs.oauth.models import RemoteOrganization, RemoteRepository
from readthedocs.projects.models import Domain, Project
class ProjectSerializer(serializers.ModelSerializer):
canonical_url = serializers.ReadOnlyField(source='get_docs_url')
class Meta:
model = Project
fields = (
'id',
'name',
'slug',
'description',
'language',
'programming_language',
'repo',
'repo_type',
'default_version',
'default_branch',
'documentation_type',
'users',
'canonical_url',
'urlconf',
)
class ProjectAdminSerializer(ProjectSerializer):
"""
Project serializer for admin only access.
Includes special internal fields that don't need to be exposed through the
general API, mostly for fields used in the build process
"""
features = serializers.SlugRelatedField(
many=True,
read_only=True,
slug_field='feature_id',
)
environment_variables = serializers.SerializerMethodField()
skip = serializers.SerializerMethodField()
def get_environment_variables(self, obj):
"""Get all environment variables, including public ones."""
return {
variable.name: dict(
value=variable.value,
public=variable.public,
)
for variable in obj.environmentvariable_set.all()
}
def get_skip(self, obj):
"""
Override ``Project.skip`` to consider more cases whether skip a project.
We rely on ``.is_active`` manager's method here that encapsulates all
these possible cases.
"""
return not Project.objects.is_active(obj)
class Meta(ProjectSerializer.Meta):
fields = ProjectSerializer.Meta.fields + (
"enable_epub_build",
"enable_pdf_build",
"conf_py_file",
"analytics_code",
"analytics_disabled",
"cdn_enabled",
"container_image",
"container_mem_limit",
"container_time_limit",
"install_project",
"use_system_packages",
"skip",
"requirements_file",
"python_interpreter",
"features",
"has_valid_clone",
"has_valid_webhook",
"show_advertising",
"environment_variables",
"max_concurrent_builds",
"build_config_file",
)
class VersionSerializer(serializers.ModelSerializer):
project = ProjectSerializer()
downloads = serializers.DictField(source='get_downloads', read_only=True)
class Meta:
model = Version
fields = [
'id',
'project',
'slug',
'identifier',
'verbose_name',
'privacy_level',
'active',
'built',
'downloads',
'type',
'has_pdf',
'has_epub',
'has_htmlzip',
'documentation_type',
]
class VersionAdminSerializer(VersionSerializer):
"""Version serializer that returns admin project data."""
project = ProjectAdminSerializer()
canonical_url = serializers.SerializerMethodField()
build_data = serializers.JSONField(required=False, write_only=True, allow_null=True)
def get_canonical_url(self, obj):
return obj.project.get_docs_url(
lang_slug=obj.project.language,
version_slug=obj.slug,
external=obj.type == EXTERNAL,
)
class Meta(VersionSerializer.Meta):
fields = VersionSerializer.Meta.fields + [
"build_data",
"canonical_url",
]
class BuildCommandSerializer(serializers.ModelSerializer):
run_time = serializers.ReadOnlyField()
class Meta:
model = BuildCommandResult
exclude = []
class BuildCommandReadOnlySerializer(BuildCommandSerializer):
"""
Serializer used on GETs to trim the commands' path.
Remove unreadable paths from the command outputs when returning it from the API.
We could make this change at build level, but we want to avoid undoable issues from now
and hack a small solution to fix the immediate problem.
This converts:
$ /usr/src/app/checkouts/readthedocs.org/user_builds/
<container_hash>/<project_slug>/envs/<version_slug>/bin/python
$ /home/docs/checkouts/readthedocs.org/user_builds/
<project_slug>/envs/<version_slug>/bin/python
into
$ python
"""
command = serializers.SerializerMethodField()
def get_command(self, obj):
return normalize_build_command(
obj.command, obj.build.project.slug, obj.build.version.slug
)
class BuildSerializer(serializers.ModelSerializer):
"""
Build serializer for user display.
This is the default serializer for Build objects over read-only operations from regular users.
Take into account that:
- It doesn't display internal fields (builder, _config)
- It's read-only for multiple fields (commands, project_slug, etc)
Staff users should use either:
- BuildAdminSerializer for write operations (e.g. builders hitting the API),
- BuildAdminReadOnlySerializer for read-only actions (e.g. dashboard retrieving build details)
"""
commands = BuildCommandReadOnlySerializer(many=True, read_only=True)
project_slug = serializers.ReadOnlyField(source='project.slug')
version_slug = serializers.ReadOnlyField(source='get_version_slug')
docs_url = serializers.SerializerMethodField()
state_display = serializers.ReadOnlyField(source='get_state_display')
commit_url = serializers.ReadOnlyField(source='get_commit_url')
# Jsonfield needs an explicit serializer
# https://github.com/dmkoch/django-jsonfield/issues/188#issuecomment-300439829
config = serializers.JSONField(required=False, allow_null=True)
class Meta:
model = Build
# `_config` should be excluded to avoid conflicts with `config`
exclude = ('builder', '_config')
def get_docs_url(self, obj):
if obj.version:
return obj.version.get_absolute_url()
return None
class BuildAdminSerializer(BuildSerializer):
"""
Build serializer to update Build objects from build instances.
It allows write operations on `commands` and display fields (e.g. builder)
that are allowed for admin purposes only.
"""
commands = BuildCommandSerializer(many=True, read_only=True)
class Meta(BuildSerializer.Meta):
# `_config` should be excluded to avoid conflicts with `config`
exclude = ('_config',)
class BuildAdminReadOnlySerializer(BuildAdminSerializer):
"""
Build serializer to retrieve Build objects from the dashboard.
It uses `BuildCommandReadOnlySerializer` to automatically parse the command
and trim the useless path.
"""
commands = BuildCommandReadOnlySerializer(many=True, read_only=True)
class SearchIndexSerializer(serializers.Serializer):
q = serializers.CharField(max_length=500)
project = serializers.CharField(max_length=500, required=False)
version = serializers.CharField(max_length=500, required=False)
page = serializers.CharField(max_length=500, required=False)
class DomainSerializer(serializers.ModelSerializer):
project = ProjectSerializer()
class Meta:
model = Domain
fields = (
'id',
'project',
'domain',
'canonical',
'machine',
'cname',
)
class RemoteOrganizationSerializer(serializers.ModelSerializer):
class Meta:
model = RemoteOrganization
exclude = ('email', 'users',)
class RemoteRepositorySerializer(serializers.ModelSerializer):
"""Remote service repository serializer."""
organization = RemoteOrganizationSerializer()
# This field does create an additional query per object returned
matches = serializers.SerializerMethodField()
admin = serializers.SerializerMethodField('is_admin')
class Meta:
model = RemoteRepository
exclude = ('users',)
def get_matches(self, obj):
request = self.context['request']
if request.user is not None and request.user.is_authenticated:
return obj.matches(request.user)
def is_admin(self, obj):
request = self.context['request']
# Use annotated value from RemoteRepositoryViewSet queryset
if hasattr(obj, 'admin'):
return obj.admin
if request.user and request.user.is_authenticated:
return obj.remote_repository_relations.filter(
user=request.user, admin=True
).exists()
return False
class ProviderSerializer(serializers.Serializer):
id = serializers.CharField(max_length=20)
name = serializers.CharField(max_length=20)
class SocialAccountSerializer(serializers.ModelSerializer):
username = serializers.SerializerMethodField()
avatar_url = serializers.URLField(source='get_avatar_url')
provider = ProviderSerializer(source='get_provider')
class Meta:
model = SocialAccount
exclude = ('extra_data',)
def get_username(self, obj):
return (
obj.extra_data.get('username') or obj.extra_data.get('login')
# FIXME: which one is GitLab?
)