From b2e83ab9a4892ab4d6f58539791964e7175a2560 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antti=20M=C3=A4ki?= Date: Tue, 19 Dec 2023 09:57:43 +0200 Subject: [PATCH] Cyberstorm API: return empty PackageVersion.website_url as None The field works on the database level a bit differently than the other fields in that it allows empty values to be stored as empty strings rather than nulls. Hide this implementation detail and keep the API consistent by returning None if the field is in fact empty. Refs TS-1980 --- .../cyberstorm/tests/test_package_details.py | 23 +++++++++++++++++++ .../api/cyberstorm/views/package_detail.py | 14 ++++++++++- 2 files changed, 36 insertions(+), 1 deletion(-) diff --git a/django/thunderstore/api/cyberstorm/tests/test_package_details.py b/django/thunderstore/api/cyberstorm/tests/test_package_details.py index 353bdc8d3..8796fd9b0 100644 --- a/django/thunderstore/api/cyberstorm/tests/test_package_details.py +++ b/django/thunderstore/api/cyberstorm/tests/test_package_details.py @@ -194,5 +194,28 @@ def test_package_detail_view__returns_info(api_client: APIClient) -> None: assert actual["website_url"] == latest.website_url +@pytest.mark.django_db +def test_package_detail_view__serializes_url_correctly(api_client: APIClient) -> None: + l = PackageListingFactory( + package_version_kwargs={ + "website_url": "https://thunderstore.io/", + }, + ) + + url = f"/api/cyberstorm/package/{l.community.identifier}/{l.package.namespace}/{l.package.name}/" + response = api_client.get(url) + actual = response.json() + + assert actual["website_url"] == "https://thunderstore.io/" + + l.package.latest.website_url = "" + l.package.latest.save(update_fields=("website_url",)) + + response = api_client.get(url) + actual = response.json() + + assert actual["website_url"] == None + + def _date_to_z(value: datetime) -> str: return value.strftime("%Y-%m-%dT%H:%M:%S.%fZ") diff --git a/django/thunderstore/api/cyberstorm/views/package_detail.py b/django/thunderstore/api/cyberstorm/views/package_detail.py index 66d70b064..dd03b221d 100644 --- a/django/thunderstore/api/cyberstorm/views/package_detail.py +++ b/django/thunderstore/api/cyberstorm/views/package_detail.py @@ -37,6 +37,18 @@ class TeamSerializer(serializers.Serializer): members = CyberstormTeamMemberSerializer(many=True) +class EmptyStringAsNoneField(serializers.Field): + """ + Serialize empty string to None and deserialize vice versa. + """ + + def to_representation(self, value): + return None if value == "" else value + + def to_internal_value(self, data): + return "" if data is None else data + + class ResponseSerializer(serializers.Serializer): """ Data shown on package detail view. @@ -69,7 +81,7 @@ class ResponseSerializer(serializers.Serializer): rating_count = serializers.IntegerField(min_value=0) size = serializers.IntegerField(min_value=0, source="package.latest.file_size") team = TeamSerializer(source="package.owner") - website_url = serializers.CharField(source="package.latest.website_url") + website_url = EmptyStringAsNoneField(source="package.latest.website_url") def get_has_changelog(self, listing: PackageListing) -> bool: changelog = listing.package.latest.changelog