Repository: fossasia/open-event-server

Issue:
Implement StripeAuthorization API with permissions
**I'm submitting a ...**  (check one with "x")
- [ ] bug report
- [x] feature request
- [ ] support request => Please do not submit support requests here, instead ask your query in out Gitter channel at https://gitter.im/fossasia/open-event-orga-server




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
@@ -1,4 +1,6 @@
 from app.api.bootstrap import api
+from app.api.stripe_authorization import StripeAuthorizationList, StripeAuthorizationDetail, \
+    StripeAuthorizationRelationship, StripeAuthorizationListPost
 from app.api.ticket_fees import TicketFeeList, TicketFeeDetail
 from app.api.users import UserList, UserDetail, UserRelationship, VerifyUser
 from app.api.notifications import NotificationList, NotificationDetail, NotificationRelationship
@@ -46,8 +48,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')
@@ -359,3 +361,12 @@
 api.route(CustomFormDetail, 'custom_form_detail', '/custom-forms/<int:id>')
 api.route(CustomFormRelationshipRequired, 'custom_form_event',
           '/custom-forms/<int:id>/relationships/event')
+
+# Stripe Authorization API
+api.route(StripeAuthorizationListPost, 'stripe_authorization_list_post', '/stripe-authorization')
+api.route(StripeAuthorizationList, 'stripe_authorization_list', '/events/<int:event_id>/stripe-authorization',
+          '/events/<event_identifier>/stripe-authorization')
+api.route(StripeAuthorizationDetail, 'stripe_authorization_detail',  '/stripe-authorization/<int:id>',
+          '/events/<int:event_id>/stripe-authorization', '/events/<event_identifier>/stripe-authorization')
+api.route(StripeAuthorizationRelationship, 'stripe_authorization_event',
+          '/stripe-authorization/<int:id>/relationships/event')
diff --git a/app/api/stripe_authorization.py b/app/api/stripe_authorization.py
new file mode 100644
--- /dev/null
+++ b/app/api/stripe_authorization.py
@@ -0,0 +1,122 @@
+from flask_rest_jsonapi import ResourceDetail, ResourceList
+from marshmallow_jsonapi.flask import Schema, Relationship
+from marshmallow_jsonapi import fields
+from sqlalchemy.orm.exc import NoResultFound
+
+from app.api.bootstrap import api
+from app.api.helpers.db import safe_query
+from app.api.helpers.exceptions import ForbiddenException, ConflictException
+from app.api.helpers.permission_manager import has_access
+from app.api.helpers.permissions import jwt_required
+from app.models import db
+from app.models.event import Event
+from app.api.helpers.utilities import dasherize, require_relationship
+from app.models.stripe_authorization import StripeAuthorization
+
+
+class StripeAuthorizationSchema(Schema):
+    """
+        Stripe Authorization Schema
+    """
+
+    class Meta:
+        """
+        Meta class for StripeAuthorization Api Schema
+        """
+        type_ = 'stripe-authorization'
+        self_view = 'v1.stripe_authorization_detail'
+        self_view_kwargs = {'id': '<id>'}
+        inflect = dasherize
+
+    id = fields.Str(dump_only=True)
+    stripe_secret_key = fields.Str(required=True)
+    stripe_refresh_token = fields.Str(required=True)
+    stripe_publishable_key = fields.Str(required=True)
+    stripe_user_id = fields.Str(required=True)
+    stripe_email = fields.Str(required=True)
+
+    event = Relationship(self_view='v1.stripe_authorization_event',
+                         self_view_kwargs={'id': '<id>'},
+                         related_view='v1.event_detail',
+                         related_view_kwargs={'id': '<id>'},
+                         schema="EventSchema",
+                         type_='event')
+
+
+class StripeAuthorizationListPost(ResourceList):
+    """
+        List and Create Stripe Authorization
+    """
+    def before_post(self, args, kwargs, data):
+        require_relationship(['event'], data)
+        if not has_access('is_organizer', event_id=data['event']):
+            raise ForbiddenException({'source': ''}, "Minimum Organizer access required")
+
+    def before_create_object(self, data, view_kwargs):
+        try:
+            self.session.query(StripeAuthorization).filter_by(event_id=data['event']).one()
+        except NoResultFound:
+            pass
+        else:
+            raise ConflictException({'pointer': '/data/relationships/event'},
+                                    "Stripe Authorization already exists for this event")
+
+    def before_get(self, args, kwargs):
+        if not has_access('is_super_admin'):
+            raise ForbiddenException({'source': ''}, "Super Admin Access Required")
+
+    schema = StripeAuthorizationSchema
+    decorators = (jwt_required, )
+    data_layer = {'session': db.session,
+                  'model': StripeAuthorization}
+
+
+class StripeAuthorizationList(ResourceList):
+    """
+    Stripe Authorization List Resource
+    """
+
+    def query(self, view_kwargs):
+        query_ = self.session.query(StripeAuthorization)
+        if view_kwargs.get('event_id'):
+            event = safe_query(self, Event, 'id', view_kwargs['event_id'], 'event_id')
+            query_ = query_.filter_by(event_id=event.id)
+
+        if view_kwargs.get('event_identifier'):
+            event = safe_query(self, Event, 'identifier', view_kwargs['identifier'], 'event_identifier')
+            query_ = query_.filter_by(event_id=event.id)
+        return query_
+
+    view_kwargs = True
+    methods = ['GET', ]
+    decorators = (api.has_permission('is_organizer', fetch="event_id", fetch_as="event_id"), )
+    schema = StripeAuthorizationSchema
+    data_layer = {'session': db.session,
+                  'model': StripeAuthorization,
+                  'methods': {
+                      'query': query
+                  }}
+
+
+class StripeAuthorizationDetail(ResourceDetail):
+    """
+    Stripe Authorization Detail Resource by ID
+    """
+
+    decorators = (api.has_permission('is_coorganizer', fetch="event_id",
+                                     fetch_as="event_id", model=StripeAuthorization),)
+    schema = StripeAuthorizationSchema
+    data_layer = {'session': db.session,
+                  'model': StripeAuthorization}
+
+
+class StripeAuthorizationRelationship(ResourceDetail):
+    """
+    Stripe Authorization Relationship
+    """
+
+    decorators = (api.has_permission('is_coorganizer', fetch="event_id",
+                                     fetch_as="event_id", model=StripeAuthorization),)
+    schema = StripeAuthorizationSchema
+    data_layer = {'session': db.session,
+                  'model': StripeAuthorization}
diff --git a/app/factories/stripe_authorization.py b/app/factories/stripe_authorization.py
new file mode 100644
--- /dev/null
+++ b/app/factories/stripe_authorization.py
@@ -0,0 +1,20 @@
+import factory
+
+from app.factories.event import EventFactoryBasic
+from app.models import db
+import app.factories.common as common
+from app.models.stripe_authorization import StripeAuthorization
+
+
+class StripeAuthorizationFactory(factory.alchemy.SQLAlchemyModelFactory):
+    class Meta:
+        model = StripeAuthorization
+        sqlalchemy_session = db.session
+
+    event = factory.RelatedFactory(EventFactoryBasic)
+    stripe_secret_key = common.string_
+    stripe_refresh_token = common.string_
+    stripe_publishable_key = common.string_
+    stripe_user_id = common.string_
+    stripe_email = common.email_
+    event_id = 1


Test patch:
diff --git a/tests/hook_main.py b/tests/hook_main.py
--- a/tests/hook_main.py
+++ b/tests/hook_main.py
@@ -47,6 +47,8 @@
 from app.factories.user_permission import UserPermissionFactory
 from app.factories.email_notification import EmailNotificationFactory
 from app.factories.activities import ActivityFactory
+from app.factories.stripe_authorization import StripeAuthorizationFactory
+
 
 stash = {}
 api_username = "open_event_test_user@fossasia.org"
@@ -2272,3 +2274,69 @@ def user_permission_delete(transaction):
         user_permission = UserPermissionFactory()
         db.session.add(user_permission)
         db.session.commit()
+
+
+# ------------------------- Stripe Authorizations -------------------------
+@hooks.before("StripeAuthorization > StripeAuthorization Collection > List all Stripe Authorizations")
+def stripe_authorization_get_list(transaction):
+    """
+    GET /events/1/stripe
+    :param transaction:
+    :return:
+    """
+    with stash['app'].app_context():
+        stripe = StripeAuthorizationFactory()
+        db.session.add(stripe)
+        db.session.commit()
+
+
+@hooks.before("StripeAuthorization > StripeAuthorization Collection > Create Stripe Authorization")
+def stripe_authorization_post(transaction):
+    """
+    POST /events/1/stripe
+    :param transaction:
+    :return:
+    """
+    with stash['app'].app_context():
+        event = EventFactoryBasic()
+        db.session.add(event)
+        db.session.commit()
+
+
+@hooks.before("StripeAuthorization > Stripe Authorization Details > Get Stripe Authorization")
+def stripe_authorization_get_detail(transaction):
+    """
+    GET /stripe-authorization/1
+    :param transaction:
+    :return:
+    """
+    with stash['app'].app_context():
+        stripe = StripeAuthorizationFactory()
+        db.session.add(stripe)
+        db.session.commit()
+
+
+@hooks.before("StripeAuthorization > Stripe Authorization Details > Update Stripe Authorization")
+def stripe_authorization_patch(transaction):
+    """
+    PATCH /stripe-authorization/1
+    :param transaction:
+    :return:
+    """
+    with stash['app'].app_context():
+        stripe = StripeAuthorizationFactory()
+        db.session.add(stripe)
+        db.session.commit()
+
+
+@hooks.before("StripeAuthorization > Stripe Authorization Details > Delete Stripe Authorization")
+def stripe_authorization_delete(transaction):
+    """
+    DELETE /stripe-authorization/1
+    :param transaction:
+    :return:
+    """
+    with stash['app'].app_context():
+        stripe = StripeAuthorizationFactory()
+        db.session.add(stripe)
+        db.session.commit()
diff --git a/tests/unittests/api/helpers/test_db.py b/tests/unittests/api/helpers/test_db.py
--- a/tests/unittests/api/helpers/test_db.py
+++ b/tests/unittests/api/helpers/test_db.py
@@ -1,6 +1,8 @@
 import unittest
 
 from app import current_app as app
+from app.factories.attendee import AttendeeFactory
+from app.models.ticket_holder import TicketHolder
 from tests.unittests.utils import OpenEventTestCase
 from app.factories.event import EventFactoryBasic
 from app.api.helpers.db import save_to_db, safe_query, get_or_create, get_count
@@ -47,9 +49,9 @@ def test_get_or_create(self):
 
     def test_get_count(self):
         with app.test_request_context():
-            event = EventFactoryBasic()
-            save_to_db(event)
-            self.assertEqual(get_count(Event.query), 1)
+            attendee = AttendeeFactory()
+            save_to_db(attendee)
+            self.assertEqual(get_count(TicketHolder.query), 1)
 
 if __name__ == '__main__':
     unittest.main()