Repository: City-of-Helsinki/linkedevents

Issue:
API validation: require short description and description



Discussion:

Resolving patch:
diff --git a/events/api.py b/events/api.py
--- a/events/api.py
+++ b/events/api.py
@@ -731,10 +731,8 @@ class EventSerializer(LinkedEventsSerializer, GeoModelAPIView):
     audience = JSONLDRelatedField(serializer=KeywordSerializer, view_name='keyword-detail',
                                   many=True, required=False, queryset=Keyword.objects.all())
     view_name = 'event-detail'
-    # JA: TODO: Temporarily disabled new validation code
-    fields_needed_to_publish = ('keywords', 'location', 'start_time') #,
-                                # 'short_description', 'description',
-                                # 'offers')
+
+    fields_needed_to_publish = ('keywords', 'location', 'start_time', 'short_description', 'description')
 
     def __init__(self, *args, skip_empties=False, **kwargs):
         super(EventSerializer, self).__init__(*args, **kwargs)
@@ -787,21 +785,34 @@ def validate(self, data):
         languages = [x[0] for x in settings.LANGUAGES]
 
         errors = {}
-        lang_error_msg = 'This field for language "%s" must be specified for event in this language' \
-                         ' before an event is published.'
+        lang_error_msg = _('This field must be specified before an event is published.')
         for field in self.fields_needed_to_publish:
             if field in self.translated_fields:
                 for lang in languages:
                     name = "name_%s" % lang
                     field_lang = "%s_%s" % (field, lang)
                     if data.get(name) and not data.get(field_lang):
-                        errors.setdefault(field, {})[lang] = _(lang_error_msg % lang)
+                        errors.setdefault(field, {})[lang] = lang_error_msg
+                    if field == 'short_description' and len(data.get(field_lang, [])) > 160:
+                        errors.setdefault(field, {})[lang] = (
+                            _('Short description length must be 160 characters or less'))
+
             elif not data.get(field):
                 # The start time may be null if a published event is postponed!
                 if field == 'start_time' and 'start_time' in data:
                     pass
                 else:
-                    errors[field] = _('This field must be specified before an event is published.')
+                    errors[field] = lang_error_msg
+
+        # published events need price info = at least one offer that either has a price or is free
+        price_exists = False
+        for offer in data.get('offers', []):
+            is_free = offer.get('is_free', False)
+            if is_free or 'price' in offer:
+                price_exists = True
+                break
+        if not price_exists:
+            errors['offers'] = _('Price info must be specified before an event is published.')
 
         # adjust start_time and has_start_time
 
@@ -835,11 +846,6 @@ def validate(self, data):
         if errors:
             raise serializers.ValidationError(errors)
 
-        # JA: TODO: Temporarily disabled new validation code
-        # if data.get('short_description') and len(data['short_description']) > 160:
-        #     raise serializers.ValidationError(
-        #         {'short_description': 'Short description length must be 160 characters or less'})
-
         return data
 
     def validate_event_status(self, value):


Test patch:
diff --git a/events/tests/test_event_post.py b/events/tests/test_event_post.py
--- a/events/tests/test_event_post.py
+++ b/events/tests/test_event_post.py
@@ -3,6 +3,7 @@
 
 import pytest
 from django.utils import timezone, translation
+from django.utils.encoding import force_text
 from .utils import versioned_reverse as reverse
 
 from events.tests.utils import assert_event_data_is_equal
@@ -177,3 +178,57 @@ def test_start_time_and_end_time_validation(api_client, minimal_event_dict, user
     assert response.status_code == 400
     assert 'Start time cannot be in the past.' in response.data['start_time']
     assert 'End time cannot be in the past.' in response.data['end_time']
+
+
+@pytest.mark.django_db
+def test_description_and_short_description_required_in_name_languages(api_client, minimal_event_dict, user):
+    api_client.force_authenticate(user)
+
+    minimal_event_dict['name'] = {'fi': 'nimi', 'sv': 'namn'}
+    minimal_event_dict['short_description'] = {'fi': 'lyhyt kuvaus'}
+    minimal_event_dict['description'] = {'sv': 'description in swedish'}
+
+    with translation.override('en'):
+        response = api_client.post(reverse('event-list'), minimal_event_dict, format='json')
+
+    # there should be only one error
+    assert len(response.data['short_description']) == 1
+    assert len(response.data['description']) == 1
+
+    assert (force_text(response.data['short_description']['sv']) ==
+            'This field must be specified before an event is published.')
+    assert (force_text(response.data['description']['fi']) ==
+            'This field must be specified before an event is published.')
+
+
+@pytest.mark.django_db
+def test_short_description_cannot_exceed_160_chars(api_client, minimal_event_dict, user):
+    api_client.force_authenticate(user)
+
+    minimal_event_dict['short_description']['fi'] = 'x' * 161
+
+    with translation.override('en'):
+        response = api_client.post(reverse('event-list'), minimal_event_dict, format='json')
+    assert response.status_code == 400
+    assert (force_text(response.data['short_description']['fi'] ==
+            'Short description length must be 160 characters or less'))
+
+
+@pytest.mark.django_db
+@pytest.mark.parametrize("offers, expected", [
+    ([{'is_free': True}], 201),
+    ([{'is_free': False, 'price': {'fi': 4}}], 201),
+    ([{'description': 'foo'}, {'is_free': True}], 201),
+
+    ([{'is_free': False}], 400)
+])
+def test_price_info_required(api_client, minimal_event_dict, user, offers, expected):
+    api_client.force_authenticate(user)
+    minimal_event_dict['offers'] = offers
+
+    with translation.override('en'):
+        response = api_client.post(reverse('event-list'), minimal_event_dict, format='json')
+
+    assert response.status_code == expected
+    if expected == 400:
+        assert force_text(response.data['offers'][0]) == 'Price info must be specified before an event is published.'