Skip to content

Allow large form posts via multipart encoded forms to command API #5000

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jan 22, 2019
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 21 additions & 1 deletion readthedocs/doc_builder/environments.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,14 @@
from docker.errors import APIError as DockerAPIError
from docker.errors import DockerException
from requests.exceptions import ConnectionError
from requests_toolbelt.multipart.encoder import MultipartEncoder
from slumber.exceptions import HttpClientError

from readthedocs.builds.constants import BUILD_STATE_FINISHED
from readthedocs.builds.models import BuildCommandResultMixin
from readthedocs.core.utils import slugify
from readthedocs.projects.constants import LOG_TEMPLATE
from readthedocs.projects.models import Feature
from readthedocs.restapi.client import api as api_v2

from .constants import (
Expand Down Expand Up @@ -224,7 +226,25 @@ def save(self):
'start_time': self.start_time,
'end_time': self.end_time,
}
api_v2.command.post(data)

if self.build_env.project.has_feature(Feature.API_LARGE_DATA):
# Don't use slumber directly here. Slumber tries to enforce a string,
# which will break our multipart encoding here.
encoder = MultipartEncoder(
{key: str(value) for key, value in data.items()}
)
resource = api_v2.command
resp = resource._store['session'].post(
resource._store['base_url'] + '/',
data=encoder,
headers={
'Content-Type': encoder.content_type,
}
)
log.info('Post response via multipart form: %s', resp)
else:
resp = api_v2.command.post(data)
log.info('Post response via JSON encoded data: %s', resp)


class DockerBuildCommand(BuildCommand):
Expand Down
2 changes: 2 additions & 0 deletions readthedocs/projects/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -1010,6 +1010,7 @@ def add_features(sender, **kwargs):
DONT_OVERWRITE_SPHINX_CONTEXT = 'dont_overwrite_sphinx_context'
ALLOW_V2_CONFIG_FILE = 'allow_v2_config_file'
MKDOCS_THEME_RTD = 'mkdocs_theme_rtd'
API_LARGE_DATA = 'api_large_data'

FEATURES = (
(USE_SPHINX_LATEST, _('Use latest version of Sphinx')),
Expand All @@ -1022,6 +1023,7 @@ def add_features(sender, **kwargs):
(ALLOW_V2_CONFIG_FILE, _(
'Allow to use the v2 of the configuration file')),
(MKDOCS_THEME_RTD, _('Use Read the Docs theme for MkDocs as default theme')),
(API_LARGE_DATA, _('Try alternative method of posting large data'))
)

projects = models.ManyToManyField(
Expand Down
2 changes: 2 additions & 0 deletions readthedocs/restapi/views/model_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
from django.shortcuts import get_object_or_404
from django.template.loader import render_to_string
from rest_framework import decorators, permissions, status, viewsets
from rest_framework.parsers import MultiPartParser, JSONParser, FormParser
from rest_framework.renderers import BaseRenderer, JSONRenderer
from rest_framework.response import Response

Expand Down Expand Up @@ -235,6 +236,7 @@ class BuildViewSet(SettingsOverrideObject):


class BuildCommandViewSet(UserSelectViewSet):
parser_classes = [JSONParser, MultiPartParser]
permission_classes = [APIRestrictedPermission]
renderer_classes = (JSONRenderer,)
serializer_class = BuildCommandSerializer
Expand Down