From 810a2eff18045d12c74b0997f3e8600f44010cef Mon Sep 17 00:00:00 2001 From: mattdough Date: Sat, 30 Oct 2021 11:13:20 -0700 Subject: [PATCH 01/15] add todos --- .pre-commit-config.yaml | 2 +- actions/actions.py | 11 ++++++++--- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 087d0de..194f33b 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,5 +1,5 @@ repos: - repo: https://github.com/ambv/black - rev: stable + rev: 21.9b0 hooks: - id: black diff --git a/actions/actions.py b/actions/actions.py index 948a015..f4c83ff 100644 --- a/actions/actions.py +++ b/actions/actions.py @@ -12,8 +12,11 @@ vers = "vers: 0.1.0, date: Apr 2, 2020" logger.debug(vers) -snow = SnowAPI() +# TODO read helpdesk service from endpoints.yml localmode = snow.localmode +# TODO instantiante either snow or jira or nothing for local +snow = SnowAPI() + logger.debug(f"Local mode: {snow.localmode}") @@ -28,7 +31,9 @@ def run( domain: Dict[Text, Any], ) -> List[Dict]: if tracker.get_slot("previous_email"): - dispatcher.utter_message(template=f"utter_ask_use_previous_email",) + dispatcher.utter_message( + template=f"utter_ask_use_previous_email", + ) else: dispatcher.utter_message(template=f"utter_ask_email") return [] @@ -177,7 +182,7 @@ def run( domain: Dict[Text, Any], ) -> List[Dict]: """Look up all incidents associated with email address - and return status of each""" + and return status of each""" email = tracker.get_slot("email") From fd2631d2b1502a0774b7ae860a69ba320070a77f Mon Sep 17 00:00:00 2001 From: mattdough Date: Sun, 21 Nov 2021 06:14:45 -0800 Subject: [PATCH 02/15] jira_actions.py contains the functions like snow.py --- actions/actions.py | 1 + actions/jira_actions.py | 109 +++++++++++++++++++++++++++++++++++ actions/jira_credentials.yml | 5 ++ endpoints.yml | 2 + requirements.txt | 5 +- 5 files changed, 120 insertions(+), 2 deletions(-) create mode 100644 actions/jira_actions.py create mode 100644 actions/jira_credentials.yml diff --git a/actions/actions.py b/actions/actions.py index f4c83ff..f52642d 100644 --- a/actions/actions.py +++ b/actions/actions.py @@ -15,6 +15,7 @@ # TODO read helpdesk service from endpoints.yml localmode = snow.localmode # TODO instantiante either snow or jira or nothing for local +# TODO snow = SnowAPI() logger.debug(f"Local mode: {snow.localmode}") diff --git a/actions/jira_actions.py b/actions/jira_actions.py new file mode 100644 index 0000000..8b22711 --- /dev/null +++ b/actions/jira_actions.py @@ -0,0 +1,109 @@ +import logging +import pathlib +import ruamel.yaml +from typing import Dict, Text, Any +from jira import ( + JIRA, +) # requires jira 3.1.0rc1 for the search_user function to work +from jira.resources import Customer, Priority, User + +logger = logging.getLogger(__name__) + +here = pathlib.Path(__file__).parent.absolute() + + +class JiraPy(object): + def __init__(self): + jira_config = ( + ruamel.yaml.safe_load(open(f"{here}/jira_credentials.yml", "r")) + or {} + ) + + self.jira_user = jira_config.get("jira_user") + self.jira_token = jira_config.get("jira_token") + self.jira_url = jira_config.get("jira_url") + self.project = jira_config.get("project") + self.jira_obj = JIRA( + self.jira_url, basic_auth=(self.jira_user, self.jira_token) + ) + + def handle_request(self): + # TODO This might not be needed. In the service now version it isn't called directly by actions + True + + def email_to_sysid(self, email) -> Dict[Text, Any]: + result = {} + email_result = self.jira_obj.search_users( + None, 0, 10, True, False, email + ) + if len(email_result) == 1: + result["account_id"] = vars(email_result[0]).get("accountId") + else: + result["account_id"] = [] + result["error"] = ( + f"Could not retreive account id; " + f"Multiple records found for email {email}" + ) + + return result + + def retrieve_incidents(self, email) -> Dict[Text, Any]: + result = {} + incidents = {} + email_result = self.email_to_sysid(email) + account_id = email_result.get("account_id") + issues = self.jira_obj.search_issues( + f"reporter in ({account_id}) order by created DESC" + ) + if account_id: + for issue in issues: + incidents[issue.key] = issue.fields.summary + elif isinstance(issues, list): + result["error"] = f"No incidents on record for {email}" + + result["account_id"] = account_id + result["incidents"] = incidents + return result + + def create_incident( + self, description, short_description, priority, email + ) -> Dict[Text, Any]: + project = self.project + account_id = self.email_to_sysid(email).get("account_id") + print(account_id) + issue = self.jira_obj.create_issue( + project=project, + summary=short_description, + description=description, + issuetype={"id": "10002"}, + priority={"id": priority}, + reporter={"accountId": account_id}, + ) + return issue + + # TODO need to use this for setting priority + @staticmethod + def priority_db() -> Dict[str, int]: + """Database of supported priorities""" + priorities = {"low": 4, "medium": 3, "high": 2} + return priorities + + +jira = JiraPy() + +id = jira.email_to_sysid("abelincoln@example.com") +print((id)) + +new_issue = jira.create_incident( + "function call with email", "test out eamil", "3", "abelincoln@example.com" +) + +print(new_issue.fields.project.key) +print(new_issue) +print(new_issue.fields.issuetype.name) +print(new_issue.fields.reporter) +print(new_issue.fields.summary) +print(new_issue.fields.comment.comments) + +issues = jira.retrieve_incidents("abelincoln@example.com") +print(issues) diff --git a/actions/jira_credentials.yml b/actions/jira_credentials.yml new file mode 100644 index 0000000..574b370 --- /dev/null +++ b/actions/jira_credentials.yml @@ -0,0 +1,5 @@ +jira_user: matt.a.dougherty@gmail.com +jira_token: q514H7eXTyXDIp7D8X2vB222 +jira_url: https://rcbot.atlassian.net +project: + id : '10000' diff --git a/endpoints.yml b/endpoints.yml index 7912200..d172073 100644 --- a/endpoints.yml +++ b/endpoints.yml @@ -40,3 +40,5 @@ action_endpoint: # username: username # password: password # queue: queue +helpdeask_service: + service: localmode #snow, jira \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index d1318af..d91cd65 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,4 @@ -rasa~=2.8.0 -rasa-sdk~=2.8.0 # if you change this, make sure to change the Dockerfile to match +rasa~=2.8.12 +rasa-sdk~=2.8.2 # if you change this, make sure to change the Dockerfile to match -r actions/requirements-actions.txt +jira~=3.1.0rc1 \ No newline at end of file From a9b6d0b89630a65af5553d346d30efb65592c26c Mon Sep 17 00:00:00 2001 From: mattdough Date: Sun, 21 Nov 2021 07:09:53 -0800 Subject: [PATCH 03/15] Delete jira_credentials.yml --- actions/jira_credentials.yml | 5 ----- 1 file changed, 5 deletions(-) delete mode 100644 actions/jira_credentials.yml diff --git a/actions/jira_credentials.yml b/actions/jira_credentials.yml deleted file mode 100644 index 574b370..0000000 --- a/actions/jira_credentials.yml +++ /dev/null @@ -1,5 +0,0 @@ -jira_user: matt.a.dougherty@gmail.com -jira_token: q514H7eXTyXDIp7D8X2vB222 -jira_url: https://rcbot.atlassian.net -project: - id : '10000' From df6044b2fc54df34a182ca4a66a7d8c0d18f57d6 Mon Sep 17 00:00:00 2001 From: mattdough Date: Mon, 22 Nov 2021 19:12:04 -0800 Subject: [PATCH 04/15] remove creds --- actions/jira_credentials.yml | 5 ----- 1 file changed, 5 deletions(-) delete mode 100644 actions/jira_credentials.yml diff --git a/actions/jira_credentials.yml b/actions/jira_credentials.yml deleted file mode 100644 index 574b370..0000000 --- a/actions/jira_credentials.yml +++ /dev/null @@ -1,5 +0,0 @@ -jira_user: matt.a.dougherty@gmail.com -jira_token: q514H7eXTyXDIp7D8X2vB222 -jira_url: https://rcbot.atlassian.net -project: - id : '10000' From ea7287aea1a4ad3b41bc1319506c524910e43297 Mon Sep 17 00:00:00 2001 From: mattdough Date: Tue, 23 Nov 2021 05:23:28 -0800 Subject: [PATCH 05/15] working on local,jira,snow mode --- actions/actions.py | 21 ++++++++++++++++----- actions/jira_actions.py | 4 ---- endpoints.yml | 4 ++-- 3 files changed, 18 insertions(+), 11 deletions(-) diff --git a/actions/actions.py b/actions/actions.py index f52642d..3c8d01f 100644 --- a/actions/actions.py +++ b/actions/actions.py @@ -6,17 +6,28 @@ from rasa_sdk.events import AllSlotsReset, SlotSet from actions.snow import SnowAPI import random - +import ruamel.yaml +import pathlib logger = logging.getLogger(__name__) vers = "vers: 0.1.0, date: Apr 2, 2020" logger.debug(vers) +here = pathlib.Path(__file__).parent.absolute() +endpoint_config = ( + ruamel.yaml.safe_load(open(f"{here}/endpoints.yml", "r")) or {} +) +mode = endpoint_config.get("mode") +print(mode) + # TODO read helpdesk service from endpoints.yml -localmode = snow.localmode # TODO instantiante either snow or jira or nothing for local # TODO + snow = SnowAPI() +# TODO Change to handle mode set to local, snow or jira +localmode = snow.localmode + logger.debug(f"Local mode: {snow.localmode}") @@ -51,7 +62,7 @@ def _validate_email( return {"email": None, "previous_email": None} elif isinstance(value, bool): value = tracker.get_slot("previous_email") - + # TODO Change to handle mode set to local, snow or jira if localmode: return {"email": value} @@ -123,7 +134,7 @@ def run( template="utter_incident_creation_canceled" ) return [AllSlotsReset(), SlotSet("previous_email", email)] - + # TODO Change to handle mode set to local, snow or jira if localmode: message = ( f"An incident with the following details would be opened " @@ -193,7 +204,7 @@ def run( "On Hold": "has been put on hold", "Closed": "has been closed", } - if localmode: + if localmode: # TODO Change to handle mode set to local, snow or jira status = random.choice(list(incident_states.values())) message = ( f"Since ServiceNow isn't connected, I'm making this up!\n" diff --git a/actions/jira_actions.py b/actions/jira_actions.py index 8b22711..d577431 100644 --- a/actions/jira_actions.py +++ b/actions/jira_actions.py @@ -27,10 +27,6 @@ def __init__(self): self.jira_url, basic_auth=(self.jira_user, self.jira_token) ) - def handle_request(self): - # TODO This might not be needed. In the service now version it isn't called directly by actions - True - def email_to_sysid(self, email) -> Dict[Text, Any]: result = {} email_result = self.jira_obj.search_users( diff --git a/endpoints.yml b/endpoints.yml index d172073..4109962 100644 --- a/endpoints.yml +++ b/endpoints.yml @@ -40,5 +40,5 @@ action_endpoint: # username: username # password: password # queue: queue -helpdeask_service: - service: localmode #snow, jira \ No newline at end of file +helpdesk: + mode: localmode #snow, jira From fdb30564798268e290d10d10d0910dd9a5cc6421 Mon Sep 17 00:00:00 2001 From: mattdough Date: Sat, 27 Nov 2021 14:24:14 -0800 Subject: [PATCH 06/15] actions, jira, endpoints, and creds for testing --- actions/actions.py | 101 +++++++++++++++++++++++++---------- actions/jira_actions.py | 63 ++++++++++------------ actions/jira_credentials.yml | 5 ++ endpoints.yml | 2 +- 4 files changed, 106 insertions(+), 65 deletions(-) create mode 100644 actions/jira_credentials.yml diff --git a/actions/actions.py b/actions/actions.py index 3c8d01f..989b00b 100644 --- a/actions/actions.py +++ b/actions/actions.py @@ -4,7 +4,8 @@ from rasa_sdk.executor import CollectingDispatcher, Action from rasa_sdk.forms import FormValidationAction from rasa_sdk.events import AllSlotsReset, SlotSet -from actions.snow import SnowAPI +from snow import SnowAPI +from jira_actions import JiraPy import random import ruamel.yaml import pathlib @@ -15,21 +16,18 @@ here = pathlib.Path(__file__).parent.absolute() endpoint_config = ( - ruamel.yaml.safe_load(open(f"{here}/endpoints.yml", "r")) or {} + ruamel.yaml.safe_load(open(f"{here.parent}/endpoints.yml", "r")) or {} ) -mode = endpoint_config.get("mode") -print(mode) +mode = endpoint_config.get("helpdesk").get("mode") +if mode == "jira": + jira = JiraPy() + print(mode) -# TODO read helpdesk service from endpoints.yml -# TODO instantiante either snow or jira or nothing for local -# TODO +if mode == "snow": + snow = SnowAPI() + print(mode) -snow = SnowAPI() -# TODO Change to handle mode set to local, snow or jira -localmode = snow.localmode - - -logger.debug(f"Local mode: {snow.localmode}") +logger.debug(f"mode: {mode}") class ActionAskEmail(Action): @@ -62,12 +60,17 @@ def _validate_email( return {"email": None, "previous_email": None} elif isinstance(value, bool): value = tracker.get_slot("previous_email") - # TODO Change to handle mode set to local, snow or jira - if localmode: - return {"email": value} - results = snow.email_to_sysid(value) - caller_id = results.get("caller_id") + if mode == "snow": + results = snow.email_to_sysid(value) + caller_id = results.get("caller_id") + + elif mode == "jira": + results = jira.email_to_sysid(value) + caller_id = results.get("account_id") + + else: # localmode + return {"email": value} if caller_id: return {"email": value, "caller_id": caller_id} @@ -102,11 +105,19 @@ def validate_priority( ) -> Dict[Text, Any]: """Validate priority is a valid value.""" - if value.lower() in snow.priority_db(): - return {"priority": value} - else: - dispatcher.utter_message(template="utter_no_priority") - return {"priority": None} + if mode == "jira": + if value.lower() in jira.priority_db(): + return {"priority": value} + else: + dispatcher.utter_message(template="utter_no_priority") + return {"priority": None} + + else: # handles local mode and snow + if value.lower() in snow.priority_db(): + return {"priority": value} + else: + dispatcher.utter_message(template="utter_no_priority") + return {"priority": None} class ActionOpenIncident(Action): @@ -134,8 +145,8 @@ def run( template="utter_incident_creation_canceled" ) return [AllSlotsReset(), SlotSet("previous_email", email)] - # TODO Change to handle mode set to local, snow or jira - if localmode: + + if mode == "local": message = ( f"An incident with the following details would be opened " f"if ServiceNow was connected:\n" @@ -143,7 +154,7 @@ def run( f"problem description: {problem_description}\n" f"title: {incident_title}\npriority: {priority}" ) - else: + elif mode == "snow": snow_priority = snow.priority_db().get(priority) response = snow.create_incident( description=problem_description, @@ -164,6 +175,27 @@ def run( f"Something went wrong while opening an incident for you. " f"{response.get('error')}" ) + elif mode == "jira": + jira_priority = jira.priority_db().get(priority) + response = jira.create_incident( + description=problem_description, + short_description=incident_title, + priority=jira_priority, + email=email, + ) + # TODO Need to test. Might need a try catch around this. If the email returns an error, the response won't be an object with id function. + incident_number = response.id + if incident_number: + message = ( + f"Successfully opened up incident {incident_number} " + f"for you. Someone will reach out soon." + ) + else: + message = ( + f"Something went wrong while opening an incident for you. " + f"{response.get('error')}" + ) + dispatcher.utter_message(message) return [AllSlotsReset(), SlotSet("previous_email", email)] @@ -204,13 +236,13 @@ def run( "On Hold": "has been put on hold", "Closed": "has been closed", } - if localmode: # TODO Change to handle mode set to local, snow or jira + if mode == "local": status = random.choice(list(incident_states.values())) message = ( f"Since ServiceNow isn't connected, I'm making this up!\n" f"The most recent incident for {email} {status}" ) - else: + elif mode == "snow": incidents_result = snow.retrieve_incidents(email) incidents = incidents_result.get("incidents") if incidents: @@ -226,6 +258,19 @@ def run( else: message = f"{incidents_result.get('error')}" + elif mode == "jira": + incidents_result = jira.retrieve_incidents(email) + incidents = incidents_result.get("incidents") + if incidents: + message = "\n".join( + [ + f"Incident {i}: " + f'{issues["incidents"][i]["summary"]} ' + f'opened on {issues["incidents"][i]["created_on"]} ' + f'Status: {issues["incidents"][i]["status"]} ' + for i in issues["incidents"] + ] + ) dispatcher.utter_message(message) return [AllSlotsReset(), SlotSet("previous_email", email)] diff --git a/actions/jira_actions.py b/actions/jira_actions.py index d577431..ee59a1d 100644 --- a/actions/jira_actions.py +++ b/actions/jira_actions.py @@ -9,13 +9,15 @@ logger = logging.getLogger(__name__) -here = pathlib.Path(__file__).parent.absolute() +cred_path = str(pathlib.Path(__file__).parent.parents[0]) + "/.vscode" class JiraPy(object): def __init__(self): jira_config = ( - ruamel.yaml.safe_load(open(f"{here}/jira_credentials.yml", "r")) + ruamel.yaml.safe_load( + open(f"{cred_path}/jira_credentials.yml", "r") + ) or {} ) @@ -32,13 +34,14 @@ def email_to_sysid(self, email) -> Dict[Text, Any]: email_result = self.jira_obj.search_users( None, 0, 10, True, False, email ) - if len(email_result) == 1: + num_records = len(email_result) + if num_records == 1: result["account_id"] = vars(email_result[0]).get("accountId") else: result["account_id"] = [] result["error"] = ( f"Could not retreive account id; " - f"Multiple records found for email {email}" + f"{num_records} records found for email {email}" ) return result @@ -53,7 +56,11 @@ def retrieve_incidents(self, email) -> Dict[Text, Any]: ) if account_id: for issue in issues: - incidents[issue.key] = issue.fields.summary + incidents[issue.key] = { + "summary": issue.fields.summary, + "created_on": issue.fields.created, + "status": issue.fields.status.name, + } elif isinstance(issues, list): result["error"] = f"No incidents on record for {email}" @@ -65,17 +72,21 @@ def create_incident( self, description, short_description, priority, email ) -> Dict[Text, Any]: project = self.project - account_id = self.email_to_sysid(email).get("account_id") - print(account_id) - issue = self.jira_obj.create_issue( - project=project, - summary=short_description, - description=description, - issuetype={"id": "10002"}, - priority={"id": priority}, - reporter={"accountId": account_id}, - ) - return issue + email_result = self.email_to_sysid(email) + account_id = email_result.get("account_id") + if account_id: + result = self.jira_obj.create_issue( + project=project, + summary=short_description, + description=description, + issuetype={"id": "10002"}, + priority={"id": priority}, + reporter={"accountId": account_id}, + ) + else: + result = email_result.get("error") + + return result # TODO need to use this for setting priority @staticmethod @@ -83,23 +94,3 @@ def priority_db() -> Dict[str, int]: """Database of supported priorities""" priorities = {"low": 4, "medium": 3, "high": 2} return priorities - - -jira = JiraPy() - -id = jira.email_to_sysid("abelincoln@example.com") -print((id)) - -new_issue = jira.create_incident( - "function call with email", "test out eamil", "3", "abelincoln@example.com" -) - -print(new_issue.fields.project.key) -print(new_issue) -print(new_issue.fields.issuetype.name) -print(new_issue.fields.reporter) -print(new_issue.fields.summary) -print(new_issue.fields.comment.comments) - -issues = jira.retrieve_incidents("abelincoln@example.com") -print(issues) diff --git a/actions/jira_credentials.yml b/actions/jira_credentials.yml new file mode 100644 index 0000000..15ae108 --- /dev/null +++ b/actions/jira_credentials.yml @@ -0,0 +1,5 @@ +jira_user: YOUR_EMAIL +jira_token: YOUR_TOKEN +jira_url: YOUR_URL +project: + id : '10000' diff --git a/endpoints.yml b/endpoints.yml index 4109962..28c7120 100644 --- a/endpoints.yml +++ b/endpoints.yml @@ -41,4 +41,4 @@ action_endpoint: # password: password # queue: queue helpdesk: - mode: localmode #snow, jira + mode: jira #local, snow, jira From 73629badfe65dc072bd1dcea7ac79edd081bc3c7 Mon Sep 17 00:00:00 2001 From: mattdough Date: Sat, 27 Nov 2021 14:39:16 -0800 Subject: [PATCH 07/15] actions message --- actions/actions.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/actions/actions.py b/actions/actions.py index 989b00b..c0ab9c2 100644 --- a/actions/actions.py +++ b/actions/actions.py @@ -260,15 +260,14 @@ def run( message = f"{incidents_result.get('error')}" elif mode == "jira": incidents_result = jira.retrieve_incidents(email) - incidents = incidents_result.get("incidents") - if incidents: + if incidents_result: message = "\n".join( [ f"Incident {i}: " - f'{issues["incidents"][i]["summary"]} ' - f'opened on {issues["incidents"][i]["created_on"]} ' - f'Status: {issues["incidents"][i]["status"]} ' - for i in issues["incidents"] + f'{incidents_result["incidents"][i]["summary"]} ' + f'opened on {incidents_result["incidents"][i]["created_on"]} ' + f'Status: {incidents_result["incidents"][i]["status"]} ' + for i in incidents_result["incidents"] ] ) From 1920af2f621f058d75382ab48c6bfc6f42acd4d9 Mon Sep 17 00:00:00 2001 From: mattdough Date: Sun, 28 Nov 2021 06:50:16 -0800 Subject: [PATCH 08/15] updated actions, jira. Working with bot --- actions/actions.py | 5 +++-- actions/jira_actions.py | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/actions/actions.py b/actions/actions.py index c0ab9c2..eac36be 100644 --- a/actions/actions.py +++ b/actions/actions.py @@ -4,8 +4,8 @@ from rasa_sdk.executor import CollectingDispatcher, Action from rasa_sdk.forms import FormValidationAction from rasa_sdk.events import AllSlotsReset, SlotSet -from snow import SnowAPI -from jira_actions import JiraPy +from actions.snow import SnowAPI +from actions.jira_actions import JiraPy import random import ruamel.yaml import pathlib @@ -185,6 +185,7 @@ def run( ) # TODO Need to test. Might need a try catch around this. If the email returns an error, the response won't be an object with id function. incident_number = response.id + print(incident_number) if incident_number: message = ( f"Successfully opened up incident {incident_number} " diff --git a/actions/jira_actions.py b/actions/jira_actions.py index ee59a1d..7aa6229 100644 --- a/actions/jira_actions.py +++ b/actions/jira_actions.py @@ -92,5 +92,5 @@ def create_incident( @staticmethod def priority_db() -> Dict[str, int]: """Database of supported priorities""" - priorities = {"low": 4, "medium": 3, "high": 2} + priorities = {"low": "4", "medium": "3", "high": "2"} return priorities From f7ef3285340e7305e5a8306d1998fe2209468a39 Mon Sep 17 00:00:00 2001 From: mattdough Date: Sat, 11 Dec 2021 06:38:42 -0800 Subject: [PATCH 09/15] added assigned issues and change priority methods --- actions/jira_actions.py | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/actions/jira_actions.py b/actions/jira_actions.py index 7aa6229..f645671 100644 --- a/actions/jira_actions.py +++ b/actions/jira_actions.py @@ -88,9 +88,34 @@ def create_incident( return result - # TODO need to use this for setting priority + def delete_issue(self, issue_id): + issue = self.jira_obj.issue(issue_id) + issue.delete() + return + + def change_priority(self, issue_id, priority): + issue = self.jira_obj.issue(issue_id) + issue.update(priority={"id": priority}) + + def assigned_issues(self, account_id): + issues = self.jira_obj.search_issues( + f"assignee = {account_id} ORDER BY priority" + ) + return issues + @staticmethod def priority_db() -> Dict[str, int]: """Database of supported priorities""" priorities = {"low": "4", "medium": "3", "high": "2"} return priorities + + +if __name__ == "__main__": + + jira = JiraPy() + + # test assigned issues + # email = "ADMINEMAIL" with issues assigned + # account_id = jira.email_to_sysid(email).get("account_id") + # my_issues = jira.assigned_issues(account_id) + # print(my_issues) From 4d529ea9e87018e1cd2bafde969dda70c26cd871 Mon Sep 17 00:00:00 2001 From: mattdough Date: Mon, 3 Jan 2022 19:30:23 -0800 Subject: [PATCH 10/15] changed JiraPy to JiraAPI --- actions/actions.py | 4 ++-- actions/jira_actions.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/actions/actions.py b/actions/actions.py index eac36be..11fa9c1 100644 --- a/actions/actions.py +++ b/actions/actions.py @@ -5,7 +5,7 @@ from rasa_sdk.forms import FormValidationAction from rasa_sdk.events import AllSlotsReset, SlotSet from actions.snow import SnowAPI -from actions.jira_actions import JiraPy +from actions.jira_actions import JiraAPI import random import ruamel.yaml import pathlib @@ -20,7 +20,7 @@ ) mode = endpoint_config.get("helpdesk").get("mode") if mode == "jira": - jira = JiraPy() + jira = JiraAPI() print(mode) if mode == "snow": diff --git a/actions/jira_actions.py b/actions/jira_actions.py index f645671..0646f92 100644 --- a/actions/jira_actions.py +++ b/actions/jira_actions.py @@ -12,7 +12,7 @@ cred_path = str(pathlib.Path(__file__).parent.parents[0]) + "/.vscode" -class JiraPy(object): +class JiraAPI(object): def __init__(self): jira_config = ( ruamel.yaml.safe_load( @@ -112,7 +112,7 @@ def priority_db() -> Dict[str, int]: if __name__ == "__main__": - jira = JiraPy() + jira = JiraAPI() # test assigned issues # email = "ADMINEMAIL" with issues assigned From 3a64b684fd2db51ca23ccd14f7a7bcd1ea25ca79 Mon Sep 17 00:00:00 2001 From: mattdough Date: Mon, 3 Jan 2022 19:33:55 -0800 Subject: [PATCH 11/15] credentials sample added --- actions/jira_credentials.yml | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 actions/jira_credentials.yml diff --git a/actions/jira_credentials.yml b/actions/jira_credentials.yml new file mode 100644 index 0000000..de4b373 --- /dev/null +++ b/actions/jira_credentials.yml @@ -0,0 +1,5 @@ +jira_user: YOUR_EMAIL +jira_token: YOUR_API_TOKEN +jira_url: YOUR_URL +project: + id : 'PROJECT_ID' From a661d80e3547c86a557fb4fd78a7c13da8526ec8 Mon Sep 17 00:00:00 2001 From: mattdough Date: Wed, 5 Jan 2022 05:55:37 -0800 Subject: [PATCH 12/15] updated readme,creds, and enpoints --- README.md | 33 ++++++++++++++++++++++++++------- actions/jira_actions.py | 13 +------------ endpoints.yml | 3 ++- 3 files changed, 29 insertions(+), 20 deletions(-) diff --git a/README.md b/README.md index 9784df2..a041b89 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,9 @@ Here is an example of a conversation you can have with this bot: - [Rasa Helpdesk Assistant Example](#rasa-helpdesk-assistant-example) - [Setup](#setup) - [Install the dependencies](#install-the-dependencies) + - [Configure endpoints](#configure-endpoints) - [Optional: Connect to a ServiceNow instance](#optional-connect-to-a-servicenow-instance) + - [Optional: Connect to a Jira Instance](#optional-connect-to-a-jira-instance) - [Running the bot](#running-the-bot) - [Things you can ask the bot](#things-you-can-ask-the-bot) - [Example conversations](#example-conversations) @@ -49,13 +51,15 @@ pre-commit install > With pre-commit installed, the `black` and `doctoc` hooks will run on every `git commit`. > If any changes are made by the hooks, you will need to re-add changed files and re-commit your changes. -### Optional: Connect to a ServiceNow instance +### Configure endpoints -You can run this bot without connecting to a ServiceNow instance, in which case it will +You can run this bot without connecting to a ServiceNow or Jira instance, in which case it will send responses without creating an incident or checking the actual status of one. -To run the bot without connecting ServiceNow, -you don't need to change anything in `actions/snow_credentials.yml`; `localmode` should already be set -to `true` +To run the bot without connecting to ServiceNow or Jira you don't need to change anything +in `endpoints.yml`; `helpdesk: mode:` should already be set to `local`. + +When set to `local` (default in the code), it will just take all the data in and message out the information that would normally be sent. +### Optional: Connect to a ServiceNow instance If you do want to connect to ServiceNow, you can get your own free Developer instance to test this with [here](https://developer.servicenow.com/app.do#!/home) @@ -68,7 +72,22 @@ To connect to your ServiceNow developer instance, configure the following in `ac - `snow_pw` - The password of the service account for the ServiceNow developer instance -- `localmode` - Whether the action server should **not** try to reach out to a `snow_instance` based on the credentials in `actions/snow_credentials.yml`. When set to `True` (default in the code), it will just take all the data in and message out the information that would normally be sent. +In `endpoints.yml` configure `helpdesk: mode:` to `snow`. + +### Optional: Connect to a Jira Instance + +If you want to connect to Jira Service Management, you can setup a free limited instance [here](https://www.atlassian.com/software/jira/service-management/free) + +To connect to your Jira instance, configure the followingin `actions/jira_credentials.yml`: +- `jira_user` - This is the email of the admin account for the Jira instance + +- `jira_token` - This is the API Token for your Jira instance. Get it [here](https://id.atlassian.com/manage-profile/security/api-tokens) + +- `jira_url` - The url for the Jira instance. Should be in the form https://your-domain.atlassian.net + +- `project: id` - The id for your project. Can be found using the API https://your-domain.atlassian.net/rest/api/3/project + +In `endpoints.yml` configure `helpdesk: mode:` to `jira`. ## Running the bot @@ -80,7 +99,7 @@ Then, to run, first set up your action server in one terminal window: rasa run actions ``` -In another window, run the duckling server (for entity extraction): +In another window, run the duckling server (for netentity extraction): ```bash docker run -p 8000:8000 rasa/duckling diff --git a/actions/jira_actions.py b/actions/jira_actions.py index 0646f92..2d93b66 100644 --- a/actions/jira_actions.py +++ b/actions/jira_actions.py @@ -9,7 +9,7 @@ logger = logging.getLogger(__name__) -cred_path = str(pathlib.Path(__file__).parent.parents[0]) + "/.vscode" +cred_path = str(pathlib.Path(__file__).parent.absolute()) class JiraAPI(object): @@ -108,14 +108,3 @@ def priority_db() -> Dict[str, int]: """Database of supported priorities""" priorities = {"low": "4", "medium": "3", "high": "2"} return priorities - - -if __name__ == "__main__": - - jira = JiraAPI() - - # test assigned issues - # email = "ADMINEMAIL" with issues assigned - # account_id = jira.email_to_sysid(email).get("account_id") - # my_issues = jira.assigned_issues(account_id) - # print(my_issues) diff --git a/endpoints.yml b/endpoints.yml index 28c7120..4f9afc3 100644 --- a/endpoints.yml +++ b/endpoints.yml @@ -40,5 +40,6 @@ action_endpoint: # username: username # password: password # queue: queue + helpdesk: - mode: jira #local, snow, jira + mode: local #local, snow, jira From 95cd207367feb3a97e9a3a2e2e915c2a747304b8 Mon Sep 17 00:00:00 2001 From: mattdough Date: Fri, 21 Jan 2022 06:18:10 -0800 Subject: [PATCH 13/15] tested snow --- README.md | 6 +++--- actions/jira_credentials.yml | 11 ++++++----- actions/snow_credentials.yml | 3 +-- endpoints.yml | 2 +- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index a041b89..156a37d 100644 --- a/README.md +++ b/README.md @@ -78,7 +78,7 @@ In `endpoints.yml` configure `helpdesk: mode:` to `snow`. If you want to connect to Jira Service Management, you can setup a free limited instance [here](https://www.atlassian.com/software/jira/service-management/free) -To connect to your Jira instance, configure the followingin `actions/jira_credentials.yml`: +To connect to your Jira instance, configure the following in `actions/jira_credentials.yml`: - `jira_user` - This is the email of the admin account for the Jira instance - `jira_token` - This is the API Token for your Jira instance. Get it [here](https://id.atlassian.com/manage-profile/security/api-tokens) @@ -141,7 +141,7 @@ If configured, the bot can also hand off to another bot in response to the user ## Example conversations -With `localmode=true`: +With `helpdesk: mode: local`: ``` Bot loaded. Type a message and press enter (use '/stop' to exit): @@ -177,7 +177,7 @@ Your input -> Yes please The most recent incident for anything@example.com is currently awaiting triage ``` -With `localmode=false`: +With `helpdesk: mode: snow`: With a Service Now instance connected, it will check if the email address is in the instance database and provide an incident number for the final response: diff --git a/actions/jira_credentials.yml b/actions/jira_credentials.yml index de4b373..c78e6fa 100644 --- a/actions/jira_credentials.yml +++ b/actions/jira_credentials.yml @@ -1,5 +1,6 @@ -jira_user: YOUR_EMAIL -jira_token: YOUR_API_TOKEN -jira_url: YOUR_URL -project: - id : 'PROJECT_ID' +#jira_user: YOUR_EMAIL +#jira_token: YOUR_API_TOKEN +#jira_url: YOUR_URL +#project: +# id : 'PROJECT_ID' + diff --git a/actions/snow_credentials.yml b/actions/snow_credentials.yml index ee9aacd..3d78ad3 100644 --- a/actions/snow_credentials.yml +++ b/actions/snow_credentials.yml @@ -1,4 +1,3 @@ # snow_instance: "dev97377.service-now.com" # snow_user: "admin" -# snow_pw: "mySnowinstance1" -# localmode: false \ No newline at end of file +# snow_pw: "mySnowinstance1" \ No newline at end of file diff --git a/endpoints.yml b/endpoints.yml index 4f9afc3..9c735dd 100644 --- a/endpoints.yml +++ b/endpoints.yml @@ -42,4 +42,4 @@ action_endpoint: # queue: queue helpdesk: - mode: local #local, snow, jira + mode: snow #local, snow, jira From 6137947dbc3d81142e6b135beac46195f641e730 Mon Sep 17 00:00:00 2001 From: mattdough Date: Mon, 31 Jan 2022 05:09:41 -0800 Subject: [PATCH 14/15] new jira python version --- actions/jira_actions.py | 2 +- actions/snow_credentials.yml | 2 +- endpoints.yml | 2 +- requirements.txt | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/actions/jira_actions.py b/actions/jira_actions.py index 2d93b66..5b12751 100644 --- a/actions/jira_actions.py +++ b/actions/jira_actions.py @@ -4,7 +4,7 @@ from typing import Dict, Text, Any from jira import ( JIRA, -) # requires jira 3.1.0rc1 for the search_user function to work +) from jira.resources import Customer, Priority, User logger = logging.getLogger(__name__) diff --git a/actions/snow_credentials.yml b/actions/snow_credentials.yml index 3d78ad3..c9e8d24 100644 --- a/actions/snow_credentials.yml +++ b/actions/snow_credentials.yml @@ -1,3 +1,3 @@ # snow_instance: "dev97377.service-now.com" # snow_user: "admin" -# snow_pw: "mySnowinstance1" \ No newline at end of file +# snow_pw: "mySnowinstance1" diff --git a/endpoints.yml b/endpoints.yml index 9c735dd..4f9afc3 100644 --- a/endpoints.yml +++ b/endpoints.yml @@ -42,4 +42,4 @@ action_endpoint: # queue: queue helpdesk: - mode: snow #local, snow, jira + mode: local #local, snow, jira diff --git a/requirements.txt b/requirements.txt index d91cd65..2c0860a 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ rasa~=2.8.12 rasa-sdk~=2.8.2 # if you change this, make sure to change the Dockerfile to match -r actions/requirements-actions.txt -jira~=3.1.0rc1 \ No newline at end of file +jira~=3.1.1 \ No newline at end of file From 63f99b4a8f621b4f6ac7192250f24a3781973112 Mon Sep 17 00:00:00 2001 From: mattdough Date: Sat, 5 Feb 2022 14:57:35 -0800 Subject: [PATCH 15/15] fixe local mode priority --- actions/actions.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/actions/actions.py b/actions/actions.py index 11fa9c1..f9d7686 100644 --- a/actions/actions.py +++ b/actions/actions.py @@ -112,13 +112,16 @@ def validate_priority( dispatcher.utter_message(template="utter_no_priority") return {"priority": None} - else: # handles local mode and snow + elif mode == "snow": # handles local mode and snow if value.lower() in snow.priority_db(): return {"priority": value} else: dispatcher.utter_message(template="utter_no_priority") return {"priority": None} + else: + return {"priority": value} + class ActionOpenIncident(Action): def name(self) -> Text: