Repository: fossasia/open-event-server

Issue:
Separate order and event blueprints
**Describe the bug**
Right now even the custom endpoints of orders and events have been extended from ticket_blueprint.

**Additional context**
On it.


Discussion:

Resolving patch:
diff --git a/app/__init__.py b/app/__init__.py
--- a/app/__init__.py
+++ b/app/__init__.py
@@ -152,6 +152,8 @@ def create_app():
         from app.api.settings import admin_misc_routes
         from app.api.server_version import info_route
         from app.api.custom.orders import ticket_blueprint
+        from app.api.custom.orders import order_blueprint
+        from app.api.custom.invoices import event_blueprint
 
         app.register_blueprint(api_v1)
         app.register_blueprint(event_copy)
@@ -165,12 +167,14 @@ def create_app():
         app.register_blueprint(attendee_misc_routes)
         app.register_blueprint(order_misc_routes)
         app.register_blueprint(role_invites_misc_routes)
-        app.register_blueprint(ticket_blueprint)
         app.register_blueprint(authorised_blueprint)
         app.register_blueprint(admin_blueprint)
         app.register_blueprint(alipay_blueprint)
         app.register_blueprint(admin_misc_routes)
         app.register_blueprint(info_route)
+        app.register_blueprint(ticket_blueprint)
+        app.register_blueprint(order_blueprint)
+        app.register_blueprint(event_blueprint)
 
         add_engine_pidguard(db.engine)
 
diff --git a/app/api/custom/invoices.py b/app/api/custom/invoices.py
new file mode 100644
--- /dev/null
+++ b/app/api/custom/invoices.py
@@ -0,0 +1,59 @@
+from flask import Blueprint
+from flask_rest_jsonapi.exceptions import ObjectNotFound
+from flask_jwt_extended import current_user, jwt_required
+from sqlalchemy.orm.exc import NoResultFound
+
+
+from app.api.auth import return_file
+from app.api.helpers.errors import ForbiddenError, NotFoundError
+from app.api.helpers.order import create_pdf_tickets_for_holder
+from app.api.helpers.storage import UPLOAD_PATHS, generate_hash
+from app.models.order import Order
+from app.models.event_invoice import EventInvoice
+from app.api.custom.orders import order_blueprint
+
+event_blueprint = Blueprint('event_blueprint', __name__, url_prefix='/v1/events')
+
+
+@event_blueprint.route('/invoices/<string:invoice_identifier>')
+@jwt_required
+def event_invoices(invoice_identifier):
+    if not current_user:
+        return ForbiddenError({'source': ''}, 'Authentication Required to access Invoice').respond()
+    try:
+        event_invoice = EventInvoice.query.filter_by(identifier=invoice_identifier).first()
+        event_id = event_invoice.event_id
+    except NoResultFound:
+        return NotFoundError({'source': ''}, 'Event Invoice not found').respond()
+    if not current_user.is_organizer(event_id) and not current_user.is_staff:
+        return ForbiddenError({'source': ''}, 'Unauthorized Access').respond()
+    key = UPLOAD_PATHS['pdf']['event_invoices'].format(identifier=invoice_identifier)
+    file_path = '../generated/invoices/{}/{}/'.format(key, generate_hash(key)) + invoice_identifier + '.pdf'
+    try:
+        return return_file('event-invoice', file_path, invoice_identifier)
+    except FileNotFoundError:
+        raise ObjectNotFound({'source': ''},
+                             "The Event Invoice isn't available at the moment. \
+                             Invoices are usually issued on the 1st of every month")
+
+
+@order_blueprint.route('/invoices/<string:order_identifier>')
+@jwt_required
+def order_invoices(order_identifier):
+    if current_user:
+        try:
+            order = Order.query.filter_by(identifier=order_identifier).first()
+        except NoResultFound:
+            return NotFoundError({'source': ''}, 'Order Invoice not found').respond()
+        if current_user.can_download_tickets(order):
+            key = UPLOAD_PATHS['pdf']['order'].format(identifier=order_identifier)
+            file_path = '../generated/invoices/{}/{}/'.format(key, generate_hash(key)) + order_identifier + '.pdf'
+            try:
+                return return_file('invoice', file_path, order_identifier)
+            except FileNotFoundError:
+                create_pdf_tickets_for_holder(order)
+                return return_file('invoice', file_path, order_identifier)
+        else:
+            return ForbiddenError({'source': ''}, 'Unauthorized Access').respond()
+    else:
+        return ForbiddenError({'source': ''}, 'Authentication Required to access Invoice').respond()
diff --git a/app/api/custom/orders.py b/app/api/custom/orders.py
--- a/app/api/custom/orders.py
+++ b/app/api/custom/orders.py
@@ -1,8 +1,7 @@
 from flask import Blueprint, jsonify, request
-from flask_rest_jsonapi.exceptions import ObjectNotFound
 from flask_jwt_extended import current_user, jwt_required
-from sqlalchemy.orm.exc import NoResultFound
 from flask_limiter.util import get_remote_address
+from sqlalchemy.orm.exc import NoResultFound
 
 
 from app import limiter
@@ -10,20 +9,21 @@
 from app.api.auth import return_file
 from app.api.helpers.db import safe_query
 from app.api.helpers.mail import send_email_to_attendees
-from app.api.helpers.errors import ForbiddenError, NotFoundError, UnprocessableEntityError
-from app.api.helpers.order import create_pdf_tickets_for_holder, calculate_order_amount
+from app.api.helpers.errors import ForbiddenError, UnprocessableEntityError, NotFoundError
+from app.api.helpers.order import calculate_order_amount, create_pdf_tickets_for_holder
 from app.api.helpers.storage import UPLOAD_PATHS
 from app.api.helpers.storage import generate_hash
 from app.api.helpers.ticketing import TicketingManager
 from app.api.helpers.permission_manager import has_access
-from app.models.event_invoice import EventInvoice
 from app.models.discount_code import DiscountCode
 from app.models.order import Order
 
-ticket_blueprint = Blueprint('ticket_blueprint', __name__, url_prefix='/v1')
+order_blueprint = Blueprint('order_blueprint', __name__, url_prefix='/v1/orders')
+ticket_blueprint = Blueprint('ticket_blueprint', __name__, url_prefix='/v1/tickets')
 
 
-@ticket_blueprint.route('/tickets/<string:order_identifier>')
+@ticket_blueprint.route('/<string:order_identifier>')
+@order_blueprint.route('/<string:order_identifier>/tickets-pdf')
 @jwt_required
 def ticket_attendee_authorized(order_identifier):
     if current_user:
@@ -45,51 +45,7 @@ def ticket_attendee_authorized(order_identifier):
         return ForbiddenError({'source': ''}, 'Authentication Required to access ticket').respond()
 
 
-@ticket_blueprint.route('/orders/invoices/<string:order_identifier>')
-@jwt_required
-def order_invoices(order_identifier):
-    if current_user:
-        try:
-            order = Order.query.filter_by(identifier=order_identifier).first()
-        except NoResultFound:
-            return NotFoundError({'source': ''}, 'Order Invoice not found').respond()
-        if current_user.can_download_tickets(order):
-            key = UPLOAD_PATHS['pdf']['order'].format(identifier=order_identifier)
-            file_path = '../generated/invoices/{}/{}/'.format(key, generate_hash(key)) + order_identifier + '.pdf'
-            try:
-                return return_file('invoice', file_path, order_identifier)
-            except FileNotFoundError:
-                create_pdf_tickets_for_holder(order)
-                return return_file('invoice', file_path, order_identifier)
-        else:
-            return ForbiddenError({'source': ''}, 'Unauthorized Access').respond()
-    else:
-        return ForbiddenError({'source': ''}, 'Authentication Required to access Invoice').respond()
-
-
-@ticket_blueprint.route('/events/invoices/<string:invoice_identifier>')
-@jwt_required
-def event_invoices(invoice_identifier):
-    if not current_user:
-        return ForbiddenError({'source': ''}, 'Authentication Required to access Invoice').respond()
-    try:
-        event_invoice = EventInvoice.query.filter_by(identifier=invoice_identifier).first()
-        event_id = event_invoice.event_id
-    except NoResultFound:
-        return NotFoundError({'source': ''}, 'Event Invoice not found').respond()
-    if not current_user.is_organizer(event_id) and not current_user.is_staff:
-        return ForbiddenError({'source': ''}, 'Unauthorized Access').respond()
-    key = UPLOAD_PATHS['pdf']['event_invoices'].format(identifier=invoice_identifier)
-    file_path = '../generated/invoices/{}/{}/'.format(key, generate_hash(key)) + invoice_identifier + '.pdf'
-    try:
-        return return_file('event-invoice', file_path, invoice_identifier)
-    except FileNotFoundError:
-        raise ObjectNotFound({'source': ''},
-                             "The Event Invoice isn't available at the moment. \
-                             Invoices are usually issued on the 1st of every month")
-
-
-@ticket_blueprint.route('/orders/resend-email', methods=['POST'])
+@order_blueprint.route('/resend-email', methods=['POST'])
 @limiter.limit(
     '5/minute', key_func=lambda: request.json['data']['user'], error_message='Limit for this action exceeded'
 )
@@ -124,7 +80,7 @@ def resend_emails():
         return ForbiddenError({'source': ''}, "Co-Organizer Access Required").respond()
 
 
-@ticket_blueprint.route('/orders/calculate-amount', methods=['POST'])
+@order_blueprint.route('/calculate-amount', methods=['POST'])
 @jwt_required
 def calculate_amount():
     data = request.get_json()


Test patch: