Repository: fossasia/open-event-server

Issue:
Don't show trashed items in API
Parent issue : #1220 

Trash has been implemented using a bool column in each table (in_trash) which is True if the item has been deleted. Currently API makes no distinction between trashed and active items. 
As trash is only for admins and not for the end-user, the API should not return trashed items and treat them as deleted. 



Discussion:
@aviaryan could u give me the link for the document from which u referred the import/export feature 

@aditya1702 Let's talk in chat. That question is not related to this issue. 


Resolving patch:
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
@@ -37,6 +37,8 @@ def get_object_list(klass, **kwargs):
     `klass` can be a model such as a Track, Event, Session, etc.
     """
     queryset = _get_queryset(klass)
+    if hasattr(klass, 'in_trash'):
+        queryset = queryset.filter(klass.in_trash != True)
     kwargs, specials = extract_special_queries(kwargs)
     # case insenstive filter
     for i in kwargs:
@@ -68,7 +70,7 @@ def get_object_or_404(klass, id_):
     """
     queryset = _get_queryset(klass)
     obj = queryset.get(id_)
-    if obj is None:
+    if obj is None or (hasattr(klass, 'in_trash') and obj.in_trash):
         raise NotFoundError(message='{} does not exist'.format(klass.__name__))
     return obj
 
@@ -227,7 +229,11 @@ def delete_model(model, item_id, event_id=None):
         item = get_object_in_event(model, item_id, event_id)
     else:
         item = get_object_or_404(model, item_id)
-    delete_from_db(item, '{} deleted'.format(model.__name__))
+    if hasattr(item, 'in_trash'):
+        item.in_trash = True
+        save_to_db(item, '{} moved to trash'.format(model.__name__))
+    else:
+        delete_from_db(item, '{} deleted'.format(model.__name__))
     return item
 
 


Test patch:
diff --git a/tests/api/test_delete_apis.py b/tests/api/test_delete_apis.py
--- a/tests/api/test_delete_apis.py
+++ b/tests/api/test_delete_apis.py
@@ -4,7 +4,7 @@
 
 from tests.setup_database import Setup
 from tests.utils import OpenEventTestCase
-from tests.api.utils import create_event, get_path
+from tests.api.utils import create_event, get_path, Event, Session
 from tests.api.utils_post_data import *
 from tests.auth_helper import register
 from open_event import current_app as app
@@ -50,8 +50,15 @@ def _test_model(self, name, data):
         self.assertEqual(response.status_code, 404)
         self.assertIn('does not exist', response.data)
 
+    def _test_trashed(self, model):
+        with app.test_request_context():
+            item = model.query.get(1)
+            self.assertNotEqual(item, None)
+            self.assertEqual(item.in_trash, True)
+
     def test_event_api(self):
         self._test_model('event', POST_EVENT_DATA)
+        self._test_trashed(Event)
 
     def test_track_api(self):
         self._test_model('track', POST_TRACK_DATA)
@@ -61,6 +68,7 @@ def test_microlocation_api(self):
 
     def test_session_api(self):
         self._test_model('session', POST_SESSION_DATA)
+        self._test_trashed(Session)
 
     def test_speaker_api(self):
         self._test_model('speaker', POST_SPEAKER_DATA)
diff --git a/tests/api/test_trashed_items.py b/tests/api/test_trashed_items.py
new file mode 100644
--- /dev/null
+++ b/tests/api/test_trashed_items.py
@@ -0,0 +1,61 @@
+import unittest
+
+from tests.setup_database import Setup
+from tests.utils import OpenEventTestCase
+from open_event.helpers.data import save_to_db
+from open_event.models.user import User
+from tests.auth_helper import register
+from tests.api.utils import get_path, create_event, create_services, \
+    Event, Session
+from open_event import current_app as app
+
+
+class TestTrashedItems404(OpenEventTestCase):
+    """
+    Test if trashed items return 404 through API
+    """
+
+    def setUp(self):
+        self.app = Setup.create_app()
+        with app.test_request_context():
+            register(self.app, u'Test@example.com', u'test')
+            event_id = create_event(creator_email='Test@example.com')
+            create_services(event_id)
+
+    def _test_model(self, name, model):
+        # get path
+        if name == 'event':
+            path = get_path(1)
+        elif name == 'user':
+            path = '/api/v2/users/1'
+        else:
+            path = get_path(1, name + 's', 1)
+        # check if exists
+        resp = self.app.get(path)
+        self.assertEqual(resp.status_code, 200)
+        # delete virtually
+        with app.test_request_context():
+            item = model.query.get(1)
+            item.in_trash = True
+            save_to_db(item)
+        # get item
+        resp = self.app.get(path)
+        self.assertEqual(resp.status_code, 404)
+        self.assertNotIn('Test', resp.data)
+        # get item list and check empty
+        resp = self.app.get(path[:-2])
+        self.assertEqual(resp.status_code, 200)
+        self.assertNotIn('Test', resp.data)
+
+    def test_event_api(self):
+        self._test_model('event', Event)
+
+    def test_session_api(self):
+        self._test_model('session', Session)
+
+    def test_user_api(self):
+        self._test_model('user', User)
+
+
+if __name__ == '__main__':
+    unittest.main()
diff --git a/tests/api/test_user_api.py b/tests/api/test_user_api.py
--- a/tests/api/test_user_api.py
+++ b/tests/api/test_user_api.py
@@ -2,6 +2,7 @@
 import json
 
 from open_event import current_app as app
+from open_event.models.user import User
 
 from tests.setup_database import Setup
 from tests.utils import OpenEventTestCase
@@ -98,6 +99,11 @@ def test_delete_api(self):
         self.assertIn('em1', resp.data)
         resp = self.app.get(path)
         self.assertEqual(resp.status_code, 404)
+        # test existance in database (in trash)
+        with app.test_request_context():
+            user = User.query.get(1)
+            self.assertNotEqual(user, None)
+            self.assertEqual(user.in_trash, True)
 
 
 if __name__ == '__main__':