From 8456ebc5f9fc0a5756cc9af3eb133871e121bb67 Mon Sep 17 00:00:00 2001 From: Sambhav Kothari Date: Sun, 18 Dec 2016 01:45:42 +0530 Subject: [PATCH 1/8] Add support for JIRA --- botbot_plugins/plugins/jira.py | 69 ++++++++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100644 botbot_plugins/plugins/jira.py diff --git a/botbot_plugins/plugins/jira.py b/botbot_plugins/plugins/jira.py new file mode 100644 index 0000000..a034c01 --- /dev/null +++ b/botbot_plugins/plugins/jira.py @@ -0,0 +1,69 @@ +import requests +import json +from urlparse import urljoin +from .. import config +from ..base import BasePlugin +from ..decorators import listens_to_all, listens_to_mentions + + + +class Config(config.BaseConfig): + jira_link = config.Field(help_text="Jira Link, eg: 'https://tickets.metabrainz.org'", default="https://tickets.metabrainz.org") + rest_api_suffix = config.Field(help_text="Suffix for the Jira REST API, eg: 'rest/api/2/project'", default="rest/api/2/project") + +class Plugin(BasePlugin): + """ + Jira issue lookup + + Returns the description of a Jira Issue + + jira:{{projectname}}-{{issuenumber}} + """ + config_class = Config + + @listens_to_all(ur'(?:.*)\b(?P\w+)-(?P\d+)\b(?:.*)') + def issue_lookup(self, line, project, issue): + """Lookup a specified jira issue + + Usage: + Just mention the issue by its {{ISSUENAME}} + Eg: + Can you please checkup on PROJECT-123 + """ + + api_url = urljoin(self.config['jira_link'], self.config['rest_api_suffix']) + projects = json.loads(self.retrieve('projects')) + if project.upper() in projects: + + issue_url = urljoin(api_url,"issue/{}-{}".format(project.upper(),(issue))) + response = requests.get(issue_url) + if response.status_code == 200: + response_text = json.loads(response.text) + name = response_text['key'] + desc = response_text['fields']['summary'] + return_url = urljoin(self.config['jira_link'],"projects/{}/issues/{}".format(project,name)) + return "{}: {}\n{}".format(name,desc,return_url) + else: + return "Th' servers be not reachable matey, give a go' again later" + + @listens_to_mentions(ur'UPDATE:JIRA') + def update_projects(self, line): + """Updates projects list + + Usage: + Ping the botbot with the command: + UPDATE:JIRA + """ + + api_url = urljoin(self.config['jira_link'], self.config['rest_api_suffix']) + project_url = urljoin(api_url, 'project') + response = requests.get(project_url) + if response.status_code == 200: + projects = [project['key'] for project in json.loads(response.text)] + self.store('projects', json.dumps(projects)) + return "Successfully updated projects list" + return "Could not update projects list" + + + + From 269b484dd8ffba0f1c936cb19697d39e6e99dff5 Mon Sep 17 00:00:00 2001 From: samj1912 Date: Mon, 19 Dec 2016 20:48:53 +0530 Subject: [PATCH 2/8] Update jira.py --- botbot_plugins/plugins/jira.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/botbot_plugins/plugins/jira.py b/botbot_plugins/plugins/jira.py index a034c01..9d5789f 100644 --- a/botbot_plugins/plugins/jira.py +++ b/botbot_plugins/plugins/jira.py @@ -8,7 +8,7 @@ class Config(config.BaseConfig): - jira_link = config.Field(help_text="Jira Link, eg: 'https://tickets.metabrainz.org'", default="https://tickets.metabrainz.org") + jira_link = config.Field(help_text="Jira Link, eg: 'https://tickets.metabrainz.org'") rest_api_suffix = config.Field(help_text="Suffix for the Jira REST API, eg: 'rest/api/2/project'", default="rest/api/2/project") class Plugin(BasePlugin): @@ -62,8 +62,4 @@ def update_projects(self, line): projects = [project['key'] for project in json.loads(response.text)] self.store('projects', json.dumps(projects)) return "Successfully updated projects list" - return "Could not update projects list" - - - - + return "Could not update projects list" From 28c9f3a9a54ec8a66c7fc944c4dca923c8060af7 Mon Sep 17 00:00:00 2001 From: samj1912 Date: Mon, 19 Dec 2016 21:05:56 +0530 Subject: [PATCH 3/8] Add an init method which fetches projects list --- botbot_plugins/plugins/jira.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/botbot_plugins/plugins/jira.py b/botbot_plugins/plugins/jira.py index 9d5789f..06cc226 100644 --- a/botbot_plugins/plugins/jira.py +++ b/botbot_plugins/plugins/jira.py @@ -20,6 +20,13 @@ class Plugin(BasePlugin): jira:{{projectname}}-{{issuenumber}} """ config_class = Config + + def __init__(self, *args, **kwargs): + """Initializes plugin and fetches projects list"" + + super(Plugin, self).__init__(self, *args, **kwargs) + update_projects(self, None) + @listens_to_all(ur'(?:.*)\b(?P\w+)-(?P\d+)\b(?:.*)') def issue_lookup(self, line, project, issue): From 67df5077d748cd3a5918d5dab7042a0eb2069816 Mon Sep 17 00:00:00 2001 From: Sambhav Kothari Date: Mon, 19 Dec 2016 22:27:37 +0530 Subject: [PATCH 4/8] Fix recursive loop --- botbot_plugins/plugins/jira.py | 28 +++++++++++----------------- 1 file changed, 11 insertions(+), 17 deletions(-) diff --git a/botbot_plugins/plugins/jira.py b/botbot_plugins/plugins/jira.py index 06cc226..11c1543 100644 --- a/botbot_plugins/plugins/jira.py +++ b/botbot_plugins/plugins/jira.py @@ -2,14 +2,15 @@ import json from urlparse import urljoin from .. import config -from ..base import BasePlugin +from ..base import BasePlugin, DummyLine from ..decorators import listens_to_all, listens_to_mentions class Config(config.BaseConfig): - jira_link = config.Field(help_text="Jira Link, eg: 'https://tickets.metabrainz.org'") + jira_url = config.Field(help_text="Jira Link, eg: 'https://tickets.metabrainz.org'") rest_api_suffix = config.Field(help_text="Suffix for the Jira REST API, eg: 'rest/api/2/project'", default="rest/api/2/project") + bot_name = config.Field(help_text="Name of your bot, eg: BrainzBot") class Plugin(BasePlugin): """ @@ -21,13 +22,6 @@ class Plugin(BasePlugin): """ config_class = Config - def __init__(self, *args, **kwargs): - """Initializes plugin and fetches projects list"" - - super(Plugin, self).__init__(self, *args, **kwargs) - update_projects(self, None) - - @listens_to_all(ur'(?:.*)\b(?P\w+)-(?P\d+)\b(?:.*)') def issue_lookup(self, line, project, issue): """Lookup a specified jira issue @@ -38,31 +32,29 @@ def issue_lookup(self, line, project, issue): Can you please checkup on PROJECT-123 """ - api_url = urljoin(self.config['jira_link'], self.config['rest_api_suffix']) + api_url = urljoin(self.config['jira_url'], self.config['rest_api_suffix']) projects = json.loads(self.retrieve('projects')) - if project.upper() in projects: - + if project.upper() in projects and line.user != self.config['bot_name']: issue_url = urljoin(api_url,"issue/{}-{}".format(project.upper(),(issue))) response = requests.get(issue_url) if response.status_code == 200: response_text = json.loads(response.text) name = response_text['key'] desc = response_text['fields']['summary'] - return_url = urljoin(self.config['jira_link'],"projects/{}/issues/{}".format(project,name)) + return_url = urljoin(self.config['jira_url'],"projects/{}/issues/{}".format(project,name)) return "{}: {}\n{}".format(name,desc,return_url) else: return "Th' servers be not reachable matey, give a go' again later" - @listens_to_mentions(ur'UPDATE:JIRA') + @listens_to_mentions(ur'(.*)\bUPDATE:JIRA') def update_projects(self, line): - """Updates projects list + """Updates projects list on mentioning the bot with the command Usage: Ping the botbot with the command: UPDATE:JIRA """ - - api_url = urljoin(self.config['jira_link'], self.config['rest_api_suffix']) + api_url = urljoin(self.config['jira_url'], self.config['rest_api_suffix']) project_url = urljoin(api_url, 'project') response = requests.get(project_url) if response.status_code == 200: @@ -70,3 +62,5 @@ def update_projects(self, line): self.store('projects', json.dumps(projects)) return "Successfully updated projects list" return "Could not update projects list" + + From e6a562052d824c50b6dfbc1eab49d1513caa321d Mon Sep 17 00:00:00 2001 From: Sambhav Kothari Date: Mon, 19 Dec 2016 22:30:14 +0530 Subject: [PATCH 5/8] Fix recursive loop --- botbot_plugins/plugins/jira.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/botbot_plugins/plugins/jira.py b/botbot_plugins/plugins/jira.py index 11c1543..dff00b8 100644 --- a/botbot_plugins/plugins/jira.py +++ b/botbot_plugins/plugins/jira.py @@ -61,6 +61,4 @@ def update_projects(self, line): projects = [project['key'] for project in json.loads(response.text)] self.store('projects', json.dumps(projects)) return "Successfully updated projects list" - return "Could not update projects list" - - + return "Could not update projects list" \ No newline at end of file From 585a79b78b28e0ebf00c6b1c8e141d9ed85fb1ef Mon Sep 17 00:00:00 2001 From: Sambhav Kothari Date: Mon, 19 Dec 2016 23:38:15 +0530 Subject: [PATCH 6/8] Remove error message on unreacheble server --- botbot_plugins/plugins/jira.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/botbot_plugins/plugins/jira.py b/botbot_plugins/plugins/jira.py index dff00b8..45f7500 100644 --- a/botbot_plugins/plugins/jira.py +++ b/botbot_plugins/plugins/jira.py @@ -43,8 +43,6 @@ def issue_lookup(self, line, project, issue): desc = response_text['fields']['summary'] return_url = urljoin(self.config['jira_url'],"projects/{}/issues/{}".format(project,name)) return "{}: {}\n{}".format(name,desc,return_url) - else: - return "Th' servers be not reachable matey, give a go' again later" @listens_to_mentions(ur'(.*)\bUPDATE:JIRA') def update_projects(self, line): From e0a4522373760b3cfb25f58e0d720eeac0f1843a Mon Sep 17 00:00:00 2001 From: Sambhav Kothari Date: Tue, 20 Dec 2016 00:05:54 +0530 Subject: [PATCH 7/8] Add tests for JIRA plugin --- botbot_plugins/tests/test_jira.py | 41 +++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 botbot_plugins/tests/test_jira.py diff --git a/botbot_plugins/tests/test_jira.py b/botbot_plugins/tests/test_jira.py new file mode 100644 index 0000000..d4aba64 --- /dev/null +++ b/botbot_plugins/tests/test_jira.py @@ -0,0 +1,41 @@ +import pytest +import json +from mock import patch, call +import requests +from botbot_plugins.base import DummyApp +from botbot_plugins.plugins import jira + +class FakeProjectResponse(object): + """Dummy response from JIRA""" + status_code = 200 + text = json.dumps([{'key': 'TEST'}]) + +class FakeUserResponse(object): + """Dummy response from JIRA""" + status_code = 200 + text = json.dumps({'key': 'TEST-123', 'fields': {'summary': "Testing JIRA plugin"}}) + +@pytest.fixture +def app(): + dummy_app = DummyApp(test_plugin=jira.Plugin()) + dummy_app.set_config('jira', {'jira_url': 'https://tickets.test.org', 'bot_name': 'testbot'}) + return dummy_app + + +def test_jira(app): + # patch requests.get so we don't need to make a real call to Jira + + # Test projecct retrival + with patch.object(requests, 'get') as mock_get: + mock_get.return_value = FakeProjectResponse() + responses = app.respond("@UPDATE:JIRA") + mock_get.assert_called_with( + 'https://tickets.test.org/rest/api/2/project') + assert responses == ["Successfully updated projects list"] + + with patch.object(requests, 'get') as mock_get: + mock_get.return_value = FakeUserResponse() + responses = app.respond("I just assigned TEST-123 to testuser") + mock_get.assert_called_with( + 'https://tickets.test.org/rest/api/2/issue/TEST-123') + assert responses == ["TEST-123: Testing JIRA plugin\nhttps://tickets.test.org/projects/TEST/issues/TEST-123"] \ No newline at end of file From ae3b4c8eda55fa6f3c2961d7e125d929670330f3 Mon Sep 17 00:00:00 2001 From: Sambhav Kothari Date: Tue, 20 Dec 2016 00:05:54 +0530 Subject: [PATCH 8/8] Add tests for JIRA plugin --- botbot_plugins/tests/test_jira.py | 42 +++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 botbot_plugins/tests/test_jira.py diff --git a/botbot_plugins/tests/test_jira.py b/botbot_plugins/tests/test_jira.py new file mode 100644 index 0000000..5a67a08 --- /dev/null +++ b/botbot_plugins/tests/test_jira.py @@ -0,0 +1,42 @@ +import pytest +import json +from mock import patch, call +import requests +from botbot_plugins.base import DummyApp +from botbot_plugins.plugins import jira + +class FakeProjectResponse(object): + """Dummy response from JIRA""" + status_code = 200 + text = json.dumps([{'key': 'TEST'}]) + +class FakeUserResponse(object): + """Dummy response from JIRA""" + status_code = 200 + text = json.dumps({'key': 'TEST-123', 'fields': {'summary': "Testing JIRA plugin"}}) + +@pytest.fixture +def app(): + dummy_app = DummyApp(test_plugin=jira.Plugin()) + dummy_app.set_config('jira', {'jira_url': 'https://tickets.test.org', 'bot_name': 'testbot'}) + return dummy_app + + +def test_jira(app): + # patch requests.get so we don't need to make a real call to Jira + + # Test project retrival + with patch.object(requests, 'get') as mock_get: + mock_get.return_value = FakeProjectResponse() + responses = app.respond("@UPDATE:JIRA") + mock_get.assert_called_with( + 'https://tickets.test.org/rest/api/2/project') + assert responses == ["Successfully updated projects list"] + + # Test appropriate response + with patch.object(requests, 'get') as mock_get: + mock_get.return_value = FakeUserResponse() + responses = app.respond("I just assigned TEST-123 to testuser") + mock_get.assert_called_with( + 'https://tickets.test.org/rest/api/2/issue/TEST-123') + assert responses == ["TEST-123: Testing JIRA plugin\nhttps://tickets.test.org/projects/TEST/issues/TEST-123"] \ No newline at end of file