-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit d685d59
Showing
8 changed files
with
251 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
settings.py | ||
.idea/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
import urllib3 | ||
import json | ||
|
||
from settings import CONFIG | ||
|
||
|
||
class Payload: | ||
def __init__(self, username, event): | ||
self.username = username | ||
self.embeds = [] | ||
self.event = event | ||
self.data = {} | ||
|
||
if event.is_handled(): | ||
self.embeds.append(make_embed(event.title(), event.url(), event.description(), CONFIG["colorNotification"], event.change_details())) | ||
self.content = make_content(event.title(), event.url(), event.description(), event.change_details()) | ||
else: | ||
warning = f"Warning: `{event.event_name}` webhook event is not handled in Jira-to-Discord AWS Lambda" | ||
self.embeds.append(make_embed(warning, '', '', CONFIG["colorWarning"], "Auto-generated by AWS lambda function")) | ||
self.content = make_content(warning, '', '', "Auto-generated by AWS lambda function") | ||
|
||
self.__set_data() | ||
|
||
def __set_data(self): | ||
self.data = {'username': self.username} | ||
if CONFIG["useEmbed"]: | ||
self.data['embeds'] = self.embeds | ||
if CONFIG["useContent"]: | ||
self.data['content'] = self.content | ||
|
||
|
||
def make_embed(title, url, description, color, footer_text): | ||
return { | ||
"title": title, | ||
"url": url, | ||
"description": description, | ||
"color": color, | ||
"footer": { | ||
"text": footer_text | ||
} | ||
} | ||
|
||
def make_content(title, url, description, footer_text): | ||
content = f"**[{title}]({url})**\n" | ||
if description is not None: content += f"> {description}\n" | ||
content += footer_text | ||
return content |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,111 @@ | ||
from settings import CONFIG | ||
|
||
|
||
class JiraEvent: | ||
def __init__(self, root_json): | ||
self.raw_data = root_json | ||
self.event_name = self.get_event_name() | ||
self.target = self.event_name.split(" ")[0] | ||
self.verb = self.event_name.split(" ", 1)[1] | ||
|
||
self.user = None | ||
self.comment = None | ||
self.ticket = None | ||
self.changelog = None | ||
|
||
self.__populate_conditional_fields() | ||
|
||
def get_event_name(self): | ||
# If this is an Issue Event, get the more specific name | ||
if 'issue_event_type_name' in self.raw_data: | ||
raw_name = self.raw_data['issue_event_type_name'] | ||
|
||
# If this is a generic event, figure out what it should actually be called | ||
if raw_name == 'issue_generic' and 'changelog' in self.raw_data: | ||
raw_name = 'issue_status_changed' | ||
else: | ||
raw_name = self.raw_data['webhookEvent'] | ||
|
||
# Now make it pretty | ||
return raw_name.replace("_", " ").title() | ||
|
||
def is_handled(self): | ||
return self.event_name in CONFIG["handledEvents"] | ||
|
||
def get_actor(self): | ||
if self.user: | ||
return self.user | ||
if self.comment: | ||
return self.comment.update_author | ||
elif self.ticket: | ||
return self.ticket.assignee | ||
|
||
def title(self): | ||
return f'{self.ticket.key}: {self.ticket.name}' | ||
|
||
def url(self): | ||
if self.target in ['Issue', 'Comment']: | ||
return f'{CONFIG["jiraRootUrl"]}{self.ticket.key}' | ||
else: | ||
return '' | ||
|
||
def description(self): | ||
if self.target == 'Issue': | ||
# Issue created shouldn't have a description, the title is enough | ||
if self.verb == 'Created': | ||
return None | ||
else: | ||
return f'{self.changelog.changed_field.title()} changed from {self.changelog.from_value} to {self.changelog.to_value}' | ||
|
||
if self.target == 'Comment': | ||
# Strikethrough if the comment was deleted | ||
if self.verb == 'Deleted': | ||
return f'~~{self.comment.body}~~' | ||
else: | ||
return self.comment.body | ||
|
||
def change_details(self): | ||
return f'{self.target} {self.verb} by {self.get_actor().name}' | ||
|
||
def __populate_conditional_fields(self): | ||
if 'issue' in self.raw_data: | ||
self.ticket = JiraTicket(self.raw_data['issue']) | ||
if 'comment' in self.raw_data: | ||
self.comment = JiraComment(self.raw_data['comment']) | ||
if 'changelog' in self.raw_data: | ||
self.changelog = JiraChangelog(self.raw_data['changelog']) | ||
if 'user' in self.raw_data: | ||
self.user = JiraUser(self.raw_data['user']) | ||
|
||
|
||
class JiraChangelog: | ||
def __init__(self, changelog_json): | ||
self.raw_data = changelog_json | ||
self.changed_field = changelog_json['items'][0]['field'] | ||
self.from_value = changelog_json['items'][0]['fromString'] | ||
self.to_value = changelog_json['items'][0]['toString'] | ||
|
||
|
||
class JiraTicket: | ||
def __init__(self, ticket_json): | ||
self.raw_data = ticket_json | ||
self.key = ticket_json['key'] | ||
self.name = ticket_json['fields']['summary'] | ||
self.type = ticket_json['fields']['issuetype']['name'] | ||
self.assignee = JiraUser(ticket_json['fields']['assignee']) | ||
self.priority = ticket_json['fields']['priority']['name'] | ||
self.status = ticket_json['fields']['status']['name'] | ||
|
||
|
||
class JiraComment: | ||
def __init__(self, comment_json): | ||
self.raw_data = comment_json | ||
self.author = JiraUser(comment_json['author']) | ||
self.update_author = JiraUser(comment_json['updateAuthor']) | ||
self.body = comment_json['body'] | ||
|
||
|
||
class JiraUser: | ||
def __init__(self, user_json): | ||
self.raw_data = user_json | ||
self.name = user_json['displayName'] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
import json | ||
import urllib3 | ||
|
||
from discord import Payload | ||
from jira import JiraEvent | ||
from settings import CONFIG | ||
|
||
|
||
def lambda_handler(event): | ||
http = urllib3.PoolManager() | ||
e = JiraEvent(event) | ||
payload = Payload('JIRA', e) | ||
url = CONFIG["discordWarningWebhookUrl"] if not e.is_handled() else CONFIG["discordWebhookUrl"] | ||
|
||
r = http.request( | ||
'POST', | ||
'' if CONFIG["doNotSend"] else url, | ||
headers={"Content-type": "application/json"}, | ||
body=json.dumps(payload.data).encode("utf-8")) | ||
|
||
return { | ||
"isBase64Encoded": False, | ||
"statusCode": r.status, | ||
"headers": {"headerName": "headerValue"}, | ||
"body": r.data.decode("utf-8") | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
An AWS lambda function that handles requests generated from Jira events, puts them into a shape Discord webhooks can understand, and forwards them to a Discord server. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
urllib3 | ||
requests==2.23.0 | ||
# pytest==5.4.1 # Uncomment this if you want to run tests |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
CONFIG = { | ||
'jiraRootUrl': "", # The root url of your Jira organization, e.g. https://developerorganizations.atlassian.net/browse | ||
'discordWarningWebhookUrl': "", # The webhook url for integration failures (should be things not yet implemented), e.g. https://discordapp.com/api/webhooks/999999999999999999/ABCdefghijklmonpqrstu-vwxyz12345667810111213141516171819202122212400 | ||
'discordWebhookUrl': "", # The webhook url for jira notifications, e.g. https://discordapp.com/api/webhooks/999999999999999999/ABCdefghijklmonpqrstu-vwxyz12345667810111213141516171819202122212400 | ||
'colorNotification': 4245067, # The color you want the embeds for notifications to be | ||
'colorWarning': 12268107, # The color you want the embeds for warnings to be | ||
'useEmbed': True, # If you would like notifications to show up as embeds | ||
'useContent': False, # If you would like notifications to show up as content (basically a normal post); this looks worse but if helpful because embeds don't show up if your personal settings disable link unfolding for some reason | ||
'doNotSend': False, # If you don't want anything to actually send to the webhook, just makes it easier to turn it off for whatever reason if you don't want to disable the webhook in Jira | ||
# These are the events I've set up for this to handle so far | ||
'handledEvents': ['Issue Created', | ||
'Issue Updated', | ||
'Issue Assigned', | ||
'Issue Status Changed', | ||
'Comment Created', | ||
'Comment Updated', | ||
'Comment Deleted'] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
import lambda_function | ||
import json | ||
|
||
ISSUE_CREATED = """ Copy the raw json sent from Jira here """ | ||
ISSUE_ASSIGNED = """ Copy the raw json sent from Jira here """ | ||
ISSUE_STATUS_CHANGED = """ Copy the raw json sent from Jira here """ | ||
ISSUE_LINK_CREATED = """ Copy the raw json sent from Jira here """ | ||
COMMENT_CREATED = """ Copy the raw json sent from Jira here """ | ||
COMMENT_UPDATED = """ Copy the raw json sent from Jira here """ | ||
COMMENT_DELETED = """ Copy the raw json sent from Jira here """ | ||
|
||
ACTUALLY_HIT_DISCORD = True | ||
|
||
def escape_chars(string): | ||
return string.replace('\r','\\r').replace('\n','\\n') | ||
|
||
def json_string_to_obj(string): | ||
return json.loads(escape_chars(string)) | ||
|
||
def test_lambda_issue_created(): | ||
lambda_function.lambda_handler(json_string_to_obj(ISSUE_CREATED)) | ||
x = 0 | ||
|
||
def test_lambda_issue_assigned(): | ||
lambda_function.lambda_handler(json_string_to_obj(ISSUE_ASSIGNED)) | ||
x = 0 | ||
|
||
def test_lambda_comment_created(): | ||
lambda_function.lambda_handler(json_string_to_obj(COMMENT_CREATED)) | ||
x = 0 | ||
|
||
def test_lambda_comment_updated(): | ||
lambda_function.lambda_handler(json_string_to_obj(COMMENT_UPDATED)) | ||
x = 0 | ||
|
||
def test_lambda_comment_deleted(): | ||
lambda_function.lambda_handler(json_string_to_obj(COMMENT_DELETED)) | ||
x = 0 | ||
|
||
def test_lambda_status_changed(): | ||
lambda_function.lambda_handler(json_string_to_obj(ISSUE_STATUS_CHANGED)) | ||
x = 0 | ||
|