Skip to content

Commit fe278cf

Browse files
committed
View/Serializer to import a Project from APIv3
1 parent b576f16 commit fe278cf

File tree

2 files changed

+63
-4
lines changed

2 files changed

+63
-4
lines changed

readthedocs/api/v3/serializers.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -378,6 +378,22 @@ def get_translations(self, obj):
378378
return self._absolute_url(path)
379379

380380

381+
class ProjectCreateSerializer(FlexFieldsModelSerializer):
382+
383+
"""Serializer used to Import a Project."""
384+
385+
repository = RepositorySerializer(source='*')
386+
387+
class Meta:
388+
model = Project
389+
fields = (
390+
'name',
391+
'language',
392+
'repository',
393+
'project_url', # project_homepage
394+
)
395+
396+
381397
class ProjectSerializer(FlexFieldsModelSerializer):
382398

383399
language = LanguageSerializer()

readthedocs/api/v3/views.py

Lines changed: 47 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,10 @@
1818
from rest_framework_extensions.mixins import NestedViewSetMixin
1919

2020
from readthedocs.builds.models import Build, Version
21-
from readthedocs.core.utils import trigger_build
21+
from readthedocs.core.utils import trigger_build, trigger_initial_build
2222
from readthedocs.projects.models import Project
23+
from readthedocs.projects.signals import project_import
24+
2325

2426
from .filters import BuildFilter, ProjectFilter, VersionFilter
2527
from .mixins import ProjectQuerySetMixin
@@ -29,6 +31,7 @@
2931
BuildCreateSerializer,
3032
BuildSerializer,
3133
ProjectSerializer,
34+
ProjectCreateSerializer,
3235
VersionSerializer,
3336
VersionUpdateSerializer,
3437
)
@@ -63,7 +66,7 @@ class APIv3Settings:
6366

6467

6568
class ProjectsViewSet(APIv3Settings, NestedViewSetMixin, ProjectQuerySetMixin,
66-
FlexFieldsMixin, ReadOnlyModelViewSet):
69+
FlexFieldsMixin, CreateModelMixin, ReadOnlyModelViewSet):
6770

6871
# Markdown docstring is automatically rendered by BrowsableAPIRenderer.
6972

@@ -111,14 +114,13 @@ class ProjectsViewSet(APIv3Settings, NestedViewSetMixin, ProjectQuerySetMixin,
111114
* Subprojects of a project: ``/api/v3/projects/{project_slug}/subprojects/``
112115
* Superproject of a project: ``/api/v3/projects/{project_slug}/superproject/``
113116
114-
Go to [https://docs.readthedocs.io/en/stable/api/v3.html](https://docs.readthedocs.io/en/stable/api/v3.html)
117+
Go to [https://docs.readthedocs.io/page/api/v3.html](https://docs.readthedocs.io/page/api/v3.html)
115118
for a complete documentation of the APIv3.
116119
""" # noqa
117120

118121
model = Project
119122
lookup_field = 'slug'
120123
lookup_url_kwarg = 'project_slug'
121-
serializer_class = ProjectSerializer
122124
filterset_class = ProjectFilter
123125
queryset = Project.objects.all()
124126
permit_list_expands = [
@@ -127,6 +129,20 @@ class ProjectsViewSet(APIv3Settings, NestedViewSetMixin, ProjectQuerySetMixin,
127129
'active_versions.last_build.config',
128130
]
129131

132+
def get_serializer_class(self):
133+
"""
134+
Return correct serializer depending on the action.
135+
136+
For GET it returns a serializer with many fields and on PUT/PATCH/POST,
137+
it return a serializer to validate just a few fields.
138+
"""
139+
if self.action in ('list', 'retrieve'):
140+
return ProjectSerializer
141+
elif self.action in ('create',):
142+
return ProjectCreateSerializer
143+
# elif self.action in ('update', 'partial_update'):
144+
# return ProjectUpdateSerializer
145+
130146
def get_queryset(self):
131147
# Allow hitting ``/api/v3/projects/`` to list their own projects
132148
if self.basename == 'projects' and self.action == 'list':
@@ -162,6 +178,33 @@ def get_view_description(self, *args, **kwargs): # pylint: disable=arguments-di
162178
return mark_safe(description.format(project_slug=project.slug))
163179
return description
164180

181+
def create(self, request, *args, **kwargs):
182+
"""
183+
Override method to importing a Project.
184+
185+
* Save the Project object
186+
* Assign the user from the request as owner
187+
* Sent project_import signal
188+
* Trigger an initial Build
189+
"""
190+
serializer = self.get_serializer(data=request.data)
191+
serializer.is_valid(raise_exception=True)
192+
project = serializer.save()
193+
headers = self.get_success_headers(serializer.data)
194+
195+
# TODO: these lines need to be adapted for Corporate
196+
project.users.add(request.user)
197+
project_import.send(sender=project, request=request)
198+
trigger_initial_build(project, request.user)
199+
200+
# Full render Project
201+
serializer = ProjectSerializer(instance=project)
202+
return Response(
203+
serializer.data,
204+
status=status.HTTP_201_CREATED,
205+
headers=headers,
206+
)
207+
165208
@action(detail=True, methods=['get'])
166209
def superproject(self, request, project_slug):
167210
"""Return the superproject of a ``Project``."""

0 commit comments

Comments
 (0)