From d38f3778c907d1f3a6d5a0555bd8d049c4f73433 Mon Sep 17 00:00:00 2001 From: Matthias Dellweg Date: Tue, 19 May 2026 14:55:57 +0200 Subject: [PATCH] Let the DownloaderConfig serializer consume json When used as a nested object in a multipart body, json is the default stringification method. fixes: #7677 (cherry picked from commit bf58f08956d72295018e40f63e0362b34797b59c) --- CHANGES/7677.bugfix | 1 + .../tests/functional/api/test_crud_content_unit.py | 6 +++++- pulpcore/app/serializers/base.py | 14 +++++++++++++- 3 files changed, 19 insertions(+), 2 deletions(-) create mode 100644 CHANGES/7677.bugfix diff --git a/CHANGES/7677.bugfix b/CHANGES/7677.bugfix new file mode 100644 index 00000000000..ecedf6c8d6d --- /dev/null +++ b/CHANGES/7677.bugfix @@ -0,0 +1 @@ +Allow nested downloader_config to be passed json stringified. diff --git a/pulp_file/tests/functional/api/test_crud_content_unit.py b/pulp_file/tests/functional/api/test_crud_content_unit.py index bf7fbe19bde..04aedc53d4b 100644 --- a/pulp_file/tests/functional/api/test_crud_content_unit.py +++ b/pulp_file/tests/functional/api/test_crud_content_unit.py @@ -296,7 +296,11 @@ def test_create_file_from_url( ): # Test create w/ url remote = file_remote_factory(manifest_path=basic_manifest_path) - body = {"file_url": remote.url, "relative_path": "PULP_MANIFEST"} + body = { + "file_url": remote.url, + "relative_path": "PULP_MANIFEST", + "downloader_config": {"total_timeout": 5}, + } response = file_bindings.ContentFilesApi.create(**body) task = monitor_task(response.task) assert len(task.created_resources) == 1 diff --git a/pulpcore/app/serializers/base.py b/pulpcore/app/serializers/base.py index 17d26c3b5fd..b130449f262 100644 --- a/pulpcore/app/serializers/base.py +++ b/pulpcore/app/serializers/base.py @@ -1,4 +1,5 @@ import functools +import json import re import traceback from collections import namedtuple @@ -80,7 +81,7 @@ def to_internal_value(self, data): return super().to_internal_value(data) -class _MatchingRegexViewName(object): +class _MatchingRegexViewName: """This is a helper class to help defining object matching rules for master-detail. If you can be specific, please specify the `view_name`, but if you cannot, this allows @@ -778,6 +779,17 @@ def _validate_certificate(which_cert, value): "Invalid {} specified, error '{}'".format(which_cert, e.args) ) + def to_internal_value(self, data): + if isinstance(data, str): + try: + # Nested Serializers in from-urlencoded bodies are stringified as json by default. + data = json.loads(data) + except json.DecodeError: + raise serializers.ValidationError( + "Invalid json data for (nested) downloader config." + ) + return super().to_internal_value(data) + def validate(self, data): """ Check that proxy credentials are only provided completely and if a proxy is configured.