Skip to content

Version: new field addons #10337

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 3 commits into from
May 24, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
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
2 changes: 2 additions & 0 deletions readthedocs/api/v2/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@ class VersionAdminSerializer(VersionSerializer):
project = ProjectAdminSerializer()
canonical_url = serializers.SerializerMethodField()
build_data = serializers.JSONField(required=False, write_only=True, allow_null=True)
addons = serializers.BooleanField(required=False, write_only=True, allow_null=False)

def get_canonical_url(self, obj):
return obj.project.get_docs_url(
Expand All @@ -138,6 +139,7 @@ def get_canonical_url(self, obj):

class Meta(VersionSerializer.Meta):
fields = VersionSerializer.Meta.fields + [
"addons",
"build_data",
"canonical_url",
]
Expand Down
23 changes: 23 additions & 0 deletions readthedocs/builds/migrations/0051_add_addons_field.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Generated by Django 3.2.19 on 2023-05-23 07:07

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
("builds", "0050_build_readthedocs_yaml_path"),
]

operations = [
migrations.AddField(
model_name="version",
name="addons",
field=models.BooleanField(
blank=True,
default=False,
null=True,
verbose_name="Inject new addons js library for this version",
),
),
]
16 changes: 16 additions & 0 deletions readthedocs/builds/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,11 @@ class Version(TimeStampedModel):
populate_from='verbose_name',
)

# TODO: this field (`supported`) could be removed. It's returned only on
# the footer API response but I don't think anybody is using this field at
# all.
supported = models.BooleanField(_('Supported'), default=True)

active = models.BooleanField(_('Active'), default=False)
state = models.CharField(
_("State"),
Expand All @@ -156,7 +160,12 @@ class Version(TimeStampedModel):
help_text=_("State of the PR/MR associated to this version."),
)
built = models.BooleanField(_("Built"), default=False)

# TODO: this field (`uploaded`) could be removed. It was used to mark a
# version as "Manually uploaded" by the core team, but this is not required
# anymore. Users can use `build.commands` for these cases now.
uploaded = models.BooleanField(_("Uploaded"), default=False)

privacy_level = models.CharField(
_('Privacy Level'),
max_length=20,
Expand Down Expand Up @@ -192,6 +201,13 @@ class Version(TimeStampedModel):
null=True,
)

addons = models.BooleanField(
_("Inject new addons js library for this version"),
null=True,
blank=True,
default=False,
)

objects = VersionManager.from_queryset(VersionQuerySet)()
# Only include BRANCH, TAG, UNKNOWN type Versions.
internal = InternalVersionManager.from_queryset(partial(VersionQuerySet, internal_only=True))()
Expand Down
7 changes: 7 additions & 0 deletions readthedocs/doc_builder/director.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,9 @@ def __init__(self, data):
"""
self.data = data

# Reset `addons` field. It will be set to `True` only when it's built via `build.commands`
self.data.version.addons = False

def setup_vcs(self):
"""
Perform all VCS related steps.
Expand Down Expand Up @@ -422,6 +425,10 @@ def run_build_commands(self):
# Update the `Version.documentation_type` to match the doctype defined
# by the config file. When using `build.commands` it will be `GENERIC`
self.data.version.documentation_type = self.data.config.doctype

# Mark this version to inject the new js client when serving it via El Proxito
self.data.version.addons = True

self.store_readthedocs_build_yaml()

def install_build_tools(self):
Expand Down
1 change: 1 addition & 0 deletions readthedocs/projects/tasks/builds.py
Original file line number Diff line number Diff line change
Expand Up @@ -600,6 +600,7 @@ def on_success(self, retval, task_id, args, kwargs):
"has_epub": "epub" in valid_artifacts,
"has_htmlzip": "htmlzip" in valid_artifacts,
"build_data": self.data.version.build_data,
"addons": self.data.version.addons,
}
)
except HttpClientError:
Expand Down
2 changes: 2 additions & 0 deletions readthedocs/projects/tests/test_build_tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -299,6 +299,7 @@ def test_build_updates_documentation_type(self, load_yaml_config):
assert self.requests_mock.request_history[7]._request.method == "PATCH"
assert self.requests_mock.request_history[7].path == "/api/v2/version/1/"
assert self.requests_mock.request_history[7].json() == {
"addons": False,
"build_data": None,
"built": True,
"documentation_type": "mkdocs",
Expand Down Expand Up @@ -552,6 +553,7 @@ def test_successful_build(
assert self.requests_mock.request_history[7]._request.method == "PATCH"
assert self.requests_mock.request_history[7].path == "/api/v2/version/1/"
assert self.requests_mock.request_history[7].json() == {
"addons": False,
"build_data": None,
"built": True,
"documentation_type": "sphinx",
Expand Down
15 changes: 15 additions & 0 deletions readthedocs/proxito/middleware.py
Original file line number Diff line number Diff line change
Expand Up @@ -289,10 +289,25 @@ def process_request(self, request): # noqa
return None

def add_hosting_integrations_headers(self, request, response):
addons = False
project_slug = getattr(request, "path_project_slug", "")
version_slug = getattr(request, "path_version_slug", "")

if project_slug:
project = Project.objects.get(slug=project_slug)

# Check for the feature flag
if project.has_feature(Feature.HOSTING_INTEGRATIONS):
addons = True
else:
# Check if the version forces injecting the addons (e.g. using `build.commands`)
version = (
project.versions.filter(slug=version_slug).only("addons").first()
)
if version and version.addons:
addons = True

if addons:
response["X-RTD-Hosting-Integrations"] = "true"

def _get_https_redirect(self, request):
Expand Down
12 changes: 12 additions & 0 deletions readthedocs/proxito/tests/test_headers.py
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,18 @@ def test_user_domain_headers(self):
self.assertEqual(r[http_header], http_header_value)
self.assertEqual(r[http_header_secure], http_header_value)

def test_hosting_integrations_header(self):
version = self.project.versions.get(slug=LATEST)
version.addons = True
version.save()

r = self.client.get(
"/en/latest/", secure=True, HTTP_HOST="project.dev.readthedocs.io"
)
self.assertEqual(r.status_code, 200)
self.assertIsNotNone(r.get("X-RTD-Hosting-Integrations"))
self.assertEqual(r["X-RTD-Hosting-Integrations"], "true")

@override_settings(ALLOW_PRIVATE_REPOS=False)
def test_cache_headers_public_version_with_private_projects_not_allowed(self):
r = self.client.get(
Expand Down