gitea-github-sync/bridge/webgit.py

195 lines
5.4 KiB
Python

from typing import Any
from dataclasses import dataclass, field
import requests
from requests import Request, Session
from requests.exceptions import HTTPError
from flask import abort
@dataclass
class WebgitClient:
"""
A quite thin wrapper around various code forges' REST APIs.
Designed to be subclassed.
"""
api_token: str
headers: Any = field(init=False)
api_prefix: str = field(init=False)
def _post_request(self, request_obj):
try:
request_obj.raise_for_status()
except HTTPError as e:
print("An exception occured: {}({})".format(
type(e).__name__, e
))
abort(500)
def _request_wrapper(self, method, *args, **kwargs):
s = Session()
r = Request(method, *args, **kwargs, headers=self.headers)
prepped = s.prepare_request(r)
settings = s.merge_environment_settings(prepped.url, {}, None, None, None)
r = s.send(prepped, **settings)
self._post_request(r)
return r
def post(self, *args, **kwargs):
return self._request_wrapper("POST", *args, **kwargs)
def patch(self, *args, **kwargs):
return self._request_wrapper("PATCH", *args, **kwargs)
def delete(self, *args, **kwargs):
return self._request_wrapper("DELETE", *args, **kwargs)
def create_repo(self, name, description):
return self.post(
self.api_prefix + "/user/repos",
json={
"name": name,
"description": description,
},
)
def create_issue(self, owner, repo_name, title, body):
return self.post(
self.api_prefix + "/repos/{}/{}/issues".format(
owner,
repo_name,
),
json={
"title": title,
"body": body,
},
)
def leave_comment_on_issue_by_number(self, owner, repo_name, issue_number, body):
return self.post(
self.api_prefix + "/repos/{}/{}/issues/{}/comments".format(
owner,
repo_name,
issue_number,
),
json={
"body": body,
},
)
def leave_comment_on_issue_by_url(self, url, body):
return self.post(
url + "/comments",
json={
"body": body,
},
)
def close_issue_by_number(self, owner, repo_name, issue_number):
return self.patch(
self.api_prefix + "/repos/{}/{}/issues/{}".format(
owner,
repo_name,
issue_number,
),
json={
"state": "closed",
},
)
def close_issue_by_url(self, url):
return self.patch(
url,
json={
"state": "closed",
},
)
def delete_repo(self, owner, repo_name):
return self.delete(
self.api_prefix + "/repos/{}/{}".format(owner, repo_name)
)
@dataclass
class Github(WebgitClient):
"""
A quite thin wrapper around Github's REST API.
"""
def __post_init__(self):
self.headers = {
"Accept": "application/vnd.github+json",
"Authorization": "token " + self.api_token,
"X-GitHub-Api-Version": "2022-11-28",
}
self.api_prefix = "https://api.github.com"
def create_webhook(self, owner, repo_name, http_endpoint, events: list[str]):
return self.post(
self.api_prefix + "/repos/{}/{}/hooks".format(
owner,
repo_name,
),
json={
"name": "web",
"config": {
"url": http_endpoint,
"content_type": "json",
},
"events": events,
},
)
@dataclass
class Gitea(WebgitClient):
"""
A quite thin wrapper around Gitea's REST API.
"""
instance_name: str
def __post_init__(self):
self.headers = {
"Authorization": "token " + self.api_token,
}
self.api_prefix = "https://{}/api/v1".format(self.instance_name)
def add_push_target(self, owner, repo_name, address, username, password):
return self.post(
self.api_prefix + "/repos/{}/{}/push_mirrors".format(
owner, repo_name
),
json={
"interval": "8h0m0s",
"remote_address": address,
"remote_password": password,
"remote_username": username,
"sync_on_commit": True,
},
)
def force_push_target(self, owner, repo_name):
return self.post(
self.api_prefix + "/repos/{}/{}/push_mirrors-sync".format(
owner,
repo_name
),
)
def create_webhook(self, owner, repo_name, http_endpoint, events: list[str]):
return self.post(
self.api_prefix + "/repos/{}/{}/hooks".format(
owner,
repo_name,
),
json={
"active": True,
"config": {
"url": http_endpoint,
"content_type": "json",
},
"events": events,
"type": "gitea",
},
)