# -*- coding: utf-8 -*- # Copyright 2010 - 2017 RhodeCode GmbH and the AppEnlight project authors # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import json import requests from . import IntegrationBase, IntegrationException _ = str class GithubAuthException(Exception): pass class GithubIntegration(IntegrationBase): __mapper_args__ = {"polymorphic_identity": "github"} front_visible = True as_alert_channel = False supports_report_alerting = False action_notification = True integration_action = "Add issue to Github" @classmethod def create_client(cls, request, user_name=None, repo_name=None): """ Creates REST client that can authenticate to specific repo uses auth tokens for current request user """ token = None secret = None for identity in request.user.external_identities: if identity.provider_name == "github": token = identity.access_token secret = identity.token_secret break if not token: raise IntegrationException("No valid auth token present for this service") client = GithubClient(token=token, owner=user_name, name=repo_name) return client class GithubClient(object): api_url = "https://api.github.com" repo_type = "github" def __init__(self, token, owner, name): self.access_token = token self.owner = owner self.name = name def make_request(self, url, method="get", data=None, headers=None): req_headers = { "User-Agent": "appenlight", "Content-Type": "application/json", "Authorization": "token %s" % self.access_token, } try: if data: data = json.dumps(data) resp = getattr(requests, method)( url, data=data, headers=req_headers, timeout=10 ) except Exception as e: msg = "Error communicating with Github: %s" raise IntegrationException(_(msg) % (e,)) if resp.status_code == 404: msg = "User or repo name are incorrect" raise IntegrationException(_(msg)) if resp.status_code == 401: msg = "You are not authorized to access this repo" raise IntegrationException(_(msg)) elif resp.status_code not in [200, 201]: msg = "Github response_code: %s" raise IntegrationException(_(msg) % resp.status_code) try: return resp.json() except Exception as e: msg = "Error decoding response from Github: %s" raise IntegrationException(_(msg) % (e,)) def get_statuses(self): """Gets list of possible item statuses""" url = "%(api_url)s/repos/%(owner)s/%(name)s/labels" % { "api_url": self.api_url, "owner": self.owner, "name": self.name, } data = self.make_request(url) statuses = [] for status in data: statuses.append(status["name"]) return statuses def get_repo(self): """Gets list of possible item statuses""" url = "%(api_url)s/repos/%(owner)s/%(name)s" % { "api_url": self.api_url, "owner": self.owner, "name": self.name, } data = self.make_request(url) return data def get_assignees(self): """Gets list of possible assignees""" url = "%(api_url)s/repos/%(owner)s/%(name)s/collaborators" % { "api_url": self.api_url, "owner": self.owner, "name": self.name, } data = self.make_request(url) results = [] for entry in data: results.append({"user": entry["login"], "name": entry.get("name")}) return results def create_issue(self, form_data): """ Make a REST call to create issue in Github's issue tracker """ url = "%(api_url)s/repos/%(owner)s/%(name)s/issues" % { "api_url": self.api_url, "owner": self.owner, "name": self.name, } payload = { "title": form_data["title"], "body": form_data["content"], "labels": [], "assignee": form_data["responsible"], } payload["labels"].extend(form_data["kind"]) data = self.make_request(url, "post", data=payload) to_return = { "id": data["number"], "resource_url": data["url"], "web_url": data["html_url"], } return to_return