From 4896f8c04033909a25233a25e6f350b0752fcdf5 Mon Sep 17 00:00:00 2001 From: Adam Kendis Date: Tue, 31 Mar 2020 14:52:39 -0700 Subject: [PATCH 1/6] Added redacted Github config vars to settings.example.cfg. --- server/src/settings.example.cfg | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/server/src/settings.example.cfg b/server/src/settings.example.cfg index 845340958..92046a82d 100644 --- a/server/src/settings.example.cfg +++ b/server/src/settings.example.cfg @@ -20,3 +20,8 @@ AP2018 = h65r-yf5i AP2017 = d4vt-q4t5 AP2016 = ndkd-k878 AP2015 = ms7h-a45h + +[Github] +GITHUB_TOKEN = REDACTED +ISSUES_URL = https://api.github.com/repos/hackforla/311-data/issues +PROJECT_URL = REDACTED From 3c7fa46f5decf6462ba65a12b4d75a1b46095143 Mon Sep 17 00:00:00 2001 From: Adam Kendis Date: Tue, 31 Mar 2020 15:12:22 -0700 Subject: [PATCH 2/6] FeedbackService for github integration. --- server/src/services/feedbackService.py | 62 ++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 server/src/services/feedbackService.py diff --git a/server/src/services/feedbackService.py b/server/src/services/feedbackService.py new file mode 100644 index 000000000..eb382a43d --- /dev/null +++ b/server/src/services/feedbackService.py @@ -0,0 +1,62 @@ +from json import dumps, loads +import requests_async as requests + + +class FeedbackService(object): + def __init__(self, config=None): + self.config = config + self.token = None if not self.config \ + else self.config['Github']['GITHUB_TOKEN'] + self.issues_url = None if not self.config \ + else self.config['Github']['ISSUES_URL'] + self.project_url = None if not self.config \ + else self.config['Github']['PROJECT_URL'] + + async def create_issue(self, title, body, labels=['feedback'], milestone=None, assignees=[]): + """ + Creates a Github issue via Github API v3 and returns the new issue id. + + Note: Per Github, the API (and required 'Accept' headers) may change without notice. + See https://developer.github.com/v3/issues/ + """ + headers = { + "Authorization": "token {}".format(self.token), + "Accept": "application/vnd.github.v3+json" + } + data = { + 'title': title, + 'body': body, + 'labels': labels, + 'milestone': milestone, + 'assigness': assignees + } + payload = dumps(data) + + async with requests.Session() as session: + response = await session.post(self.issues_url, data=payload, headers=headers) + response_content = loads(response.content) + issue_id = response_content['id'] + return issue_id + + + async def add_issue_to_project(self, issue_id, content_type='Issue'): + """ + Takes a Github issue id and adds the issue to a project board card. + Returns the response from Github API. + + Note: Per Github, the API (and required 'Accept' headers) may change without notice. + See https://developer.github.com/v3/projects/cards/ + """ + headers = { + "Authorization": "token {}".format(self.token), + "Accept": "application/vnd.github.inertia-preview+json" + } + data = { + 'content_id': issue_id, + 'content_type': content_type + } + payload = dumps(data) + + async with requests.Session() as session: + response = await session.post(self.project_url, data=payload, headers=headers) + return response From 769ef4d0bd7720d321f79dfa9ae3b019116dab6a Mon Sep 17 00:00:00 2001 From: Adam Kendis Date: Tue, 31 Mar 2020 15:32:35 -0700 Subject: [PATCH 3/6] Added some error handling to FeedbackService. --- server/src/services/feedbackService.py | 32 +++++++++++++++++++++----- 1 file changed, 26 insertions(+), 6 deletions(-) diff --git a/server/src/services/feedbackService.py b/server/src/services/feedbackService.py index eb382a43d..6c15bf387 100644 --- a/server/src/services/feedbackService.py +++ b/server/src/services/feedbackService.py @@ -33,10 +33,20 @@ async def create_issue(self, title, body, labels=['feedback'], milestone=None, a payload = dumps(data) async with requests.Session() as session: - response = await session.post(self.issues_url, data=payload, headers=headers) - response_content = loads(response.content) - issue_id = response_content['id'] - return issue_id + try: + response = await session.post(self.issues_url, data=payload, headers=headers) + response_content = loads(response.content) + issue_id = response_content['id'] + response.raise_for_status() + return issue_id + except requests.exceptions.HTTPError as errh: + return "An Http Error occurred:" + repr(errh) + except requests.exceptions.ConnectionError as errc: + return "An Error Connecting to the API occurred:" + repr(errc) + except requests.exceptions.Timeout as errt: + return "A Timeout Error occurred:" + repr(errt) + except requests.exceptions.RequestException as err: + return "An Unknown Error occurred" + repr(err) async def add_issue_to_project(self, issue_id, content_type='Issue'): @@ -58,5 +68,15 @@ async def add_issue_to_project(self, issue_id, content_type='Issue'): payload = dumps(data) async with requests.Session() as session: - response = await session.post(self.project_url, data=payload, headers=headers) - return response + try: + response = await session.post(self.project_url, data=payload, headers=headers) + response.raise_for_status() + return response.status_code + except requests.exceptions.HTTPError as errh: + return "An Http Error occurred:" + repr(errh) + except requests.exceptions.ConnectionError as errc: + return "An Error Connecting to the API occurred:" + repr(errc) + except requests.exceptions.Timeout as errt: + return "A Timeout Error occurred:" + repr(errt) + except requests.exceptions.RequestException as err: + return "An Unknown Error occurred" + repr(err) From c2c3c7199e598a23ed94c58de43561ee60b06742 Mon Sep 17 00:00:00 2001 From: Adam Kendis Date: Tue, 31 Mar 2020 15:33:00 -0700 Subject: [PATCH 4/6] Implemented /feedback server route and handler. --- server/src/app.py | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/server/src/app.py b/server/src/app.py index 8f4ca68b9..a145e6300 100644 --- a/server/src/app.py +++ b/server/src/app.py @@ -15,6 +15,7 @@ from services.requestDetailService import RequestDetailService from services.ingress_service import ingress_service from services.sqlIngest import DataHandler +from services.feedbackService import FeedbackService app = Sanic(__name__) CORS(app) @@ -31,7 +32,9 @@ def environment_overrides(): if os.environ.get('TOKEN', None): app.config['Settings']['Socrata']['TOKEN'] =\ os.environ.get('TOKEN') - + if os.environ.get('GITHUB_TOKEN', None): + app.config['Settings']['Github']['GITHUB_TOKEN'] =\ + os.environ.get('GITHUB_TOKEN') def configure_app(): # Settings initialization @@ -180,6 +183,19 @@ async def requestDetails(request, srnumber): return json(return_data) +@app.route('/feedback', methods=["POST"]) +@compress.compress() +async def handle_feedback(request): + github_worker = FeedbackService(app.config['Settings']) + postArgs = request.json + title = postArgs.get('title', None) + body = postArgs.get('body', None) + + issue_id = await github_worker.create_issue(title, body) + response = await github_worker.add_issue_to_project(issue_id) + return json(response) + + @app.route('/test_multiple_workers') @compress.compress() async def test_multiple_workers(request): From 55496eb8058ec2e5debe0eaa086bbf0a210fccfe Mon Sep 17 00:00:00 2001 From: Adam Kendis Date: Tue, 31 Mar 2020 15:46:50 -0700 Subject: [PATCH 5/6] Fixed linting errors. --- server/src/app.py | 1 + server/src/services/feedbackService.py | 7 +++---- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/server/src/app.py b/server/src/app.py index a145e6300..2d313e1ed 100644 --- a/server/src/app.py +++ b/server/src/app.py @@ -36,6 +36,7 @@ def environment_overrides(): app.config['Settings']['Github']['GITHUB_TOKEN'] =\ os.environ.get('GITHUB_TOKEN') + def configure_app(): # Settings initialization config = ConfigParser() diff --git a/server/src/services/feedbackService.py b/server/src/services/feedbackService.py index 6c15bf387..9f8b55c5f 100644 --- a/server/src/services/feedbackService.py +++ b/server/src/services/feedbackService.py @@ -11,12 +11,12 @@ def __init__(self, config=None): else self.config['Github']['ISSUES_URL'] self.project_url = None if not self.config \ else self.config['Github']['PROJECT_URL'] - + async def create_issue(self, title, body, labels=['feedback'], milestone=None, assignees=[]): """ Creates a Github issue via Github API v3 and returns the new issue id. - Note: Per Github, the API (and required 'Accept' headers) may change without notice. + Note: Per Github, the API (and required 'Accept' headers) may change without notice. See https://developer.github.com/v3/issues/ """ headers = { @@ -48,13 +48,12 @@ async def create_issue(self, title, body, labels=['feedback'], milestone=None, a except requests.exceptions.RequestException as err: return "An Unknown Error occurred" + repr(err) - async def add_issue_to_project(self, issue_id, content_type='Issue'): """ Takes a Github issue id and adds the issue to a project board card. Returns the response from Github API. - Note: Per Github, the API (and required 'Accept' headers) may change without notice. + Note: Per Github, the API (and required 'Accept' headers) may change without notice. See https://developer.github.com/v3/projects/cards/ """ headers = { From dc6c0a5542f0e76ce06c42b83a8dffdefae05818 Mon Sep 17 00:00:00 2001 From: Adam Kendis Date: Tue, 31 Mar 2020 16:10:54 -0700 Subject: [PATCH 6/6] Fixed typo in FeedbackService. --- server/src/services/feedbackService.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/src/services/feedbackService.py b/server/src/services/feedbackService.py index 9f8b55c5f..ee79614cb 100644 --- a/server/src/services/feedbackService.py +++ b/server/src/services/feedbackService.py @@ -28,7 +28,7 @@ async def create_issue(self, title, body, labels=['feedback'], milestone=None, a 'body': body, 'labels': labels, 'milestone': milestone, - 'assigness': assignees + 'assignees': assignees } payload = dumps(data)