Repository: fossasia/open-event-server

Issue:
Import fails if copyright is null or not set in Event API payload
This was reported here https://github.com/loklak/loklak_server/issues/642#issuecomment-230478572
Copyright was added to Event API here https://github.com/fossasia/open-event-orga-server/pull/1425
Also some code can be optimized using DAOs. 



Discussion:

Resolving patch:
diff --git a/open_event/api/events.py b/open_event/api/events.py
--- a/open_event/api/events.py
+++ b/open_event/api/events.py
@@ -9,8 +9,7 @@
 from open_event.models.user import ORGANIZER
 from open_event.helpers.data import save_to_db, update_version, record_activity
 
-from .helpers.helpers import get_paginated_list, requires_auth, parse_args, \
-    update_model, get_object_or_404
+from .helpers.helpers import get_paginated_list, requires_auth, parse_args
 from .helpers.utils import PAGINATED_MODEL, PaginatedResourceBase, \
     PAGE_PARAMS, POST_RESPONSES, PUT_RESPONSES, BaseDAO, ServiceDAO
 from .helpers import custom_fields as fields
@@ -116,18 +115,12 @@ def create(self, data, url):
         data = self.validate(data)
         payload = self.fix_payload(data)
         # Extract and save copyright info
-        copyright_data = payload.get('copyright')
-        payload.pop('copyright', None)
-        copyright = EventCopyright(**copyright_data)
-        save_to_db(copyright, 'Copyright info saved')
-
+        if payload.get('copyright'):
+            payload['copyright'] = CopyrightDAO.create(payload['copyright'])
+        # save event
         new_event = self.model(**payload)
-        save_to_db(new_event, "Event saved")
-        # Set event creator
         new_event.creator = g.user
-        # Set copyright info
-        new_event.copyright = copyright
-
+        save_to_db(new_event, "Event saved")
         # set organizer
         role = Role.query.filter_by(name=ORGANIZER).first()
         uer = UsersEventsRoles(g.user, new_event, role)
@@ -145,22 +138,24 @@ def create(self, data, url):
     def update(self, event_id, data):
         data = self.validate(data)
         payload = self.fix_payload(data)
-        # Extract and update copyright info
-        copyright_data = payload.get('copyright')
-        event = get_object_or_404(EventModel, event_id)
-        copyright = update_model(
-            EventCopyright,
-            event.copyright.id,
-            copyright_data,
-            event_id
-        )
-        save_to_db(copyright)
-        payload.pop('copyright', None)
+        # get event
+        event = self.get(event_id)
+        # update/create copyright
+        if payload.get('copyright'):
+            payload['copyright']['event'] = event
+            if not event.copyright:
+                # validate=False to not delete event key
+                CopyrightDAO.create(payload['copyright'], validate=False)
+            else:
+                CopyrightDAO.update(event.copyright.id, payload['copyright'])
+        # delete related fields
+        payload.pop('copyright')
         return BaseDAO.update(self, event_id, payload, validate=False)
 
 
 LinkDAO = SocialLinkDAO(SocialLinkModel, SOCIAL_LINK_POST)
 DAO = EventDAO(EventModel, EVENT_POST)
+CopyrightDAO = BaseDAO(EventCopyright, EVENT_COPYRIGHT)
 
 # DEFINE PARAMS
 
diff --git a/open_event/api/helpers/helpers.py b/open_event/api/helpers/helpers.py
--- a/open_event/api/helpers/helpers.py
+++ b/open_event/api/helpers/helpers.py
@@ -186,7 +186,8 @@ def validate_payload(payload, api_model):
             field = field.container
             data = payload[key]
         elif isinstance(field, fields.Nested):
-            validate_payload(payload[key], field.model)
+            if payload[key]:  # not null
+                validate_payload(payload[key], field.model)
         else:
             data = [payload[key]]
         if isinstance(field, CustomField) and hasattr(field, 'validate'):


Test patch:
diff --git a/tests/api/test_get_api_200.py b/tests/api/test_get_api_200.py
--- a/tests/api/test_get_api_200.py
+++ b/tests/api/test_get_api_200.py
@@ -19,7 +19,7 @@ def setUp(self):
             # Associate services to event_id
             create_services(event_id)
 
-    def _test_path(self, path, string):
+    def _test_path(self, path, *strings):
         """Helper function.
         Test response for 200 status code. Also test if response body
         contains event/service name.
@@ -27,11 +27,12 @@ def _test_path(self, path, string):
         with app.test_request_context():
             response = self.app.get(path, follow_redirects=True)
             self.assertEqual(response.status_code, 200)
-            self.assertIn(string, response.data)
+            for string in strings:
+                self.assertIn(string, response.data)
 
     def test_event_api(self):
         path = get_path(1)
-        self._test_path(path, 'TestEvent')
+        self._test_path(path, 'TestEvent', '"copyright": null')
 
     def test_track_api(self):
         path = get_path(1, 'tracks', 1)
diff --git a/tests/api/test_post_api.py b/tests/api/test_post_api.py
--- a/tests/api/test_post_api.py
+++ b/tests/api/test_post_api.py
@@ -64,7 +64,10 @@ def _test_model(self, name, data, checks=[]):
         return response
 
     def test_event_api(self):
-        self._test_model('event', POST_EVENT_DATA, ['test@example.com'])
+        self._test_model(
+            'event', POST_EVENT_DATA,
+            ['test@example.com', 'Test licence']
+        )
 
     def test_track_api(self):
         self._test_model('track', POST_TRACK_DATA)
diff --git a/tests/api/test_put_api.py b/tests/api/test_put_api.py
--- a/tests/api/test_put_api.py
+++ b/tests/api/test_put_api.py
@@ -39,7 +39,7 @@ class TestPutApi(TestPutApiBase):
     Test PUT APIs against 401 (unauthorized) and
     200 (successful) status codes
     """
-    def _test_model(self, name, data):
+    def _test_model(self, name, data, *args):
         """
         Tests -
         1. Without login, try to do a PUT request and catch 401 error
@@ -55,9 +55,12 @@ def _test_model(self, name, data):
         self.assertEqual(200, response.status_code, msg=response.data)
         # surrounded by quotes for strict checking
         self.assertIn('"Test%s"' % str(name).title(), response.data)
+        # check more things
+        for _ in args:
+            self.assertIn(_, response.data, response.data)
 
     def test_event_api(self):
-        self._test_model('event', POST_EVENT_DATA)
+        self._test_model('event', POST_EVENT_DATA, 'Test licence')
 
     def test_track_api(self):
         self._test_model('track', POST_TRACK_DATA)
diff --git a/tests/api/utils.py b/tests/api/utils.py
--- a/tests/api/utils.py
+++ b/tests/api/utils.py
@@ -6,7 +6,6 @@
 from open_event.models.session import Session
 from open_event.models.speaker import Speaker
 from open_event.models.sponsor import Sponsor
-from open_event.models.event_copyright import EventCopyright as Copyright
 from open_event.models.microlocation import Microlocation
 from open_event.models.session_type import SessionType
 from open_event.models.social_link import SocialLink
@@ -17,11 +16,9 @@ def create_event(name='TestEvent', creator_email=None):
     """Creates Event and returns its `id`.
     :param name Name of Event
     """
-    copyright = Copyright(holder='copyright holder')
     event = Event(name=name,
                   start_time=datetime(2016, 4, 8, 12, 30, 45),
-                  end_time=datetime(2016, 4, 9, 12, 30, 45),
-                  copyright=copyright)
+                  end_time=datetime(2016, 4, 9, 12, 30, 45))
     if creator_email:
         event.creator = User.query.filter_by(email=creator_email).first()
     save_to_db(event, 'Event saved')