Adding Modules API on Open Event Server

The Open Event Server enables organizers to manage events from concerts to conferences and meet-ups. It offers features for events with several tracks and venues. Event managers can create invitation forms for speakers and build schedules in a drag and drop interface. The event information is stored in a database. The system provides API endpoints to fetch the data, and to modify and update it. The Open Event Server is based on JSON 1.0 Specification and hence build on top of Flask Rest Json API (for building Rest APIs) and Marshmallow (for Schema). In this blog, we will talk about how to add API for accessing the Modules on Open Event Server. The focus is on Schema creation and it’s API creation. Schema Creation For the ModuleSchema, we’ll make our Schema as follows Now, let’s try to understand this Schema. In this feature, we are providing Admin the rights to set whether Admin wants to include tickets, payment and donation in the open event application. First of all, we will provide three fields in this Schema, which are ticket_include, payment_include and donation_include. The very first attribute ticket_include should be Boolean as we want Admin to update it whether he wants to include ticketing system in the application from default one which is False. Next attribute payment_include should be Boolean as we want Admin to update it whether he wants to include payment system in the application from default one which is False. Next attribute donation_include should be Boolean as we want Admin to update it whether he wants to include donation system in the application from default one which is False. API Creation For the ModuleDetail, we’ll make our API as follows Now, let’s try to understand this API. In this API, we are providing Admin the rights to set whether Admin wants to include tickets, payment and donation in the open event application. First of all, there is the need to know that this API has two method GET and PATCH. Decorators shows us that only Admin has permissions to access PATCH method for this API i.e. only Admins can modify the modules . before_get method shows us that this API will give first record of Modules model irrespective of the id requested by user. Schema used here is default one of Modules Hence, GET Request is accessible to all the users. So, we saw how Module Schema and API is created to allow users to get it’s values and Admin users to modify it’s values. Resources Documentation | Marshmallow : https://marshmallow-jsonapi.readthedocs.io/en/latest/ Documentation | Flask Rest JSONAPI : http://flask-rest-jsonapi.readthedocs.io/en/latest/

Continue ReadingAdding Modules API on Open Event Server

Implementing Permissions for Orders API in Open Event API Server

Open Event API Server Orders API is one of the core APIs. The permissions in Orders API are robust and secure enough to ensure no leak on payment and ticketing.The permission manager provides the permissions framework to implement the permissions and proper access controls based on the dev handbook. The following table is the permissions in the developer handbook.   List View Create Update Delete Superadmin/admin ✓ ✓ ✓ ✓ ✓ Event organizer ✓ [1] ✓ [1] ✓ [1] ✓ [1][2] ✓ [1][3] Registered user ✓ [4] Everyone else Only self-owned events Can only change order status A refund will also be initiated if paid ticket Only if order placed by self Super Admins and admins are allowed to create any order with any amount but any coupon they apply is not consumed on creating order. They can update almost every field of the order and can provide any custom status to the order. Permissions are applied with the help of Permission Manager which takes care the authorization roles. For example, if a permission is set based on admin access then it is automatically set for super admin as well i.e., to the people with higher rank. Self-owned events This allows the event admins, Organizer and Co-Organizer to manage the orders of the event they own. This allows then to view all orders and create orders with or without discount coupon with any custom price and update status of orders. Event admins can provide specific status while others cannot if not has_access('is_coorganizer', event_id=data['event']): data['status'] = 'pending' And Listing requires Co-Organizer access elif not has_access('is_coorganizer', event_id=kwargs['event_id']): raise ForbiddenException({'source': ''}, "Co-Organizer Access Required") Can only change order status The organizer cannot change the order fields except the status of the order. Only Server Admin and Super Admins are allowed to update any field of the order. if not has_access('is_admin'): for element in data: if element != 'status': setattr(data, element, getattr(order, element)) And Delete access is prohibited to event admins thus only Server admins can delete orders by providing a cancelling note which will be provided to the Attendee/Buyer. def before_delete_object(self, order, view_kwargs): if not has_access('is_coorganizer', event_id=order.event.id): raise ForbiddenException({'source': ''}, 'Access Forbidden') Registered User A registered user can create order with basic details like the attendees' records and payment method with fields like country and city. They are not allowed to provide any custom status to the order they are creating. All orders will be set by default to “pending” Also, they are not allowed to update any field in their order. Any status update will be done internally thus maintaining the security of Order System. Although they are allowed to view their place orders. This is done by comparing their logged in user id with the user id of the purchaser. if not has_access('is_coorganizer_or_user_itself', event_id=order.event_id, user_id=order.user_id): return ForbiddenException({'source': ''}, 'Access Forbidden') Event Admins The event admins have one more restriction, as an event admin, you cannot provide discount coupon and even if you do it will be ignored. # Apply discount only…

Continue ReadingImplementing Permissions for Orders API in Open Event API Server

Generating Ticket PDFs in Open Event API Server

In the ordering system of Open Event API Server, there is a requirement to send email notifications to the attendees. These attendees receive the URL of the pdf of the generated ticket. On creating the order, first the pdfs are generated and stored in the preferred storage location and then these are sent to the users through the email. Generating PDF is a simple process, using xhtml2pdf we can generate PDFs from the html. The generated pdf is then passed to storage helpers to store it in the desired location and pdf-url is updated in the attendees record. Sample PDF PDF Template The templates are written in HTML which is then converted using the module xhtml2pdf. To store the templates a new directory was created at  app/templates where all HTML files are stored. Now, The template directory needs to be updated at flask initializing app so that template engine can pick the templates from there. So in app/__init__.py we updated flask initialization with template_dir = os.path.dirname(__file__) + "/templates" app = Flask(__name__, static_folder=static_dir, template_folder=template_dir) This allows the template engine to pick the templates files from this template directory. Generating PDFs Generating PDF is done by rendering the html template first. This html content is then parsed into the pdf file = open(dest, "wb") pisa.CreatePDF(cStringIO.StringIO(pdf_data.encode('utf-8')), file) file.close() The generated pdf is stored in the temporary location and then passed to storage helper to upload it. uploaded_file = UploadedFile(dest, filename) upload_path = UPLOAD_PATHS['pdf']['ticket_attendee'].format(identifier=get_file_name()) new_file = upload(uploaded_file, upload_path) This generated pdf path is returned here Rendering HTML and storing PDF for holder in order.ticket_holders:   if holder.id != current_user.id:       pdf = create_save_pdf(render_template('/pdf/ticket_attendee.html', order=order, holder=holder))   else:       pdf = create_save_pdf(render_template('/pdf/ticket_purchaser.html', order=order))   holder.pdf_url = pdf   save_to_db(holder) The html is rendered using flask template engine and passed to create_save_pdf and link is updated on the attendee record. Sending PDF on email These pdfs are sent as a link to the email after creating the order. Thus a ticket is sent to each attendee and a summarized order details with attendees to the purchased. send_email(   to=holder.email,   action=TICKET_PURCHASED_ATTENDEE,   subject=MAILS[TICKET_PURCHASED_ATTENDEE]['subject'].format(       event_name=order.event.name,       invoice_id=order.invoice_number   ),   html= MAILS[TICKET_PURCHASED_ATTENDEE]['message'].format(       pdf_url=holder.pdf_url,       event_name=order.event.name   ) ) References Readme - xhtml2pdf https://github.com/xhtml2pdf/xhtml2pdf/blob/master/README.rst Using xhtml2pdf and create pdfs https://micropyramid.com/blog/generating-pdf-files-in-python-using-xhtml2pdf/  

Continue ReadingGenerating Ticket PDFs in Open Event API Server

Discount Codes in Open Event Server

The Open Event System allows usage of discount codes with tickets and events. This blogpost describes what types of discount codes are present and what endpoints can be used to fetch and update details. In Open Event API Server, each event can have two types of discount codes. One is ‘event’ discount code, while the other is ‘ticket’ discount code. As the name suggests, the event discount code is an event level discount code and the ticket discount code is ticket level. Now each event can have only one ‘event’ discount code and is accessible only to the server admin. The Open Event server admin can create, view and update the ‘event’ discount code for an event. The event discount code followsDiscountCodeEvent Schema. This schema is inherited from the parent class DiscountCodeSchemaPublic. To save the unique discount code associated with an event, the event model’s discount_code_id field is used. The ‘ticket’ discount is accessible by the event organizer and co-organizer. Each event can have any number of ‘ticket’ discount codes. This follows the DiscountCodeTicket schema, which is also inherited from the same base class ofDiscountCodeSchemaPublic. The use of the schema is decided based on the value of the field ‘used_for’ which can have the value either ‘events’ or ‘tickets’. Both the schemas have different relationships with events and marketer respectively. We have the following endpoints for Discount Code events and tickets: '/events/<int:event_id>/discount-code' ‘/events/<int:event_id>/discount-codes’ The first endpoint is based on the DiscountCodeDetail class. It returns the detail of one discount code which in this case is the event discount code associated with the event. The second endpoint is based on the DiscountCodeList class which returns a list of discount codes associated with an event. Note that this list also includes the ‘event’ discount code, apart from all the ticket discount codes. class DiscountCodeFactory(factory.alchemy.SQLAlchemyModelFactory): class Meta: model = DiscountCode sqlalchemy_session = db.session event_id = None user = factory.RelatedFactory(UserFactory) user_id = 1 Since each discount code belongs to an event(either directly or through the ticket), the factory for this has event as related factory, but to check for /events/<int:event_id>/discount-code endpoint we first need the event and then pass the discount code id to be 1 for dredd to check this. Hence, event is not included as a related factory, but added as a different object every time a discount code object is to be used. @hooks.before("Discount Codes > Get Discount Code Detail of an Event > Get Discount Code Detail of an Event") def event_discount_code_get_detail(transaction): """ GET /events/1/discount-code :param transaction: :return: """ with stash['app'].app_context(): discount_code = DiscountCodeFactory() db.session.add(discount_code) db.session.commit() event = EventFactoryBasic(discount_code_id=1) db.session.add(event) db.session.commit() The other tests and extended documentation can be found here. References: How to set up discount codes for one or multiple event -  Antwonne D, [blog] Related Factories in FactoryBoy - Official Docs, FactoryBoy Discount Code related endpoints documentation - enigmaeth[commit]

Continue ReadingDiscount Codes in Open Event Server

User Guide for the PSLab Remote-Access Framework

The remote-lab framework of the pocket science lab has been designed to enable user to access their devices remotely via the internet. The pslab-remote repository includes an API server built with Python-Flask and a webapp that uses EmberJS. This post is a guide for users who wish to test the framework. A series of blog posts have been previously written which have explored and elaborated various aspect of the remote-lab such as designing the API server, remote execution of function strings, automatic deployment on various domains etc. In this post, we shall explore how to execute function strings, execute example scripts, and write a script ourselves. A live demo is hosted at pslab-remote.surge.sh . The API server is hosted at pslab-stage.herokuapp.com, and an API reference which is being developed can be accessed at pslab-stage.herokuapp.com/apidocs . A screencast of the remote lab is also available Create an account Signing up at this point is very straightforward, and does not include any third party verification tools since the framework is under active development, and cannot be claimed to be ready for release yet. Click on the sign-up button, and provide a username, email, and password. The e-mail will be used as the login-id, and needs to be unique. Login to the remote lab Use the email-id used for signing up, enter the password, and the app will redirect you to your new home-page, where you will be greeted with a similar screen. Your home-page On the home-page, you will find that the first section includes a text box for entering a function string, and an execute button. Here, you can enter any valid PSLab function such as `get_resistance()` , and click on the execute button in order to run the function on the PSLab device connected to the API server, and view the results. A detailed blog post on this process can be found here. Since this is a new account, no saved scripts are present in the Your Scripts section. We will come to that shortly, but for now, there are some pre-written example scripts that will let you test them as well as view their source code in order to copy into your own collection, and modify them. Click on the play icon next to `multimeter.py` in order to run the script. The eye icon to the right of the row enables you to view the source code, but this can also be done while the app is running. The multimeter app looks something like this, and you can click on the various buttons to try them out. You may also click on the Source Code tab in order to view the source Create and execute a small python script We can now try to create a simple script of our own. Click on the `New Python Script` button in the top-bar to navigate to a page that will allow you to create and save your own scripts. We shall write a small 3-line code to print some sinusoidal coordinates, save…

Continue ReadingUser Guide for the PSLab Remote-Access Framework

Stripe Authorization In Open Event API Server

The Open Event System supports payments through stripe. Stripe is a suite of APIs that powers commerce for businesses of all sizes. This blogpost covers testing of Stripe Authorization Schema and endpoints in the API Server. The Stripe Authorization class provides the following endpoints: '/stripe-authorization' '/stripe-authorization/<int:id>' '/events/<int:event_id>/stripe-authorization' '/events/<event_identifier>/stripe-authorization' In the pull request made for adding documentation and tests, these two endpoints were removed: 'stripe_authorization_list', '/events/<int:event_id>/stripe-authorization', '/events/<event_identifier>/stripe-authorization' This is because each event can have only one stripe authorization, so there can not exist a list of stripe authorization objects related to an event. The 'stripe_authorization_list' endpoint is made POST only. This is because Open Event does not allow individual resources’ list to be accessible. Since, there is no endpoint which returns a list of Stripe Authorizations the StripeAuthorizationList(ResourceList) is removed. The ResourceDetail class was modified to add a query to support  results from '/events/<int:event_id>/stripe-authorization' endpoint.  suThe view_kwargs for the detail endpoint has to contain the resource id, so event_id from view_kwags is used to get the id for stripe authorization. stripe_authorization = self.session.query(StripeAuthorization).filter_by(event_id=view_kwargs['event_id']).one() view_kwargs['id'] = stripe_authorization.id Writing Test for Documentation (Tests for the /events/1/stripe-authorization  is described here, for others please refer to links in additional references.) To test the  /events/1/stripe-authorization endpoint for GET, we first insert a Stripe Authorization object into the database which will then be retrieved by the GET request and then compared with the expected response body from the documentation file. Since stripe-auth has a required relationship with event class, an event must also exist for strie auth object to be created. The event is also required because the endpoint ‘events/’ expects an event object to exist. The StripeAuthorizationFactory takes care of this with event as a RelatedFactory. So when a StripeAuthorization object is inserted, an event is created first and passed as the required relationship to stripe_list_post endpoint. The event is related to the stripe object by setting event_id = 1 in the factory. Adding the pre-test hook for GET: @hooks.before("StripeAuthorization > Stripe Authorization for an Event > Get Stripe Authorization Details of an Event") def event_stripe_authorization_get_detail(transaction): """ GET /events/1/stripe-authorization :param transaction: :return: """ with stash['app'].app_context(): stripe = StripeAuthorizationFactory() db.session.add(stripe) db.session.commit() The expected response for this request can be found here. Additional References: Stripe API Reference - Official Stripe Documentation Adding payment to your Web applications with Stripe - Hitch, [blog] Writing Dredd Hooks In Python - Python Hooks in Dredd Docs Add stripe-auth related endpoints docs and dredd tests - enigmaeth, [commit]

Continue ReadingStripe Authorization In Open Event API Server

Copying Event in Open Event API Server

The Event Copy feature of Open Event API Server provides the ability to create a xerox copy of event copies with just one API call. This feature creates the complete copy of event by copying the related objects as well like tracks, sponsors, micro-locations, etc. This API is based on the simple method where an object is first removed is from current DB session and then applied make_transient. Next step is to remove the unique identifying columns like “id”, “identifier” and generating the new identifier and saving the new record. The process seems simple but becomes a little complex when you have to generate copies of media files associated and copies of related multiple objects ensuring no orders, attendees, access_codes relations are copied. Initial Step The first thing to copy the event is first to get the event object and all related objects first if view_kwargs.get('identifier').isdigit(): identifier = 'id' event = safe_query(db, Event, identifier, view_kwargs['identifier'], 'event_'+identifier) Next thing is to get all related objects to this event. Creating the new event After removing the current event object from "db.session", It is required to remove “id” attribute and regenerate “identifier” of the event. 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() Updating related object with new event The new event created has new “id” and “identifier”. This new “id” is added into foreign keys columns of the related object thus providing a relationship with the new event 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() Finishing up The last step of Updating related objects is repeated for all related objects to create the copy. Thus a new event is created with all related objects copied with the single endpoint. References How to clone a sqlalchemy object https://stackoverflow.com/questions/28871406/how-to-clone-a-sqlalchemy-db-object-with-new-primary-key

Continue ReadingCopying Event in Open Event API Server

Reset password in Open Event API Server

The addition of reset password API in the Open Event API Server enables the user to send a forgot password request to the server so that user can reset the password. Reset Password API is a two step process. The first endpoint allows you to request a token to reset the password and this token is sent to the user via email. The second process is making a PATCH request with the token and new password to set the new password on user’s account. Creating a Reset token This endpoint is not JSON spec based API. A reset token is simply a hash of random bits which is stored in a specific column of user’s table. hash_ = random.getrandbits(128) self.reset_password = str(hash_) Once the user completed the resetting of the password using the specific token, the old token is flushed and the new token is generated. These tokens are all one time use only. Requesting a Token A token can be requested on a specific endpoint  POST /v1/auth/reset-password The token with the direct link will be sent to registered email. link = make_frontend_url('/reset-password', {'token': user.reset_password}) send_email_with_action(user, PASSWORD_RESET, app_name=get_settings()['app_name'], link=link) Flow with frontend The flow is broken into 2 steps with front end is serving to the backend. The user when click on forget password will be redirected to reset password page in the front end which will call the API endpoint in the backend with an email to send the token. The email received will contain the link for the front end URL which when clicked will redirect the user to the front end page of providing the new password. The new password entered with the token will be sent to API server by the front end and reset password will complete. Updating Password Once clicked on the link in the email, the user will be asked to provide the new password. This password will be sent to the endpoint PATCH /v1/auth/reset-password. The body will receive the token and the new password to update. The user will be identified using the token and password is updated for the respective user. try: user = User.query.filter_by(reset_password=token).one() except NoResultFound: return abort( make_response(jsonify(error="User not found"), 404) ) else: user.password = password save_to_db(user) References Understand Self-service reset password https://en.wikipedia.org/wiki/Self-service_password_reset Python - getrandbits() https://docs.python.org/2/library/random.html

Continue ReadingReset password in Open Event API Server

Post Payment Charging in Open Event API Server

Order flow in Open Event API Server follows very simple process. On successfully creating a order through API server the user receives the payment-url. API server out of the box provides support for two payment gateways, stripe and paypal. The process followed is very simple, on creating the order you will receive the payment-url. The frontend will complete the payment through that url and on completion it will hit the specific endpoint which will confirm the payment and update the order status. Getting the payment-url Payment Url will be sent on successfully creating the order. There are three type of payment-modes which can be provided to Order API on creating order. The three payment modes are “free”, “stripe” and “paypal”. To get the payment-url just send the payment mode as stripe or paypal. POST Payment After payment processing through frontend, the charges endpoint will be called so that payment verification can be done on the server end. POST /v1/orders/<identifier>/charge This endpoint receives the stripe token if the payment mode is stripe else no token is required to process payment for paypal. The response will have the order details on successful verification. Implementation The implementation of charging is based on the custom data layer in Orga Server. The custom layer overrides the Base data layer and provide the custom implementation to “create_object” method thus, not using Alchemy layer. def create_object(self, data, view_kwargs): order = Order.query.filter_by(id=view_kwargs['id']).first() if order.payment_mode == 'stripe': if data.get('stripe') is None: raise UnprocessableEntity({'source': ''}, "stripe token is missing") success, response = TicketingManager.charge_stripe_order_payment(order, data['stripe']) if not success: raise UnprocessableEntity({'source': 'stripe_token_id'}, response) elif order.payment_mode == 'paypal': success, response = TicketingManager.charge_paypal_order_payment(order) if not success: raise UnprocessableEntity({'source': ''}, response) return order With the resource class as class ChargeList(ResourceList): methods = ['POST', ] schema = ChargeSchema data_layer = { 'class': ChargesLayer, 'session': db.session } Resources Paypal Payments API https://developer.paypal.com/docs/api/payments/ Flask-json-api custom layer docs http://flask-rest-jsonapi.readthedocs.io/en/latest/data_layer.html#custom-data-layer Stripe Payments API https://stripe.com/docs/charges

Continue ReadingPost Payment Charging in Open Event API Server

Using Order Endpoints in Open Event API Server

The main feature i.e., Ordering API is added into API server. These endpoints provide the ability to work with the ordering system. This API is not simple like other as it checks for the discount codes and various other things as well. The process in layman terms is very simple, first, a user must be registered or added as an attendee into Server without any order_id associated and then the attendee details will be sent to API server as a relationship. Things needed to take care: Validating the discount code and ensure it is not exhausted Calculating the total amount on the server side by applying coupon Do not calculate amount if the user is the event admin Do not use coupon if user is event admin Handling payment modes and generating payment links Ensure that default status is always pending, unless the user is event admin Creating Order Prerequisite Before initiating the order, attendee records needs to be created associated with the event. These records will not have any order_id associated with them initially. The Order API will add the relationships. Required Body Order API requires you to send event relationship and attendee records to create order_tickets Permissions Only organizers can provide custom amount and status. Others users will get their status as pending and amount will be recalculated in server. The response will reflect the calculated amount and updated status. Also to initiate any order, user must be logged in. Guest can not create any order Payment Modes There are three payment modes, free, stripe and paypal. If payment_mode is not provided then API will consider it as “free”. Discount Codes Discount code can be sent as a relationship to the API. The Server will validate the code and will act accordingly. Validating Discount Codes Discount codes are checked to ensure they are valid, first check ensures that the user is not co-organizer # Apply discount only if the user is not event admin if data.get('discount') and not has_access('is_coorganizer', event_id=data['event']): Second, check ensures that the discount code is active if not discount_code.is_active:   raise UnprocessableEntity({'source': 'discount_code_id'}, "Inactive Discount Code") The third, Check ensures its validity is not expired if not (valid_from <= now <= valid_till):   raise UnprocessableEntity({'source': 'discount_code_id'}, "Inactive Discount Code") Fourth Check ensure that the quantity is not exhausted if not TicketingManager.match_discount_quantity(discount_code, data['ticket_holders']):   raise UnprocessableEntity({'source': 'discount_code_id'}, 'Discount Usage Exceeded') Lastly, the fifth check ensures that event id matches with given discount associated event if discount_code.event.id != data['event'] and discount_code.user_for == TICKET:   raise UnprocessableEntity({'source': 'discount_code_id'}, "Invalid Discount Code") Calculating Order Amount The next important thing is to recalculate the order amount and it will calculated only if user is not the event admin if not has_access('is_coorganizer', **view_kwargs):   TicketingManager.calculate_update_amount(order) API Response The API response apart from general fields will provide you the payment-url depending upon the payment mode you selected. Stripe : will give payment-url as stripe Paypal: will provide the payment completing url in payment-url This all explains the flow and requirements to create an order. Order API consists…

Continue ReadingUsing Order Endpoints in Open Event API Server