DEV Community 👩‍💻👨‍💻

tportela1
tportela1

Posted on

Don't like JIRA's Mail Handler? Extend it with SES and Lambda.

In my current work environment, Jira administration is handled by a separate team. I had created service accounts to be utilized by the Jira mail handler but then realized the mail handler lacks a lot of functionality! What do you mean you can't set component, issueType, or assignee dynamically?

Hm. That would just not do.

I decided to leverage the JIRA API and Amazon's Simple Email Service, along with Lambda.
alt text

Let's break it down step by step. First is configuring Amazon's Simple Email Service to receive the email and then consequently write it to S3. Note: the only way (currently) to extract the body of an email utilizing SES is to either write it to Simple Notification Service (SNS) or Simple Storage Service (S3). I have another article that lines out how to create this if you need help.

In Lambda, below is my code. I utilized an amazing Atlassian Python library.

import json, requests, email, boto3, urllib, time, os, base64, re
from JiraPy import *

def handler(event, context):

    s3 = boto3.client('s3')
    s3r = boto3.resource('s3')
    outputBucket = "jira"

    bucket = event['Records'][0]['s3']['bucket']['name']
    key = urllib.unquote_plus(event['Records'][0]['s3']['object']['key']).decode('utf8')


    try:
        if not outputBucket:
            global outputBucket
            outputBucket = bucket

        # Use waiter to ensure the file is persisted
        waiter = s3.get_waiter('object_exists')
        waiter.wait(Bucket=bucket, Key=key)
        response = s3r.Bucket(bucket).Object(key)

        # Read the raw text file into a Email Object
        msg = email.message_from_string(response.get()["Body"].read())
        messageTo = msg['To']
        messageFrom = msg['From']
        messageSubject = msg['Subject']
        messageBody = ''
        if msg.is_multipart():
            for part in msg.walk():
                ctype = part.get_content_type()
                cdispo = str(part.get('Content-Disposition'))
                # skip any text/plain (txt) attachments
                if ctype == 'text/plain' and 'attachment' not in cdispo:
                    messageBody = part.get_payload(decode=True)  
                    break
    else:
            messageBody = msg.get_payload(decode=True)

        if len(msg.get_payload()) == 2:
            attachment = msg.get_payload()[1]

        else:
            data = messageSubject.split('|')
            projectKey = data[0]
            issueType = data[1]
            component = data[2]
            summary = data[3]
            print(projectKey, issueType, messageFrom, component, summary)
            createIssue(projectKey, issueType, messageFrom, component, summary, messageBody)

    except Exception as e:
        print(e)
        raise e

The Jira portion of the script looks like:

def createIssue(projectKey, issueType, assignee, component, summary, messageBody):
    if issueType == "Epic":
        jira.issue_create(fields={
        'project': {'key': projectKey},
        'issuetype': {
            "name": issueType},
        'customfield_10605': summary,
        'assignee': {'name': assignee},
        'components': [{'name': component}],
        'summary': summary,
        'description': messageBody,
        })
    else:
        jira.issue_create(fields={
        'project': {'key': projectKey},
        'issuetype': {
            "name": issueType},
        'assignee': {'name': assignee},
        'components': [{'name': component}],
        'summary': summary,
        'description': messageBody,
        })

def updateIssue(issueKey, messageBody):
    jira.issue_add_comment(issueKey, messageBody)

Pretty simple!

Here is a visual representation of what it looks like for the end user.

alt text "Email flow for End User"

Curious to hear your thoughts, hopefully this will be useful to someone else in the same predicament.

Top comments (0)

Timeless DEV post...

How to write a kickass README

Arguably the single most important piece of documentation for any open source project is the README. A good README not only informs people what the project does and who it is for but also how they use and contribute to it.

If you write a README without sufficient explanation of what your project does or how people can use it then it pretty much defeats the purpose of being open source as other developers are less likely to engage with or contribute towards it.