Repository: fossasia/open-event-server

Issue:
Sponsors: Add seperate resourcelist class, docs, permissions for direct endpoints
parent issue #4101 


Discussion:

Resolving patch:
diff --git a/app/api/__init__.py b/app/api/__init__.py
--- a/app/api/__init__.py
+++ b/app/api/__init__.py
@@ -14,7 +14,7 @@
     SessionRelationshipOptional
 from app.api.speakers import SpeakerList, SpeakerDetail, SpeakerRelationshipRequired, SpeakerRelationshipOptional
 from app.api.social_links import SocialLinkList, SocialLinkDetail, SocialLinkRelationship
-from app.api.sponsors import SponsorList, SponsorDetail, SponsorRelationship
+from app.api.sponsors import SponsorList, SponsorListPost, SponsorDetail, SponsorRelationship
 from app.api.tracks import TrackList, TrackListPost, TrackDetail, TrackRelationshipOptional, TrackRelationshipRequired
 from app.api.speakers_calls import SpeakersCallList, SpeakersCallDetail, SpeakersCallRelationship
 from app.api.event_invoices import EventInvoiceList, EventInvoiceListPost, EventInvoiceDetail, \
@@ -46,8 +46,8 @@
 api.route(UserList, 'user_list', '/users')
 api.route(VerifyUser, 'verify_user', '/users/<int:user_id>/verify')
 api.route(UserDetail, 'user_detail', '/users/<int:id>', '/notifications/<int:notification_id>/user',
-          '/event-invoices/<int:event_invoice_id>/user', '/speakers/<int:speaker_id>/user', 
-          '/access-codes/<int:access_code_id>/marketer', '/email-notifications/<int:email_notification_id>/user', 
+          '/event-invoices/<int:event_invoice_id>/user', '/speakers/<int:speaker_id>/user',
+          '/access-codes/<int:access_code_id>/marketer', '/email-notifications/<int:email_notification_id>/user',
           '/discount-codes/<int:discount_code_id>/marketer')
 api.route(UserRelationship, 'user_notification', '/users/<int:id>/relationships/notifications')
 api.route(UserRelationship, 'user_event_invoices', '/users/<int:id>/relationships/event-invoices')
@@ -212,7 +212,8 @@
           '/social-links/<int:id>/relationships/event')
 
 # sponsors
-api.route(SponsorList, 'sponsor_list', '/sponsors', '/events/<int:event_id>/sponsors',
+api.route(SponsorListPost, 'sponsor_list_post', '/sponsors')
+api.route(SponsorList, 'sponsor_list', '/events/<int:event_id>/sponsors',
           '/events/<event_identifier>/sponsors')
 api.route(SponsorDetail, 'sponsor_detail', '/sponsors/<int:id>')
 api.route(SponsorRelationship, 'sponsor_event', '/sponsors/<int:id>/relationships/event')
diff --git a/app/api/sponsors.py b/app/api/sponsors.py
--- a/app/api/sponsors.py
+++ b/app/api/sponsors.py
@@ -1,6 +1,8 @@
 from marshmallow_jsonapi import fields
 from marshmallow_jsonapi.flask import Schema, Relationship
 from flask_rest_jsonapi import ResourceDetail, ResourceList, ResourceRelationship
+from flask_rest_jsonapi.exceptions import ObjectNotFound
+from flask import request
 
 from app.api.helpers.utilities import dasherize
 from app.api.helpers.permissions import jwt_required
@@ -10,12 +12,15 @@
 from app.api.bootstrap import api
 from app.api.helpers.db import safe_query
 from app.api.helpers.utilities import require_relationship
+from app.api.helpers.permission_manager import has_access
+from app.api.helpers.exceptions import ForbiddenException
 
 
 class SponsorSchema(Schema):
     """
     Sponsors API schema based on Sponsors model
     """
+
     class Meta:
         """
         Meta class for Sponsor schema
@@ -41,12 +46,25 @@ class Meta:
                          type_='event')
 
 
-class SponsorList(ResourceList):
+class SponsorListPost(ResourceList):
     """
     List and create Sponsors
     """
     def before_post(self, args, kwargs, data):
         require_relationship(['event'], data)
+        if not has_access('is_coorganizer', event_id=data['event']):
+            raise ForbiddenException({'source': ''}, 'Co-organizer access is required.')
+
+    methods = ['POST']
+    schema = SponsorSchema
+    data_layer = {'session': db.session,
+                  'model': Sponsor}
+
+
+class SponsorList(ResourceList):
+    """
+    List Sponsors
+    """
 
     def query(self, view_kwargs):
         """
@@ -57,35 +75,33 @@ def query(self, view_kwargs):
         query_ = self.session.query(Sponsor)
         if view_kwargs.get('event_id'):
             event = safe_query(self, Event, 'id', view_kwargs['event_id'], 'event_id')
-            query_ = query_.join(Event).filter(Event.id == event.id)
+            if event.state != 'published':
+                if 'Authorization' in request.headers and has_access('is_coorganizer', event_id=event.id):
+                    query_ = query_.join(Event).filter(Event.id == event.id)
+                else:
+                    raise ObjectNotFound({'parameter': 'event_id'},
+                                         "Event: {} not found".format(view_kwargs['event_identifier']))
+            else:
+                query_ = query_.join(Event).filter(Event.id == event.id)
         elif view_kwargs.get('event_identifier'):
             event = safe_query(self, Event, 'identifier', view_kwargs['event_identifier'], 'event_identifier')
-            query_ = query_.join(Event).filter(Event.id == event.id)
+            if event.state != 'published':
+                if 'Authorization' in request.headers and has_access('is_coorganizer', event_id=event.id):
+                    query_ = query_.join(Event).filter(Event.id == event.id)
+                else:
+                    raise ObjectNotFound({'parameter': 'event_identifier'},
+                                         "Event: {} not found".format(view_kwargs['event_identifier']))
+            else:
+                query_ = query_.join(Event).filter(Event.id == event.id)
         return query_
 
-    def before_create_object(self, data, view_kwargs):
-        """
-        method to create object before post
-        :param data:
-        :param view_kwargs:
-        :return:
-        """
-        if view_kwargs.get('event_id'):
-            event = safe_query(self, Event, 'id', view_kwargs['event_id'], 'event_id')
-            data['event_id'] = event.id
-
-        elif view_kwargs.get('event_identifier'):
-            event = safe_query(self, Event, 'identifier', view_kwargs['event_identifier'], 'identifier')
-            data['event_id'] = event.id
-
-    decorators = (api.has_permission('is_coorganizer', fetch='event_id', fetch_as="event_id", methods="POST",
-                                     check=lambda a: a.get('event_id') or a.get('event_identifier')),)
+    view_kwargs = True
+    methods = ['GET']
     schema = SponsorSchema
     data_layer = {'session': db.session,
                   'model': Sponsor,
                   'methods': {
-                      'query': query,
-                      'before_create_object': before_create_object
+                      'query': query
                   }}
 
 
@@ -104,7 +120,7 @@ class SponsorRelationship(ResourceRelationship):
     """
     Sponsor Schema Relation
     """
-    decorators = (jwt_required, )
+    decorators = (jwt_required,)
     methods = ['GET', 'PATCH']
     schema = SponsorSchema
     data_layer = {'session': db.session,


Test patch:
diff --git a/tests/hook_main.py b/tests/hook_main.py
--- a/tests/hook_main.py
+++ b/tests/hook_main.py
@@ -782,7 +782,7 @@ def speakers_call_delete(transaction):
 
 
 # ------------------------- Sponsors -------------------------
-@hooks.before("Sponsors > Sponsors Collection > List All Sponsors")
+@hooks.before("Sponsors > Sponsors Get Collection > List All Sponsors")
 def sponsor_get_list(transaction):
     """
     GET /events/1/sponsors
@@ -795,16 +795,13 @@ def sponsor_get_list(transaction):
         db.session.commit()
 
 
-@hooks.before("Sponsors > Sponsors Collection > Create Sponsor")
+@hooks.before("Sponsors > Sponsors Post Collection > Create Sponsor")
 def sponsor_post(transaction):
     """
-    POST /events/1/sponsors
+    POST /sponsors
     :param transaction:
     :return:
     """
-    # Skip until docs for direct endpoints added
-    transaction['skip'] = True
-
     with stash['app'].app_context():
         event = EventFactoryBasic()
         db.session.add(event)