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

Implementing Endpoint to Resend Email Verification

Earlier, when a user registered via Open Event Frontend, s/he received a verification link via email to confirm their account. However, this was not enough in the long-term. If the confirmation link expired, or for some reasons the verification mail got deleted on the user side, there was no functionality to resend the verification email, which prevented the user from getting fully registered. Although the front-end already showed the option to resend the verification link, there was no support from the server to do that, yet. So it was decided that a separate endpoint should be implemented to allow re-sending the verification link to a user. /resend-verification-email was an endpoint that would fit this action. So we decided to go with it and create a route in `auth.py` file, which was the appropriate place for this feature to reside. First step was to do the necessary imports and then definition: from app.api.helpers.mail import send_email_confirmation from app.models.mail import USER_REGISTER_WITH_PASSWORD ... ... @auth_routes.route('/resend-verification-email', methods=['POST']) def resend_verification_email(): ... Now we safely fetch the email mentioned in the request and then search the database for the user corresponding to that email: def resend_verification_email(): try: email = request.json['data']['email'] except TypeError: return BadRequestError({'source': ''}, 'Bad Request Error').respond() try: user = User.query.filter_by(email=email).one() except NoResultFound: return UnprocessableEntityError( {'source': ''}, 'User with email: ' + email + ' not found.').respond() else: ... Once a user has been identified in the database, we proceed further and create an essentially unique hash for the user verification. This hash is in turn used to generate a verification link that is then ready to be sent via email to the user: else: serializer = get_serializer() hash_ = str(base64.b64encode(str(serializer.dumps( [user.email, str_generator()])).encode()), 'utf-8') link = make_frontend_url( '/email/verify'.format(id=user.id), {'token': hash_}) Finally, the email is sent: send_email_with_action( user, USER_REGISTER_WITH_PASSWORD, app_name=get_settings()['app_name'], email=user.email) if not send_email_confirmation(user.email, link): return make_response(jsonify(message="Some error occured"), 500) return make_response(jsonify(message="Verification email resent"), 200) But this was not enough. When the endpoint was tested, it was found that actual emails were not being delivered, even after correctly configuring the email settings locally. So, after a bit of debugging, it was found that the settings, which were using Sendgrid to send emails, were using a deprecated Sendgrid API endpoint. A separate email function is used to send emails via Sendgrid and it contained an old endpoint that was no longer recommended by Sendgrid: @celery.task(name='send.email.post') def send_email_task(payload, headers): requests.post( "https://api.sendgrid.com/api/mail.send.json", data=payload, headers=headers ) The new endpoint, as per Sendgrid’s documentation, is: https://api.sendgrid.com/v3/mail/send But this was not the only change required. Sendgrid had also modified the structure of requests they accepted, and the new structure was different from the existing one that was used in the server. Following is the new structure: '{"personalizations": [{"to": [{"email": "example@example.com"}]}],"from": {"email": "example@example.com"},"subject": "Hello, World!","content": [{"type": "text/plain", "value": "Heya!"}]}' The header structure was also changed, so the structure in the server was also updated to headers = { "Authorization": ("Bearer " + key), "Content-Type": "application/json" } The Sendgrid function (which is executed as a Celery task) was modified as follows, to incorporate the changes…

Continue ReadingImplementing Endpoint to Resend Email Verification

Implement Email in Open Event Server

In FOSSASIA’s Open Event Server project, we send out emails when various different actions are performed using the API. For example, when a new user is created, he/she receives an email welcoming him to the server as well as an email verification email. Users get role invites from event organisers in the form of emails, when someone buys a ticket he/she gets a PDF link to the ticket as email. So as you can understand all the important informations that are necessary to be notified to the user are sent as an email to the user and sometimes to the organizer as well. In FOSSASIA, we use sendgrid’s API or an SMTP server depending on the admin settings for sending emails. You can read more about how we use sendgrid’s API to send emails in FOSSASIA here. Now let’s dive into the modules that we have for sending the emails. The three main parts in the entire email sending are: Model - Storing the Various Actions Templates - Storing the HTML templates for the emails Email Functions - Individual functions for various different actions Let’s go through each of these modules one by one. Model USER_REGISTER = 'User Registration' USER_CONFIRM = 'User Confirmation' USER_CHANGE_EMAIL = "User email" INVITE_PAPERS = 'Invitation For Papers' NEXT_EVENT = 'Next Event' NEW_SESSION = 'New Session Proposal' PASSWORD_RESET = 'Reset Password' PASSWORD_CHANGE = 'Change Password' EVENT_ROLE = 'Event Role Invitation' SESSION_ACCEPT_REJECT = 'Session Accept or Reject' SESSION_SCHEDULE = 'Session Schedule Change' EVENT_PUBLISH = 'Event Published' AFTER_EVENT = 'After Event' USER_REGISTER_WITH_PASSWORD = 'User Registration during Payment' TICKET_PURCHASED = 'Ticket(s) Purchased' In the Model file, named as mail.py, we firstly declare the various different actions for which we send the emails out. These actions are globally used as the keys in the other modules of the email sending service. Here, we define global variables with the name of the action as strings in them. These are all constant variables, which means that there value remains throughout and never changes. For example, USER_REGISTER has the value ‘User Registration’, which essentially means that anything related to the USER_REGISTER key is executed when the User Registration action occurs. Or in other words, whenever an user registers into the system by signing up or creating a new user through the API, he/she receives the corresponding emails. Apart from this, we have the model class which defines a table in the database. We use this model class to store the actions performed while sending emails in the database. So we store the action, the time at which the email was sent, the recipient and the sender. That way we have a record about all the emails that were sent out via our server. class Mail(db.Model): __tablename__ = 'mails' id = db.Column(db.Integer, primary_key=True) recipient = db.Column(db.String) time = db.Column(db.DateTime(timezone=True)) action = db.Column(db.String) subject = db.Column(db.String) message = db.Column(db.String) def __init__(self, recipient=None, time=None, action=None, subject=None, message=None): self.recipient = recipient self.time = time if self.time is None: self.time = datetime.now(pytz.utc) self.action = action self.subject = subject…

Continue ReadingImplement Email in Open Event Server

How Meilix Generator sends Email Notifications with SendGrid

We wanted to notify the users once the build was ready for download. To solve this we attempted making an email server on Meilix Generator but that can send email when it starts but it would take around 20 minutes to get the build ready so we thought of checking the deploy link status and send email whenever the link status was available (200) but the problem with this method was that the link can be pre available if ISO is rebuilt for same event. Then, we attempted sending mail by Travis CI but the problem in that was closed SMTP ports (they have a strict policy about that) then we thought that Travis CI can trigger the Sendgrid which can send email to the user with the help of API. We will use this code so that once the deployment of ISO by Travis CI is done it can execute the email script which requests Sendgrid to send email to the user. after_deploy: - ./mail.py   We can create code using code generation service of Sendgrid we are going to choose python as it is easier to manipulate strings in python and we are going to use email as an environment variable. After generation of python 3 code from the sendgrid website we are going to edit the message and email and hide the API key as an environment variable and create an authorization string to be used there too. The URL will be generated by the below script as the body of url remains same only two things will change the TRAVIS_TAG which is event name and date. date = datetime.datetime.now().strftime('%Y%m%d') url="https://github.com/xeon-zolt/meilix/releases/download/"+os.environ["TRAVIS_TAG"]+"/meilix-zesty-"+date+"-i386.iso"   We can use this to hide the api key and use it as an environment variable because if the api key is visible in logs anyone can use it to exploit it and use it for spamming purpose. authorization = "Bearer " + os.environ["mail_api_key"] headers = { 'authorization': authorization,   The main thing left to edit in the script is the message which is in the payload and is a string type so we are going to use the email received by Meilix generator as an environment variable and concatenate it with the payload string the message sent is in the value which is in the HTML format and we add the generated URL in similar way we added email variable to string. payload = "{\"personalizations\":[{\"to\":[{\"email\":\"" + os.environ["email"] + "\"}],\"subject\":\"Your ISO is Ready\"}],\"from\":{\"email\":\"xeon.harsh@gmail.com\",\"name\":\"Meilix Generator\"},\"reply_to\":{\"email\":\"xeon.harsh@gmail.com\",\"name\":\"Meilix Generator\"},\"subject\":\"Your ISO is ready\",\"content\":[{\"type\":\"text/html\",\"value\":\"<html><p>Hi,<br>Your ISO is ready<br>URL : "+url+"<br><br>Thank You,<br>Meilix Generator Team</p></html>\"}]}"   The sent email looks like this References SendGrid Documentation

Continue ReadingHow Meilix Generator sends Email Notifications with SendGrid

Sending mails using Sendgrid on Nodejs

The open-event webapp generator project needs to send an email to the user notifying him whenever generating the webapp is finished, containing the links to the preview and zip download. For sending emails, the easiest service we found we could use was SendGrid  which provides upto 15000 free emails a month for students who have a Github Education Pack. (It anyway provides 10000 free emails to all users). To use sendgrid, it's best to use the sendgrid npm module that SendGrid officially builds. To get that installed just use the following command - npm install --save sendgrid Also, once you have made an account on Sendgrid, create an API key, and save it as an environment variable (so that your API key is not exposed in your code). For example in our project, we save it in the environment variable SENDGRID_API_KEY To make it permanent you can add it to your ~/.profile file export SEDGRID_API_KEY=xxxxxxxxxxxxxxxxxxx The actual sending takes place in the mailer.js script in our project. Basically we are using the mail helper class provided in the sendgrid module, and the bare minimum code required to send a mail is as follows var helper = require('sendgrid').mail from_email = new helper.Email('test@example.com') to_email = new helper.Email('test@example.com') subject = 'Hello World from the SendGrid Node.js Library!' content = new helper.Content('text/plain', 'Hello, Email!') mail = new helper.Mail(from_email, subject, to_email, content) var sg = require('sendgrid')(process.env.SENDGRID_API_KEY); var request = sg.emptyRequest({ method: 'POST', path: '/v3/mail/send', body: mail.toJSON() }); sg.API(request, function(error, response) { console.log(response.statusCode) console.log(response.body) console.log(response.headers) }) You need to replace the to and from emails to your requirements. Also as you can see in our project's code, if you want to send HTML formatted data, you can change the content type from text/plain to text/html and then add any html content (as a string) into the content.

Continue ReadingSending mails using Sendgrid on Nodejs

Sending Email using Sendgrid API

sendgrid1

One of the important features while creating server side code for some website/ web application is sending emails. But how do we send emails. There are different ways and packages using which you can setup SMTP ports and send emails. So why specifically sendgrid? Because along with SMTP relay, sendgrid also allows you to send emails using its Web API which makes work much easier. Here, we will discuss about using Web API.

(more…)

Continue ReadingSending Email using Sendgrid API