Upgrading Open Event to Use Sendgrid API v3

Sendgrid recently upgraded their web API to send emails, and support for previous versions was deprecated. As a result, Open Event Server’s mail sending tasks were rendered unsuccessful, because the requests they were sending to Sendgrid were not being processed. On top of that, it was also found out later that the existing Sendgrid API key on the development server was expired. This had to be fixed at the earliest because emails are a core part of Open Event functionality. The existing way for emails to be sent via Sendgrid used to hit the endpoint “https://api.sendgrid.com/api/mail.send.json” to send emails. Also, the payload structure was as follows: payload = { 'to': to, 'from': email_from, 'subject': subject, 'html': html } Also, a header  "Authorization": "Bearer " accompanied the above payload. However, Sendgrid changed the payload structure to be of the following format: { "personalizations": [ {"to": [ {"email": "example@example.com"} ] } ], "from": { "email": "example@example.com" }, "subject": "Hello, World!", "content": [ { "type": "text/plain", "value": "Heya!" } ] } Furthermore, the endpoint was changed to be “https://api.sendgrid.com/v3/mail/send”. To incorporate all these changes with the minimum number of modified lines in the codebase, it was required for that the structure change itself happens at a fairly low level. This was because there are lots of features in the server that perform a wide variety of email actions. Thus, it was clear that changing all of them will not be the most efficient thing to do. So the perfect place to implement the API changes was the function send_email() in mail.py, because all other higher-level email functions are built on top of this function. But this was not the only change, because this function itself used another function, called send_email_task() in tasks.py, specifically for sending email via Sendgrid. So, in conclusion, the header modifications were made in send_email() and payload structure as well as endpoint modifications were made within send_email_task(). This brought the server codebase back on track to send emails successfully. Finally, the key for development server was also renewed and added to its settings in the Heroku Postgres database. Screenshots: Resources Implement Email in Open Event Server SendGrid API v3 docs

Continue ReadingUpgrading Open Event to Use Sendgrid API v3

Migrating Event Ratings of Open Event with Stored Procedures

Many developers know about procedural languages and have used them in some form or another, but this is really an unpopular tool, despite its power. There are many advantages (and few disadvantages) of these languages, which we will learn about soon. Having a right amount of database-stored procedure code with the help of these languages can really enhance the speed and responsiveness of an application. This article will teach us how procedural languages can be utilized in database management and how they were used recently for a bug fix in Open Event Server. PostgreSQL, like any other powerful, relational database management system (RDBMS), provides the functionality to create and use stored procedures. Essentially, a stored procedure is database logic code which is saved on the database server. This code can be executed directly in the database, and can (and is!) often used to shift business logic from the application layer of a software to the database layer. This simple shift often has many advantages - including faster execution (as code executes at a lower stack level) and better security. When firing database queries from the application layer (i.e., the code that programmers write for storing programmable objects, performing business logic and so on), it often happens that parameters from the programming language itself are passed in to SQL, which then generates a complete SQL query. For example, here’s how a novice query might look like: import psycopg2 conn = psycopg2.connect(dbname="oevent", user="john", password="start") cur = conn.cursor() name = "Sam" cur.execute("SELECT * FROM users WHERE name='%s'" % name) # DANGEROUS! This is an extremely “exposed” code that can be exploited for malicious access, with a technique called SQL injection. This technique essentially “injects” malicious code via these passed parameters, like the variable name mentioned in the above code. With having stored procedures for business logic, there is no room for SQL injection. They solve this problem by writing the query beforehand, and having the parameterized data as a different entity. The pre-processed query within the corresponding stored procedure now looks like SELECT * FROM users WHERE name=?   The database driver sends the name of this stored procedure (or, in standard parameterised queries, just the query text itself) and a list of parameters, as distinct separate entities in the protocol. More details on how stored procedures enhance security can be found here. After learning so much about the advantages of stored procedures (which are enabled by procedural languages), let’s write one! Postgres supports multiple languages for writing stored procedures; here we will use PL/pgSQL, which is the most popular choice for Postgres. This procedural language, inspired (heavily) by Oracle’s PL/SQL language, looks very similar to SQL. To use this procedural language, we have to first install it. In Postgres, procedural languages are installed per-database, not server-wide. We can use the popular Postgres client psql for this purpose, or simply the createlang command on the command line: $ createlang plpgsql yourdb   Now let’s create a simple procedure that prints the corresponding grades…

Continue ReadingMigrating Event Ratings of Open Event with Stored Procedures

Adding System Messages on Open Event Server

The Open Event Server enables organizers to manage events from concerts to conferences and meetups. 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 System Messages on Open Event Server. The focus is on its Model updation and it’s Schema creation. Model Updation For the System Messages, we’ll make update model as follows Now, let’s try to understand this Schema. In this feature, we are providing Admin the rights to read email and notification formats used in Open Event application. First of all, there is the need to know that it has three columns notification_status, user_control_status and mail_status of type boolean. Next it has action attribute which is of type String. At last, we have hybrid properties email_message and notification_message which will return the format of email and notification respective to the action string. The hybrid properties depends on _email_message method and _notification_message method. These methods reads the MAILS and NOTIFS dictionaries and return there values corresponding to string of action key of corresponding record. Schema Creation For the System Messages, 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 read email and notification formats used in Open Event application. First of all, there is the need to know that it has three boolean properties notification_status, user_control_status and mail_status Next it has action attribute which is of type String and it’s value can be validated to have any one of the list provided in choices. At last, it has the String attributes email_message and notification_message which will return the action formats of email and notification concerning the action string provided. So, we saw how System Messages Schema and Model is created / updated to allow Admin users to read 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 System Messages on Open Event Server

Adding Dredd Tests for Image Sizes on Open Event Flask Server

In this blog, we will talk about how to add dredd hooks for testing the API of Event Image Sizes and Speaker Image Sizes in Open Event Server. The focus is on adding the factory class and dredd hooks of these APIs using factory-boy python library and Dredd API testing framework. Factory Creation For the Event and Speaker Image Sizes, we’ll make our factory classes EventImageSizeFactory  and SpeakerImageSizeFactory as follows Now, let’s try to understand this class. In this class, we are writing the sample data two records of ImageSizes Model, these records corresponds to Event and Speaker Image Sizes. First of all, we inherit class factory.alchemy.SQLAlchemyModelFactory to build our sample data which for Image Sizes. Class Meta has model and sqlalchemy_session attributes. Model tells the factory class of to which model this factory class push the data to database and sqlalchemy_session is assigned with the current database session. Next, we add the attributes according to the model and Schema of Image Sizes. Adding Dredd Hooks For the ImageSizes, we’ll make our dredd hooks as follows Now, let’s try to understand these tests. In this tests, we check the API by matching the response after adding a record in these API to one which is present at API blueprint. First of all, we use decorator @hooks.before which means we first add a record in the database and then match the response we get from API say /v1/event-image-sizes with the response mentioned at Image Size > Event Image Size Details > Get Event Image Size Details in API blueprint. We create an instance of EventImageSizeFactory which is a record of model Image Sizes. This record is then returned as a response of API /v1/event-image-sizes and matches with the blueprint at Image Size > Event Image Size Details > Get Event Image Size Details Similarly, we have added other dredd tests for PATCH method as well. So, we saw how factory-boy python library and Dredd API testing framework helped us in testing the REST APIs on Open Event Server. Resources Documentation | Dredd API testing framework: http://dredd.org/en/latest/ Documentation | Factory-boy: http://factoryboy.readthedocs.io/en/latest/

Continue ReadingAdding Dredd Tests for Image Sizes on Open Event Flask Server

Adding Event Roles Permission API on Open Event Server

The Open Event Server enables organizers to manage events from concerts to conferences and meetups. 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 and updating the events role permissions on Open Event Server. The focus is on Schema creation and it’s API creation. Schema Creation For the Events Role Permission, 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 get and update the permission given to a role concerning a service. First of all, we are provide the four fields in this Schema, which are can_create, can_read, can_update and can_delete which are Boolean. All these fields gives us idea whether a user with a role can create, read, update and delete a service or not respectively in the whole system. Next there is a relationship with role which is one of organizer, coorganizer, track_organizer, moderator, registrar or attendee. Next there is a relationship with service which is one of Track, Microlocation, Session, Speaker or Sponsor. API Creation For the Events Role Permissions, we’ll make our API as follows Now, let’s try to understand this API. In this feature, we are providing Admin the rights to get and update the permission given to a role concerning a service. 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 events role permissions . In EventsRolePermissionList, we are inheriting ResourceList from Flask Rest JSONAPI which will allow us to get all the records for the model Permission. In EventsRolePermissionDetail, we are inheriting ResourceDetail from Flask Rest JSONAPI which will allow us to get and update attributes of a record of model Permission. In EventsRolePermissionRelationship, we are inheriting ResourceRelationship from Flask Rest JSONAPI which will allow us to get and update relationships of a record of model Permission. So, we saw how Events Role Permission Schema and API is created to allow users to get it’s values and Admin users to modify it’s attributes and relationships. Resources Documentation | Marshmallow : https://marshmallow-jsonapi.readthedocs.io/en/latest/ Documentation | Flask Rest JSONAPI : http://flask-rest-jsonapi.readthedocs.io/en/latest/

Continue ReadingAdding Event Roles Permission API on Open Event Server

Building the API of Speaker Image Size on Open Event Server

The Open Event Server enables organizers to manage events from concerts to conferences and meetups. It offers features for events with several tracks and venues.It uses the JSON 1.0 Specification and 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 and updating the Speaker Image Size on Open Event Server. The focus is on its API creation. API Creation For the SpeakerImageSizeDetail, we’ll make our Schema as follows Now, let’s try to understand SpeakerImageSizeDetail. In this feature, we are providing Admin the rights to Get and Update the SpeakerImageSizes kwargs['id'] = 2 states that Image Size model has 2 records and 1st record is used for Event Image Size and 2nd record is used for Speaker Image Size. decorators = (api.has_permission('is_admin', methods="PATCH", id="2"),) states that for Speaker Image Size, Update API is accessible to Admins only. methods = ['GET', 'PATCH'] states that this API provides two methods i.e. GET and PATCH. schema = SpeakerImageSizeSchema states that the schema which is used to return the response is Speaker Image Size Schema. data_layer = {'session': db.session, 'model': ImageSizes} states the session and Model used to fetch the records. Resources Documentation | Marshmallow : https://marshmallow-jsonapi.readthedocs.io/en/latest/ Documentation | Flask Rest JSONAPI : http://flask-rest-jsonapi.readthedocs.io/en/latest/

Continue ReadingBuilding the API of Speaker Image Size on Open Event Server

Badges Search Functionality In Admin

Badgeyay is a badge generator service developed by FOSSASIA community for generating badges for conferences and events. Project is separated into frontend that is designed in EmberJS and backend that is created in Flask. Now The Badges tab in admin shows the total badges that are in the system. Along with different meta attributes like Badge creator name, date and other data. The main problem is that it doesn’t provide a way to search the badges from the admin panel. In this blog post i am going to explain about integrating badge search functionality in admin. Procedure Integrating form element for searching <form class="ui form" {{action 'findBadges' on="submit"}}>                   class="field">                       class="ui action input">                           {{input type="text" autocomplete='off' value=user placeholder="Search Badges..."}}                           class="ui icon button" {{action 'findBadges'}}>class="search icon">                                          </div>               </form>   Main difficulty is to send the data to route components as we can’t pass the props, like we pass in components. The workaround is to send the query as route parameter. findBadges() {     var userTerm = this.get('user');     this.transitionToRoute('admin.badges.list', userTerm);   }   Extracting the query parameter and making request to backend if (params.badge_status === 'all') {     filter.state = 'all';   } else if (params.badge_status === 'created') {     filter.state = 'created';   } else if (params.badge_status === 'deleted') {     filter.state = 'deleted';   } else {     filter.filter = params.badge_status;   }   Creating query in controller to fetch the data for the same. if 'filter' in args.keys():       badges = base_query.filter(User.username.contains(args['filter']))       badges = badges.paginate(page, app.config['POSTS_PER_PAGE'], False)       schema = AdminBadgeSchema(many=True)       return jsonify(schema.dump(badges.items).data)   Resources Flask Sqlalchemy documentation for query - Link Pull Request for the same - Link Ember Guide - Link

Continue ReadingBadges Search Functionality In Admin

Implement Table Sorting In Badgeyay

In this blog post I am going to explain about implementation of inplace table sorting in badgeyay. This is not about just adding the sortable class as described in the semantic docs, but the data inside the table has different characteristics and needs to be sorted in a different manner. Not like the traditional way of comparing strings as it will not be suitable for dates. For creating a custom comparison function for sorting, either we can implement a custom comparator using JQuery or we can use the data values for comparison. The latter option is more preferable as it can be extended  to different columns in the table. Procedure Adding the sortable class in the table, which needs to be sorted. <table class="ui sortable table">   . . . </table>   We need to enable a javascript function when DOM completely gets loaded. <script type="text/javascript">  $('table').tablesort(); </script>   After this we need to create a template helper to return us the time stamp from the UTC formatted DateTime string. The value that will be returned by the helper will be used as the data value for the column entries. import { helper } from '@ember/component/helper'; export function extractTimeStamp(date) { return Math.floor((new Date(date)).getTime() / 100); } export default helper(extractTimeStamp);   The value that will be returned by the helper will be used as data value for comparison by table sorter. <td data-sort-value={{extract-time-stamp user.created_at}}>{{sanitizeDate user.created_at}}</td>   Now we need that certain columns do not sort, as there is no need. Such columns are photoURL, actions etc. These columns should be ignored by the sorter for sorting, so we will add a class to avoid sorting of these columns. <th class="no-sort">User Photo</th> Resources Semantic UI table class - Link Data sorting in the table API - Link Pull Request for the same - Link Template helper guide ember - Link

Continue ReadingImplement Table Sorting In Badgeyay

Forgot Password Service in Badgeyay

Badgeyay is an open source badge generator service for generating badges developed by FOSSASIA community for technical events and conferences. The project is divided into two components mainly frontend and backend. After creating the user registration functionality in application, if the user forgets the credentials for the login, then there must be a way to recreate the credentials using a secure channel. This is only valid for the users signed up through email login as for the case of OAuth they must have access to their ID on respective social platform. The main challenges in resetting password for the user is to provide a secure channel. So the problem can be breakdown into following issues: Creating a token for reset action Sending that token via mail to user Verifying that token on the server and giving access Changing the credentials  of the user Procedure Generating token for the request to change credentials for the user. The token will be an expiry token and will be expired in the mentioned duration. So the token is valid for only a limited period of time and will prevent fraudulent requests. def pwd_reset_token():   data = request.get_json()['data']['attributes']   if 'email' not in data.keys():       print('Email not found')   email = data['email']   user = User.getUser(email=email)   if not user:       return ErrorResponse(UserNotFound().message, 422, {'Content-Type': 'application/json'}).respond()   expire = datetime.datetime.utcnow() + datetime.timedelta(seconds=900)   token = jwt.encode({       'id': user.id,       'exp': expire   }, app.config.get('SECRET_KEY'))   resetObj = ResetPasswordToken(user.id, token.decode('UTF-8'))   resetObj.save_to_db()   return jsonify(TokenSchema().dump(resetObj).data) Model for ResetPasswordToken class ResetPasswordToken(db.Model):   __tablename__ = 'Reset Password Token'   id = db.Column(db.String, primary_key=True)   token = db.Column(db.String, nullable=False)   def __init__(self, uid, token):       self.id = uid       self.token = token   def save_to_db(self):       try:           db.session.add(self)           db.session.commit()       except Exception as e:           db.session.rollback()           db.session.flush()           print(e)   Sending the password reset link via mail to the user. The link will contain the token (expiry token) that will be used to validate the request. For the case we will be using Firebase Cloud functions as an HTTP Trigger. exports.sendResetMail = functions.https.onRequest((req, res) => { let token = req.query['token']; let email = req.query['email']; res.setHeader('Content-Type', 'application/json'); sendResetMail(token, email)   .then(() => {     console.log('Reset mail sent to', email);     res.json({ data: { attributes: { status: 200 }, id: token, type: 'reset-mails' } });     return 0;   })   .catch(err => {     console.error(err);     res.json({ data: { attributes: { status: 500 }, id: token, type: 'reset-mails' } });     return -1;   }); }); function sendResetMail(token, email) { const mailOptions = {   from: `${APP_NAME}<noreply@firebase.com>`,   to: email, }; mailOptions.subject = `Password reset link`; mailOptions.html = '<p>Hey ' + email + '! Here is your password reset <a href=\'' + PASSWORD_RESET_LINK   + token + '\'>Link</a><p>'; return mailTransport.sendMail(mailOptions); }   Verifying the token on the server side to validate the user request def validate_reset_token():   args = request.args   if 'token' in args.keys():       token = args.get('token')   resp = {'id': token}   try:       jwt.decode(token, app.config['SECRET_KEY'])       resp['valid'] = True       return jsonify(ValidTokenSchema().dump(resp).data)   except Exception as e:       resp['valid'] = False       print(e)       return jsonify(ValidTokenSchema().dump(resp).data)   After user has access to change the credentials, then user can send a POST request to backend through a form shown in UI to change its password. def changePwd():…

Continue ReadingForgot Password Service in Badgeyay

Metadata Updation in Badgeyay

Badgeyay is a simple badge generator service to develop badges for technical events and conferences developed by FOSSASIA. Badgeyay is a SPA (Single Page Application) developed in ember, whose backend is in Flask. Now when user logins, he can see an option for user profile, in which all the metadata of its profile can be seen (extracted from Firebase). Now user should be able to change its metadata like profile image and username etc. So we will look how the profile image is being changed and updated in badgeyay. Procedure Create function in frontend to listen for onclick events and initiate a file upload dialog box for selecting an image. We will use document property to initiate a dummy click event, else there will be a button with the text to upload a file and that won’t look consistent as we only need an image and nothing else on the UI. class="ui small circular image profile-image">     "{{user.photoURL}}">     "display: none;" id="profileImageSelector" type="file" onchange={{action "profileImageSelected"}}>     "profile-change" onclick={{action "updateProfileImage"}}>Change </div>   Function to upload file and initiate a dummy click event updateProfileImage() {     // Initate a dummy click event     document.getElementById('profileImageSelector').click();   },   profileImageSelected(event) {     const reader = new FileReader();     const { target } = event;     const { files } = target;     const [file] = files;     const _this = this;     reader.onload = () => {       _this.get('sendProfileImage')(reader.result, file.type.split('/')[1]);     };     reader.readAsDataURL(file);   }   Profile update function in the main controller to call the API endpoint to upload the data to backend. This will send the payload to backend which will later upload the image to cloud storage and save in the link in the database. updateProfileImage(profileImageData, extension) {     const _this = this;     const user = this.get('store').peekAll('user');     user.forEach(user_ => {       _this.set('uid', user_.get('id'));     });     let profileImage = _this.get('store').createRecord('profile-image', {       image   : profileImageData,       uid   : _this.uid,       extension : '.' + extension     });     profileImage.save()       .then(record => {         user.forEach(user_ => {           user_.set('photoURL', record.photoURL);         });       })       .catch(err => {         let userErrors = profileImage.get('errors.user');         if (userErrors !== undefined) {           _this.set('userError', userErrors);         }       });   } Route to update profile image from backend @router.route('/profileImage', methods=['POST']) def update_profile_image():   try:       data = request.get_json()['data']['attributes']   except Exception:       return ErrorResponse(PayloadNotFound().message, 422, {'Content-Type': 'application/json'}).respond()   if not data['image']:       return ErrorResponse(ImageNotFound().message, 422, {'Content-Type': 'application/json'}).respond()   if not data['extension']:       return ErrorResponse(ExtensionNotFound().message, 422, {'Content-Type': 'application/json'}).respond()   uid = data['uid']   image = data['image']   extension = data['extension']   try:       imageName = saveToImage(imageFile=image, extension=extension)   except Exception:       return ErrorResponse(ImageNotFound().message, 422, {'Content-Type': 'application/json'}).respond()   fetch_user, imageLink = update_database(uid, imageName)   return jsonify(UpdateUserSchema().dump(fetch_user).data)   This will first create a temp file with the data URI and them upload that file to cloud storage and generate the link and then update the user in the database. def update_database(uid, imageName):   fetch_user = User.getUser(user_id=uid)   if fetch_user is None:       return ErrorResponse(UserNotFound(uid).message, 422, {'Content-Type': 'application/json'}).respond()   imagePath = os.path.join(app.config.get('BASE_DIR'), 'static', 'uploads', 'image') + '/' + imageName   imageLink = fileUploader(imagePath, 'profile/images/' + imageName)   fetch_user.photoURL = imageLink   fetch_user.save_to_db()   try:       os.unlink(imagePath)   except Exception:       print('Unable to delete the temporary file')   return fetch_user, imageLink   Link to PR - Link Topics Involved Google Cloud Admin Storage SDK Ember data Resources Firebase admin sdk documentation - Link Google Cloud…

Continue ReadingMetadata Updation in Badgeyay