Skip to content

Commit

Permalink
fix: handle errors while parsing dates in filters
Browse files Browse the repository at this point in the history
There were error, handling the returned value of the date string
parser. Problem was that the parser could return None value which was
compared against other None or date which obviously does not compute.

Solution to this is to fail early on the parser function and handle the
error correctly where the parser function is used.
This lead to writing error handling to the MaybeRelativeDateField since
the viewset using the function was correctly handling errors (just not
the None values).

Before this, when giving badly formatted date parameter in the
dateperiod end would have been okay and all the periods would be
returned - not anymore, now it returns a validation error.

Refs HAUKI-564
  • Loading branch information
nicobav committed Jan 31, 2025
1 parent f82a4dc commit edb49e7
Show file tree
Hide file tree
Showing 4 changed files with 45 additions and 5 deletions.
11 changes: 6 additions & 5 deletions hours/filters.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

from dateutil.parser import parse
from dateutil.relativedelta import MO, SU, relativedelta
from django.core.exceptions import ValidationError as DjangoValidationError
from django.db.models import Q
from django.forms import Field
from django.utils import timezone
Expand Down Expand Up @@ -75,10 +76,7 @@ def parse_maybe_relative_date_string(
return today + relativedelta(**relativedelta_params)

# Try to parse the exact date
try:
return parse(date_string, fuzzy=False).date()
except ValueError:
return None
return parse(date_string, fuzzy=False).date()


class MaybeRelativeDateField(Field):
Expand All @@ -95,7 +93,10 @@ def to_python(self, value):

value = value.strip()

return parse_maybe_relative_date_string(value, end_date=self.end_date)
try:
return parse_maybe_relative_date_string(value, end_date=self.end_date)
except ValueError:
raise DjangoValidationError("Invalid date format")


class MaybeRelativeNullableDateFilter(Filter):
Expand Down
12 changes: 12 additions & 0 deletions hours/tests/test_dateperiod_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,18 @@
from hours.tests.utils import assert_response_status_code


@pytest.mark.django_db
def test_invalid_format_returns_400(admin_client):
url = reverse("date_period-list")

response = admin_client.get(url, data={"start_date_lte": "2030-101-01"})

assert response.status_code == 400, "{} {}".format(
response.status_code, response.data
)
assert response.data["start_date_lte"][0] == "Invalid date format"


@pytest.mark.django_db
def test_list_date_periods_empty(admin_client):
url = reverse("date_period-list")
Expand Down
22 changes: 22 additions & 0 deletions hours/tests/test_opening_hours_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,28 @@

import pytest
from django.urls import reverse
from django.utils.translation import gettext_lazy as _


@pytest.mark.django_db
def test_opening_hours_invalid_date(admin_client):
url = reverse("opening_hours-list")

data = {
"start_date": "2020-101-01",
"end_date": "2020-11-30",
}

response = admin_client.get(
url,
data=data,
content_type="application/json",
)

assert response.status_code == 400, "{} {}".format(
response.status_code, response.data
)
assert response.data[0] == _("Invalid start_date")


@pytest.mark.django_db
Expand Down
5 changes: 5 additions & 0 deletions hours/tests/test_parse_filter_date.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,3 +85,8 @@
def test_parse_filter_date(input_string, end_date, frozen_date, expected_date):
with freeze_time(frozen_date):
assert parse_maybe_relative_date_string(input_string, end_date) == expected_date


def test_parse_errors():
with pytest.raises(ValueError):
parse_maybe_relative_date_string("2020-101-3")

0 comments on commit edb49e7

Please sign in to comment.