Skip to content

Commit

Permalink
Merge pull request #15 from shaunburdick/handle-jira-formatting
Browse files Browse the repository at this point in the history
Handle jira formatting
  • Loading branch information
shaunburdick committed Apr 22, 2016
2 parents 6105867 + 57649f2 commit 067aea9
Show file tree
Hide file tree
Showing 4 changed files with 183 additions and 6 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# Slack Bot for JIRA
[![Build Status](https://travis-ci.org/shaunburdick/slack-jirabot.svg)](https://travis-ci.org/shaunburdick/slack-jirabot) [![Coverage Status](https://coveralls.io/repos/shaunburdick/slack-jirabot/badge.svg?branch=master&service=github)](https://coveralls.io/github/shaunburdick/slack-jirabot?branch=master)
[![Build Status](https://travis-ci.org/shaunburdick/slack-jirabot.svg)](https://travis-ci.org/shaunburdick/slack-jirabot) [![Coverage Status](https://coveralls.io/repos/shaunburdick/slack-jirabot/badge.svg?branch=master&service=github)](https://coveralls.io/github/shaunburdick/slack-jirabot?branch=master) [![Docker Pulls](https://img.shields.io/docker/pulls/shaunburdick/slack-jirabot.svg?maxAge=2592000)](https://hub.docker.com/r/shaunburdick/slack-jirabot/)

This slack bot will listen on any channel it's on for JIRA tickets. It will lookup the ticket and respond with some information about it.

Expand Down Expand Up @@ -48,7 +48,7 @@ The config file should be filled out as follows:
## Docker
Build an image using `docker build -t your_image:tag`

Official Image [shaunburdick/slack-jirabot](https://registry.hub.docker.com/u/shaunburdick/slack-jirabot/)
Official Image [shaunburdick/slack-jirabot](https://hub.docker.com/r/shaunburdick/slack-jirabot/)

### Configuration Environment Variables
You can set the configuration of the bot by using environment variables. _ENVIRONMENT_VARIABLE_=Default Value
Expand Down
84 changes: 83 additions & 1 deletion lib/bot.js
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,89 @@ class Bot {
*/
formatIssueDescription(description) {
const desc = description || 'Ticket does not contain a description';
return desc.replace(/\{(quote|code)\}/g, '```');
let depths = [];
let lastDepth = 0;
return desc
// Quotes
.replace(/\{(quote)\}/g, '```')

// Stolen from: https://github.com/kylefarris/J2M
// Un-ordered Lists
.replace(/^[ \t]*(\*+)\s+/gm, (match, stars) => `${Array(stars.length).join(' ')}• `)

// Ordered lists
.replace(/^[ \t]*(#+)\s+/gm, (match, nums) => {
const curDepth = nums.length - 1;
if (curDepth === lastDepth) {
depths[curDepth] = depths[curDepth] !== undefined ? depths[curDepth] + 1 : 1;
} else if (curDepth > lastDepth) {
depths[curDepth] = 1;
} else if (curDepth < lastDepth) {
depths = depths.slice(0, curDepth + 1);
depths[curDepth]++;
}

lastDepth = curDepth;
return `${Array(curDepth + 1).join(' ')}${depths[curDepth]}. `;
})

// Headers 1-6
.replace(/^h([0-6])\.(\s*)(.*)$/gm,
(match, level, spacing, content) => `\n${spacing}*${content}*\n`)

// Bold
.replace(/\*(\s*)(\S.*?\S)(\s*)\*/g, '$1*$2*$3')

// Italic
.replace(/_(\s*)(\S.*?\S)(\s*)_/g, '$1_$2_$3')

// Monospaced text
.replace(/\{\{([^}]+)\}\}/g, '`$1`')

// Citations
.replace(/\?\?((?:.[^?]|[^?].)+)\?\?/g, '_-- $1_')

// Inserts (ignored as unsupported)
// .replace(/\+([^+]*)\+/g, '<ins>$1</ins>')

// Superscript
.replace(/\^([^^]*)\^/g, '^$1')

// Subscript
.replace(/~([^~]*)~/g, '_$1')

// Strikethrough
.replace(/\-(\s*)(\S.*?\S)(\s*)\-/g, '$1~$2~$3')

// Code Block
.replace(/\{code(:([a-z]+))?\}([^]*)\{code\}/gm, '```$2$3```')

// Pre-formatted text
.replace(/{noformat}/g, '```')

// Un-named Links
.replace(/\[([^|]+)\]/g, '<$1>')

// Named Links
.replace(/\[(.+?)\|(.+)\]/g, '<$2|$1>')

// Single Paragraph Blockquote
.replace(/^bq\.\s+/gm, '> ')

// Remove color: unsupported in md
.replace(/\{color:[^}]+\}([^]*)\{color\}/gm, '$1')

// panel into table
.replace(/\{panel:title=([^}]*)\}\n?([^]*?)\n?\{panel\}/gm, '\n| $1 |\n| --- |\n| $2 |')

// table header
.replace(/^[ \t]*((?:\|\|.*?)+\|\|)[ \t]*$/gm, (match, headers) => {
const singleBarred = headers.replace(/\|\|/g, '|');
return `\n${singleBarred}\n${singleBarred.replace(/\|[^|]+/g, '| --- ')}`;
})

// remove leading-space of table headers and rows
.replace(/^[ \t]*\|/gm, '|');
}

/**
Expand Down
5 changes: 2 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "slack-jirabot",
"version": "2.1.0",
"version": "2.2.0",
"description": "Slackbot for interacting with JIRA",
"main": "app.js",
"private": true,
Expand All @@ -23,14 +23,13 @@
},
"dependencies": {
"botkit": "^0.1.1",
"jira-client": "^3.0.1",
"jira-client": "^3.0.2",
"moment": "^2.10.3",
"redact-object": "^1.0.1",
"winston": "^2.1.1"
},
"devDependencies": {
"babel-eslint": "^6.0.3",
"blue-tape": "^0.2.0",
"coveralls": "^2.11.9",
"eslint": "^2.8.0",
"eslint-config-airbnb": "^7.0.0",
Expand Down
96 changes: 96 additions & 0 deletions test/bot.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -254,3 +254,99 @@ test('Bot: show minimal response', (assert) => {
assert.equal(response.fields.length, 0, 'No fields should be provided in minimal response');
assert.end();
});

test('Bot: show minimal response', (assert) => {
const issue = {
key: 'TEST-1',
fields: {
created: '2015-05-01T00:00:00.000',
updated: '2015-05-01T00:01:00.000',
summary: 'Blarty',
description: 'h1. Heading\nFoo foo _foo_ foo foo foo\n' +
'* Bulleted List\n** Indented more\n* Indented less\n\n' +
'# Numbered List\n' +
'## Indented more\n' +
'## Indented more\n' +
'### Indented morer\n' +
'### Indented morer\n' +
'### Indented morer\n' +
'## Indented more\n' +
'# Indented less\n\n' +
'||heading 1||heading 2||\n' +
'|col A1|col B1|\n|col A2|col B2|\n\n' +
'Bold: *boldy*\n' +
'Bold (spaced): * boldy is spaced *\n' +
'Italic: _italicy_\n' +
'Italic (spaced): _italicy is poorly spaced _\n' +
'Monospace: {{$code}}\n' +
'Citations: ??citation??\n' +
'Subscript: ~subscript~\n' +
'Superscript: ^superscript^\n' +
'Strikethrough: -strikethrough-\n' +
'Strikethrough (spaced): - strikethrough is poorly spaced-\n' +
'Code: {code}some code{code}\n' +
'Quote: {quote}quoted text{quote}\n' +
'No Format: {noformat}pre text{noformat}\n' +
'Unnamed Link: [http://someurl.com]\n' +
'Named Link: [Someurl|http://someurl.com]\n' +
'Blockquote: \nbq. This is quoted\n' +
'Color: {color:white}This is white text{color}\n' +
'Panel: {panel:title=foo}Panel Contents{panel}\n',
status: {
name: 'Open',
},
priority: {
name: 'Low',
},
reporter: {
name: 'bob',
displayName: 'Bob',
},
assignee: {
name: 'fred',
displayName: 'Fred',
},
customfield_10000: 'Fizz',
customfield_10001: [
{ value: 'Buzz' },
],
},
};

const expectedText = '\n *Heading*\n\nFoo foo _foo_ foo foo foo\n' +
'• Bulleted List\n • Indented more\n• Indented less\n\n' +
'1. Numbered List\n' +
' 1. Indented more\n' +
' 2. Indented more\n' +
' 1. Indented morer\n' +
' 2. Indented morer\n' +
' 3. Indented morer\n' +
' 3. Indented more\n' +
'2. Indented less\n\n' +
'\n|heading 1|heading 2|\n' +
'| --- | --- |\n|col A1|col B1|\n|col A2|col B2|\n\n' +
'Bold: *boldy*\n' +
'Bold (spaced): *boldy is spaced* \n' +
'Italic: _italicy_\n' +
'Italic (spaced): _italicy is poorly spaced_ \n' +
'Monospace: `$code`\n' +
'Citations: _-- citation_\n' +
'Subscript: _subscript\n' +
'Superscript: ^superscript\n' +
'Strikethrough: ~strikethrough~\n' +
'Strikethrough (spaced): ~strikethrough is poorly spaced~\n' +
'Code: ```some code```\n' +
'Quote: ```quoted text```\n' +
'No Format: ```pre text```\n' +
'Unnamed Link: <http://someurl.com>\n' +
'Named Link: <http://someurl.com|Someurl>\n' +
'Blockquote: \n> This is quoted\n' +
'Color: This is white text\n' +
'Panel: \n| foo |\n| --- |\n| Panel Contents |\n';

const bot = new Bot(configDist);
const response = bot.issueResponse(issue);

assert.equal(response.text, expectedText, 'Atlassian Markup should be converted to Slack Markup');
assert.end();
});

0 comments on commit 067aea9

Please sign in to comment.