mirror of
https://github.com/zulip/zulip.git
synced 2025-11-01 20:44:04 +00:00
Handle some JIRA formatting and make it like our own
(imported from commit 1e8369067668b0c375f226c1911814f496637d16)
This commit is contained in:
275
zerver/fixtures/jira/jira_commented_markup.json
Normal file
275
zerver/fixtures/jira/jira_commented_markup.json
Normal file
@@ -0,0 +1,275 @@
|
||||
{
|
||||
"webhookEvent": "jira:issue_updated",
|
||||
"user": {
|
||||
"self": "https://zulipp.atlassian.net/rest/api/2/user?username=leo",
|
||||
"name": "leo",
|
||||
"emailAddress": "leo@zulip.com",
|
||||
"avatarUrls": {
|
||||
"16x16": "https://zulipp.atlassian.net/secure/useravatar?size=xsmall&avatarId=10122",
|
||||
"24x24": "https://zulipp.atlassian.net/secure/useravatar?size=small&avatarId=10122",
|
||||
"32x32": "https://zulipp.atlassian.net/secure/useravatar?size=medium&avatarId=10122",
|
||||
"48x48": "https://zulipp.atlassian.net/secure/useravatar?avatarId=10122"
|
||||
},
|
||||
"displayName": "Leonardo Franchi [Administrator]",
|
||||
"active": true
|
||||
},
|
||||
"issue": {
|
||||
"id": "10100",
|
||||
"self": "https://zulipp.atlassian.net/rest/api/2/issue/10100",
|
||||
"key": "TEST-7",
|
||||
"fields": {
|
||||
"summary": "Testing of rich text",
|
||||
"progress": {
|
||||
"progress": 0,
|
||||
"total": 0
|
||||
},
|
||||
"timetracking": {},
|
||||
"issuetype": {
|
||||
"self": "https://zulipp.atlassian.net/rest/api/2/issuetype/2",
|
||||
"id": "2",
|
||||
"description": "A new feature of the product, which has yet to be developed.",
|
||||
"iconUrl": "https://zulipp.atlassian.net/images/icons/issuetypes/newfeature.png",
|
||||
"name": "New Feature",
|
||||
"subtask": false
|
||||
},
|
||||
"timespent": null,
|
||||
"reporter": {
|
||||
"self": "https://zulipp.atlassian.net/rest/api/2/user?username=leo",
|
||||
"name": "leo",
|
||||
"emailAddress": "leo@zulip.com",
|
||||
"avatarUrls": {
|
||||
"16x16": "https://zulipp.atlassian.net/secure/useravatar?size=xsmall&avatarId=10122",
|
||||
"24x24": "https://zulipp.atlassian.net/secure/useravatar?size=small&avatarId=10122",
|
||||
"32x32": "https://zulipp.atlassian.net/secure/useravatar?size=medium&avatarId=10122",
|
||||
"48x48": "https://zulipp.atlassian.net/secure/useravatar?avatarId=10122"
|
||||
},
|
||||
"displayName": "Leonardo Franchi [Administrator]",
|
||||
"active": true
|
||||
},
|
||||
"created": "2013-10-01T16:08:48.446-0400",
|
||||
"updated": "2013-10-01T16:16:47.821-0400",
|
||||
"priority": {
|
||||
"self": "https://zulipp.atlassian.net/rest/api/2/priority/3",
|
||||
"iconUrl": "https://zulipp.atlassian.net/images/icons/priorities/major.png",
|
||||
"name": "Major",
|
||||
"id": "3"
|
||||
},
|
||||
"description": "h1. So this is a heading\r\n\r\nAnd some *bold* **and _bold_?\r\n\r\n{quote}\r\nsome stuff goes here\r\n{quote}",
|
||||
"customfield_10001": null,
|
||||
"customfield_10002": null,
|
||||
"customfield_10003": null,
|
||||
"issuelinks": [],
|
||||
"customfield_10000": null,
|
||||
"subtasks": [],
|
||||
"customfield_10008": null,
|
||||
"customfield_10007": null,
|
||||
"status": {
|
||||
"self": "https://zulipp.atlassian.net/rest/api/2/status/10001",
|
||||
"description": "",
|
||||
"iconUrl": "https://zulipp.atlassian.net/images/icons/statuses/open.png",
|
||||
"name": "To Do",
|
||||
"id": "10001"
|
||||
},
|
||||
"customfield_10006": "7",
|
||||
"labels": [],
|
||||
"workratio": -1,
|
||||
"project": {
|
||||
"self": "https://zulipp.atlassian.net/rest/api/2/project/10000",
|
||||
"id": "10000",
|
||||
"key": "TEST",
|
||||
"name": "TestProject",
|
||||
"avatarUrls": {
|
||||
"16x16": "https://zulipp.atlassian.net/secure/projectavatar?size=xsmall&pid=10000&avatarId=10011",
|
||||
"24x24": "https://zulipp.atlassian.net/secure/projectavatar?size=small&pid=10000&avatarId=10011",
|
||||
"32x32": "https://zulipp.atlassian.net/secure/projectavatar?size=medium&pid=10000&avatarId=10011",
|
||||
"48x48": "https://zulipp.atlassian.net/secure/projectavatar?pid=10000&avatarId=10011"
|
||||
}
|
||||
},
|
||||
"environment": null,
|
||||
"customfield_10014": null,
|
||||
"customfield_10015": null,
|
||||
"lastViewed": "2013-10-01T16:16:48.075-0400",
|
||||
"aggregateprogress": {
|
||||
"progress": 0,
|
||||
"total": 0
|
||||
},
|
||||
"customfield_10012": null,
|
||||
"components": [],
|
||||
"customfield_10013": null,
|
||||
"comment": {
|
||||
"startAt": 0,
|
||||
"maxResults": 3,
|
||||
"total": 3,
|
||||
"comments": [
|
||||
{
|
||||
"self": "https://zulipp.atlassian.net/rest/api/2/issue/10100/comment/10000",
|
||||
"id": "10000",
|
||||
"author": {
|
||||
"self": "https://zulipp.atlassian.net/rest/api/2/user?username=leo",
|
||||
"name": "leo",
|
||||
"emailAddress": "leo@zulip.com",
|
||||
"avatarUrls": {
|
||||
"16x16": "https://zulipp.atlassian.net/secure/useravatar?size=xsmall&avatarId=10122",
|
||||
"24x24": "https://zulipp.atlassian.net/secure/useravatar?size=small&avatarId=10122",
|
||||
"32x32": "https://zulipp.atlassian.net/secure/useravatar?size=medium&avatarId=10122",
|
||||
"48x48": "https://zulipp.atlassian.net/secure/useravatar?avatarId=10122"
|
||||
},
|
||||
"displayName": "Leonardo Franchi [Administrator]",
|
||||
"active": true
|
||||
},
|
||||
"body": "Commenting that *this is important* and also _please italicize me_\r\n\r\nh2. Heading this all the way\r\n\r\nsome quote:\r\n\r\n{quote}\r\noh say can you see...\r\n{quote}",
|
||||
"updateAuthor": {
|
||||
"self": "https://zulipp.atlassian.net/rest/api/2/user?username=leo",
|
||||
"name": "leo",
|
||||
"emailAddress": "leo@zulip.com",
|
||||
"avatarUrls": {
|
||||
"16x16": "https://zulipp.atlassian.net/secure/useravatar?size=xsmall&avatarId=10122",
|
||||
"24x24": "https://zulipp.atlassian.net/secure/useravatar?size=small&avatarId=10122",
|
||||
"32x32": "https://zulipp.atlassian.net/secure/useravatar?size=medium&avatarId=10122",
|
||||
"48x48": "https://zulipp.atlassian.net/secure/useravatar?avatarId=10122"
|
||||
},
|
||||
"displayName": "Leonardo Franchi [Administrator]",
|
||||
"active": true
|
||||
},
|
||||
"created": "2013-10-01T16:11:07.615-0400",
|
||||
"updated": "2013-10-01T16:11:07.615-0400"
|
||||
},
|
||||
{
|
||||
"self": "https://zulipp.atlassian.net/rest/api/2/issue/10100/comment/10001",
|
||||
"id": "10001",
|
||||
"author": {
|
||||
"self": "https://zulipp.atlassian.net/rest/api/2/user?username=leo",
|
||||
"name": "leo",
|
||||
"emailAddress": "leo@zulip.com",
|
||||
"avatarUrls": {
|
||||
"16x16": "https://zulipp.atlassian.net/secure/useravatar?size=xsmall&avatarId=10122",
|
||||
"24x24": "https://zulipp.atlassian.net/secure/useravatar?size=small&avatarId=10122",
|
||||
"32x32": "https://zulipp.atlassian.net/secure/useravatar?size=medium&avatarId=10122",
|
||||
"48x48": "https://zulipp.atlassian.net/secure/useravatar?avatarId=10122"
|
||||
},
|
||||
"displayName": "Leonardo Franchi [Administrator]",
|
||||
"active": true
|
||||
},
|
||||
"body": "*Rich Text* comment, with _italicized_ text,\r\n\r\nand including some\r\n\r\n{quote}\r\nquotations by yours truly\r\n{quote}",
|
||||
"updateAuthor": {
|
||||
"self": "https://zulipp.atlassian.net/rest/api/2/user?username=leo",
|
||||
"name": "leo",
|
||||
"emailAddress": "leo@zulip.com",
|
||||
"avatarUrls": {
|
||||
"16x16": "https://zulipp.atlassian.net/secure/useravatar?size=xsmall&avatarId=10122",
|
||||
"24x24": "https://zulipp.atlassian.net/secure/useravatar?size=small&avatarId=10122",
|
||||
"32x32": "https://zulipp.atlassian.net/secure/useravatar?size=medium&avatarId=10122",
|
||||
"48x48": "https://zulipp.atlassian.net/secure/useravatar?avatarId=10122"
|
||||
},
|
||||
"displayName": "Leonardo Franchi [Administrator]",
|
||||
"active": true
|
||||
},
|
||||
"created": "2013-10-01T16:16:47.821-0400",
|
||||
"updated": "2013-10-01T16:16:47.821-0400"
|
||||
},
|
||||
{
|
||||
"self": "https://zulipp.atlassian.net/rest/api/2/issue/10100/comment/10002",
|
||||
"id": "10002",
|
||||
"author": {
|
||||
"self": "https://zulipp.atlassian.net/rest/api/2/user?username=leo",
|
||||
"name": "leo",
|
||||
"emailAddress": "leo@zulip.com",
|
||||
"avatarUrls": {
|
||||
"16x16": "https://zulipp.atlassian.net/secure/useravatar?size=xsmall&avatarId=10122",
|
||||
"24x24": "https://zulipp.atlassian.net/secure/useravatar?size=small&avatarId=10122",
|
||||
"32x32": "https://zulipp.atlassian.net/secure/useravatar?size=medium&avatarId=10122",
|
||||
"48x48": "https://zulipp.atlassian.net/secure/useravatar?avatarId=10122"
|
||||
},
|
||||
"displayName": "Leonardo Franchi [Administrator]",
|
||||
"active": true
|
||||
},
|
||||
"body": "This is a comment that likes to *exercise* a lot of _different_ {{conventions}} that {{jira uses}}.\r\n\r\n{noformat}\r\nthis code is not highlighted, but monospaced\r\n{noformat}\r\n\r\n{code:python}\r\ndef python():\r\n print \"likes to be formatted\"\r\n{code}\r\n\r\n[http://www.google.com] is a bare link, and [Google|http://www.google.com] is given a title.\r\n\r\nThanks!\r\n\r\n{quote}\r\nSomeone said somewhere\r\n{quote}",
|
||||
"updateAuthor": {
|
||||
"self": "https://zulipp.atlassian.net/rest/api/2/user?username=leo",
|
||||
"name": "leo",
|
||||
"emailAddress": "leo@zulip.com",
|
||||
"avatarUrls": {
|
||||
"16x16": "https://zulipp.atlassian.net/secure/useravatar?size=xsmall&avatarId=10122",
|
||||
"24x24": "https://zulipp.atlassian.net/secure/useravatar?size=small&avatarId=10122",
|
||||
"32x32": "https://zulipp.atlassian.net/secure/useravatar?size=medium&avatarId=10122",
|
||||
"48x48": "https://zulipp.atlassian.net/secure/useravatar?avatarId=10122"
|
||||
},
|
||||
"displayName": "Leonardo Franchi [Administrator]",
|
||||
"active": true
|
||||
},
|
||||
"created": "2013-10-01T17:03:00.964-0400",
|
||||
"updated": "2013-10-01T17:03:00.964-0400"
|
||||
}
|
||||
]
|
||||
},
|
||||
"timeoriginalestimate": null,
|
||||
"customfield_10017": null,
|
||||
"customfield_10016": null,
|
||||
"customfield_10019": null,
|
||||
"customfield_10018": null,
|
||||
"votes": {
|
||||
"self": "https://zulipp.atlassian.net/rest/api/2/issue/TEST-7/votes",
|
||||
"votes": 0,
|
||||
"hasVoted": false
|
||||
},
|
||||
"fixVersions": [],
|
||||
"resolution": null,
|
||||
"resolutiondate": null,
|
||||
"aggregatetimeoriginalestimate": null,
|
||||
"duedate": null,
|
||||
"customfield_10020": null,
|
||||
"customfield_10021": "Not Started",
|
||||
"watches": {
|
||||
"self": "https://zulipp.atlassian.net/rest/api/2/issue/TEST-7/watchers",
|
||||
"watchCount": 1,
|
||||
"isWatching": true
|
||||
},
|
||||
"worklog": {
|
||||
"startAt": 0,
|
||||
"maxResults": 20,
|
||||
"total": 0,
|
||||
"worklogs": []
|
||||
},
|
||||
"assignee": null,
|
||||
"attachment": [],
|
||||
"aggregatetimeestimate": null,
|
||||
"versions": [],
|
||||
"timeestimate": null,
|
||||
"aggregatetimespent": null
|
||||
}
|
||||
},
|
||||
"comment": {
|
||||
"self": "https://zulipp.atlassian.net/rest/api/2/issue/10100/comment/10002",
|
||||
"id": "10002",
|
||||
"author": {
|
||||
"self": "https://zulipp.atlassian.net/rest/api/2/user?username=leo",
|
||||
"name": "leo",
|
||||
"emailAddress": "leo@zulip.com",
|
||||
"avatarUrls": {
|
||||
"16x16": "https://zulipp.atlassian.net/secure/useravatar?size=xsmall&avatarId=10122",
|
||||
"24x24": "https://zulipp.atlassian.net/secure/useravatar?size=small&avatarId=10122",
|
||||
"32x32": "https://zulipp.atlassian.net/secure/useravatar?size=medium&avatarId=10122",
|
||||
"48x48": "https://zulipp.atlassian.net/secure/useravatar?avatarId=10122"
|
||||
},
|
||||
"displayName": "Leonardo Franchi [Administrator]",
|
||||
"active": true
|
||||
},
|
||||
"body": "This is a comment that likes to *exercise* a lot of _different_ {{conventions}} that {{jira uses}}.\r\n\r\n{noformat}\r\nthis code is not highlighted, but monospaced\r\n{noformat}\r\n\r\n{code:python}\r\ndef python():\r\n print \"likes to be formatted\"\r\n{code}\r\n\r\n[http://www.google.com] is a bare link, and [Google|http://www.google.com] is given a title.\r\n\r\nThanks!\r\n\r\n{quote}\r\nSomeone said somewhere\r\n{quote}",
|
||||
"updateAuthor": {
|
||||
"self": "https://zulipp.atlassian.net/rest/api/2/user?username=leo",
|
||||
"name": "leo",
|
||||
"emailAddress": "leo@zulip.com",
|
||||
"avatarUrls": {
|
||||
"16x16": "https://zulipp.atlassian.net/secure/useravatar?size=xsmall&avatarId=10122",
|
||||
"24x24": "https://zulipp.atlassian.net/secure/useravatar?size=small&avatarId=10122",
|
||||
"32x32": "https://zulipp.atlassian.net/secure/useravatar?size=medium&avatarId=10122",
|
||||
"48x48": "https://zulipp.atlassian.net/secure/useravatar?avatarId=10122"
|
||||
},
|
||||
"displayName": "Leonardo Franchi [Administrator]",
|
||||
"active": true
|
||||
},
|
||||
"created": "2013-10-01T17:03:00.964-0400",
|
||||
"updated": "2013-10-01T17:03:00.964-0400"
|
||||
},
|
||||
"timestamp": 1380661380969
|
||||
}
|
||||
@@ -3445,9 +3445,13 @@ class JiraHookTests(AuthedTestCase):
|
||||
self.assertEqual(msg.content, """Leo Franchi **updated** [BUG-15](http://lfranchi.com:8080/browse/BUG-15):
|
||||
|
||||
|
||||
~~~ quote
|
||||
Adding a comment. Oh, what a comment it is!
|
||||
~~~""")
|
||||
""")
|
||||
|
||||
def test_commented_markup(self):
|
||||
msg = self.send_jira_message('commented_markup')
|
||||
self.assertEqual(msg.subject, "TEST-7: Testing of rich text")
|
||||
self.assertEqual(msg.content, """Leonardo Franchi [Administrator] **updated** [TEST-7](https://zulipp.atlassian.net/browse/TEST-7):\n\n\nThis is a comment that likes to **exercise** a lot of _different_ `conventions` that `jira uses`.\r\n\r\n~~~\n\r\nthis code is not highlighted, but monospaced\r\n\n~~~\r\n\r\n~~~\n\r\ndef python():\r\n print "likes to be formatted"\r\n\n~~~\r\n\r\n[http://www.google.com](http://www.google.com) is a bare link, and [Google](http://www.google.com) is given a title.\r\n\r\nThanks!\r\n\r\n~~~ quote\n\r\nSomeone said somewhere\r\n\n~~~\n""")
|
||||
|
||||
def test_deleted(self):
|
||||
msg = self.send_jira_message('deleted')
|
||||
@@ -3469,9 +3473,8 @@ Adding a comment. Oh, what a comment it is!
|
||||
|
||||
* Changed status from **Resolved** to **Reopened**
|
||||
|
||||
~~~ quote
|
||||
Re-opened yeah!
|
||||
~~~""")
|
||||
""")
|
||||
|
||||
def test_resolved(self):
|
||||
msg = self.send_jira_message('resolved')
|
||||
@@ -3482,9 +3485,8 @@ Re-opened yeah!
|
||||
* Changed status from **Open** to **Resolved**
|
||||
* Changed assignee from **None** to **Leo Franchi**
|
||||
|
||||
~~~ quote
|
||||
Fixed it, finally!
|
||||
~~~""")
|
||||
""")
|
||||
|
||||
def test_workflow_postfuncion(self):
|
||||
msg = self.send_jira_message('postfunction_hook')
|
||||
|
||||
@@ -17,6 +17,7 @@ import base64
|
||||
import logging
|
||||
import re
|
||||
import ujson
|
||||
import markdown.inlinepatterns
|
||||
from functools import wraps
|
||||
|
||||
def github_generic_subject(noun, repository, blob):
|
||||
@@ -173,6 +174,42 @@ def elide_subject(subject):
|
||||
subject = subject[:57].rstrip() + '...'
|
||||
return subject
|
||||
|
||||
def convert_jira_markup(content):
|
||||
# Attempt to do some simplistic conversion of JIRA
|
||||
# formatting to Markdown, for consumption in Zulip
|
||||
|
||||
# Jira uses *word* for bold, we use **word**
|
||||
content = re.sub(r'\*([^\*]+)\*', r'**\1**', content)
|
||||
|
||||
# Jira uses {{word}} for monospacing, we use `word`
|
||||
content = re.sub(r'{{([^\*]+?)}}', r'`\1`', content)
|
||||
|
||||
# Starting a line with bq. block quotes that line
|
||||
content = re.sub(r'bq\. (.*)', r'> \1', content)
|
||||
|
||||
# Wrapping a block of code in {quote}stuff{quote} also block-quotes it
|
||||
quote_re = re.compile(r'{quote}(.*?){quote}', re.DOTALL)
|
||||
content = re.sub(quote_re, r'~~~ quote\n\1\n~~~', content)
|
||||
|
||||
# {noformat}stuff{noformat} blocks are just code blocks with no
|
||||
# syntax highlighting
|
||||
noformat_re = re.compile(r'{noformat}(.*?){noformat}', re.DOTALL)
|
||||
content = re.sub(noformat_re, r'~~~\n\1\n~~~', content)
|
||||
|
||||
# Code blocks are delineated by {code[: lang]} {code}
|
||||
code_re = re.compile(r'{code[^\n]*}(.*?){code}', re.DOTALL)
|
||||
content = re.sub(code_re, r'~~~\n\1\n~~~', content)
|
||||
|
||||
# Links are of form: [https://www.google.com] or [Link Title|https://www.google.com]
|
||||
# In order to support both forms, we don't match a | in bare links
|
||||
content = re.sub(r'\[([^\|]+?)\]', r'[\1](\1)', content)
|
||||
|
||||
# Full links which have a | are converted into a better markdown link
|
||||
full_link_re = re.compile(r'\[(?:(?P<title>[^|]+)\|)(?P<url>.*)\]')
|
||||
content = re.sub(full_link_re, r'[\g<title>](\g<url>)', content)
|
||||
|
||||
return content
|
||||
|
||||
@csrf_exempt
|
||||
def api_jira_webhook(request):
|
||||
try:
|
||||
@@ -245,7 +282,8 @@ def api_jira_webhook(request):
|
||||
content += "* Changed %s from **%s** to **%s**\n" % (field, item.get('fromString'), item.get('toString'))
|
||||
|
||||
if comment != '':
|
||||
content += "\n~~~ quote\n%s\n~~~" % (comment,)
|
||||
comment = convert_jira_markup(comment)
|
||||
content += "\n%s\n" % (comment,)
|
||||
elif 'transition' in payload:
|
||||
from_status = get_in(payload, ['transition', 'from_status'])
|
||||
to_status = get_in(payload, ['transition', 'to_status'])
|
||||
|
||||
Reference in New Issue
Block a user