Skip to content

Commit

Permalink
Cyberstorm API: return empty PackageVersion.website_url as None
Browse files Browse the repository at this point in the history
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
  • Loading branch information
anttimaki committed Dec 19, 2023
1 parent 7ba4403 commit b2e83ab
Show file tree
Hide file tree
Showing 2 changed files with 36 additions and 1 deletion.
23 changes: 23 additions & 0 deletions django/thunderstore/api/cyberstorm/tests/test_package_details.py
Original file line number Diff line number Diff line change
Expand Up @@ -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")
14 changes: 13 additions & 1 deletion django/thunderstore/api/cyberstorm/views/package_detail.py
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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
Expand Down

0 comments on commit b2e83ab

Please sign in to comment.