Repository: fossasia/open-event-server

Issue:
Change Event Copy API from JSON spec to non-JSON spec
**I'm submitting a ...**  (check one with "x")
- [x] bug report
- [ ] 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

**Current behavior:**
Currently, the POST to event copy is having the request body like:
```
{
  "data": {
    "type": "event-copy"
  }
}
```
which is weird.

Expected:
The request should be a simple POST to ```/v1/events/{identifier}/copy``` without any request body.
Please check the comment here: https://github.com/fossasia/open-event-frontend/issues/742#issuecomment-323535638


Discussion:
@poush @magdalenesuo @shubham-padia @enigmaeth Please someone look into this.
@sumedh123 This API is JSON spec API so the type of object is required to create.
@niranjan94 Please guide.
@poush then please make it a plain json endpoint. It really doesn't make
sense to have a body like this. event-copy isn't actually a resource.

Let's stick to json-api spec for proper resources. And for such actions,
we'll go for a plain json endpoint.

On Sun, Aug 20, 2017, 10:30 AM Sumedh Nimkarde <notifications@github.com>
wrote:

> @niranjan94 <https://github.com/niranjan94> Please guide.
>
> —
> You are receiving this because you were mentioned.
>
>
> Reply to this email directly, view it on GitHub
> <https://github.com/fossasia/open-event-orga-server/issues/4393#issuecomment-323563767>,
> or mute the thread
> <https://github.com/notifications/unsubscribe-auth/ACSwFGCrbWCEID_dq63D7Hps2i-A7Vh-ks5sZ71ngaJpZM4O8ZQN>
> .
>

Okay, sure!

Resolving patch:
diff --git a/app/__init__.py b/app/__init__.py
--- a/app/__init__.py
+++ b/app/__init__.py
@@ -119,8 +119,10 @@ def create_app():
         from app.api.imports import import_routes
         from app.api.celery_tasks import celery_routes
         from app.api.auth import auth_routes
+        from app.api.event_copy import event_copy
 
         app.register_blueprint(api_v1)
+        app.register_blueprint(event_copy)
         app.register_blueprint(upload_routes)
         app.register_blueprint(export_routes)
         app.register_blueprint(import_routes)
diff --git a/app/api/__init__.py b/app/api/__init__.py
--- a/app/api/__init__.py
+++ b/app/api/__init__.py
@@ -223,9 +223,6 @@
 api.route(EventRelationship, 'event_registrars', '/events/<int:id>/relationships/registrars',
           '/events/<identifier>/relationships/registrars')
 
-# Event Copy API
-api.route(EventCopyResource, 'event_copy', '/events/<identifier>/copy')
-
 
 # microlocations
 api.route(MicrolocationListPost, 'microlocation_list_post', '/microlocations')
diff --git a/app/api/event_copy.py b/app/api/event_copy.py
new file mode 100644
--- /dev/null
+++ b/app/api/event_copy.py
@@ -0,0 +1,130 @@
+from flask import jsonify, Blueprint, abort, make_response
+from sqlalchemy.orm import make_transient
+
+from app.api.helpers.db import safe_query
+from app.api.helpers.files import create_save_resized_image
+from app.api.helpers.permission_manager import has_access
+from app.models.custom_form import CustomForms
+from app.models.discount_code import DiscountCode
+from app.models.event import Event, get_new_event_identifier
+from app.models import db
+from app.models.microlocation import Microlocation
+from app.models.social_link import SocialLink
+from app.models.speakers_call import SpeakersCall
+from app.models.sponsor import Sponsor
+from app.models.ticket import Ticket
+from app.models.track import Track
+
+event_copy = Blueprint('event_copy', __name__, url_prefix='/v1/events')
+
+
+@event_copy.route('/<identifier>/copy', methods=['POST'])
+def create_event_copy(identifier):
+    id = 'identifier'
+
+    if identifier.isdigit():
+        id = 'id'
+
+    event = safe_query(db, Event, id, identifier, 'event_' + id)
+
+    if not has_access('is_coorganizer', event_id=event.id):
+        return abort(
+            make_response(jsonify(error="Access Forbidden"), 403)
+        )
+    tickets = Ticket.query.filter_by(event_id=event.id).all()
+    social_links = SocialLink.query.filter_by(event_id=event.id).all()
+    sponsors = Sponsor.query.filter_by(event_id=event.id).all()
+    microlocations = Microlocation.query.filter_by(event_id=event.id).all()
+    tracks = Track.query.filter_by(event_id=event.id).all()
+    custom_forms = CustomForms.query.filter_by(event_id=event.id).all()
+    discount_codes = DiscountCode.query.filter_by(event_id=event.id).all()
+    speaker_calls = SpeakersCall.query.filter_by(event_id=event.id).all()
+
+    db.session.expunge(event)  # expunge the object from session
+    make_transient(event)
+    delattr(event, 'id')
+    event.identifier = get_new_event_identifier()
+    db.session.add(event)
+    db.session.commit()
+
+    # Removes access_codes, order_tickets, ticket_tags for the new tickets created.
+    for ticket in tickets:
+        ticket_id = ticket.id
+        db.session.expunge(ticket)  # expunge the object from session
+        make_transient(ticket)
+        ticket.event_id = event.id
+        delattr(ticket, 'id')
+        db.session.add(ticket)
+        db.session.commit()
+
+    for link in social_links:
+        link_id = link.id
+        db.session.expunge(link)  # expunge the object from session
+        make_transient(link)
+        link.event_id = event.id
+        delattr(link, 'id')
+        db.session.add(link)
+        db.session.commit()
+
+    for sponsor in sponsors:
+        sponsor_id = sponsor.id
+        db.session.expunge(sponsor)  # expunge the object from session
+        make_transient(sponsor)
+        sponsor.event_id = event.id
+        logo_url = create_save_resized_image(image_file=sponsor.logo_url, resize=False)
+        delattr(sponsor, 'id')
+        sponsor.logo_url = logo_url
+        db.session.add(sponsor)
+        db.session.commit()
+
+    for location in microlocations:
+        location_id = location.id
+        db.session.expunge(location)  # expunge the object from session
+        make_transient(location)
+        location.event_id = event.id
+        delattr(location, 'id')
+        db.session.add(location)
+        db.session.commit()
+
+    # No sessions are copied for new tracks
+    for track in tracks:
+        track_id = track.id
+        db.session.expunge(track)  # expunge the object from session
+        make_transient(track)
+        track.event_id = event.id
+        delattr(track, 'id')
+        db.session.add(track)
+        db.session.commit()
+
+    for call in speaker_calls:
+        call_id = call.id
+        db.session.expunge(call)  # expunge the object from session
+        make_transient(call)
+        call.event_id = event.id
+        delattr(call, 'id')
+        db.session.add(call)
+        db.session.commit()
+
+    for code in discount_codes:
+        code_id = code.id
+        db.session.expunge(code)  # expunge the object from session
+        make_transient(code)
+        code.event_id = event.id
+        delattr(code, 'id')
+        db.session.add(code)
+        db.session.commit()
+
+    for form in custom_forms:
+        form_id = form.id
+        db.session.expunge(form)  # expunge the object from session
+        make_transient(form)
+        form.event_id = event.id
+        delattr(form, 'id')
+        db.session.add(form)
+        db.session.commit()
+
+    return jsonify({
+        'id': event.id,
+        'identifier': event.identifier,
+        "copied": True
+    })


Test patch: