-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy path_github.py
More file actions
74 lines (61 loc) · 2.43 KB
/
Copy path_github.py
File metadata and controls
74 lines (61 loc) · 2.43 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
"""Lightweight GitHub GraphQL client (stdlib only) shared by discussion bot & router.
Provides a single :class:`GitHubGraphQL` class that both
:file:`discussion_bot.py` and :file:`router.py` can import, eliminating the
duplicate ``GQL`` / ``GitHubGraphQL`` implementations.
"""
from __future__ import annotations
import json
import urllib.error
import urllib.request
from typing import Optional
GITHUB_GRAPHQL_URL = "https://api.github.com/graphql"
class GitHubGraphQL:
"""Minimal GitHub GraphQL client using only stdlib :mod:`urllib`.
Usage::
client = GitHubGraphQL("ghp_xxxx")
data = client.query("query { viewer { login } }")
"""
# Accept: GitHub recommends this preview header so the API includes
# ``isAnswerable`` etc. on DiscussionCategory nodes.
_DEFAULT_ACCEPT = "application/vnd.github.v3+json"
def __init__(
self,
token: str,
*,
user_agent: str = "github-graphql/1.0",
) -> None:
if not token:
raise ValueError("GitHub token (CSM_QA_GH_TOKEN) 未配置")
self._token = token
self._user_agent = user_agent
def query(self, gql: str, variables: Optional[dict] = None) -> dict:
"""Execute a GraphQL query and return the ``data`` node.
Raises:
RuntimeError: On HTTP errors or when the response contains
a non-empty ``errors`` field.
"""
payload = json.dumps({"query": gql, "variables": variables or {}}).encode()
req = urllib.request.Request(
GITHUB_GRAPHQL_URL,
data=payload,
headers={
"Authorization": f"Bearer {self._token}",
"Content-Type": "application/json",
"Accept": self._DEFAULT_ACCEPT,
"User-Agent": self._user_agent,
},
method="POST",
)
try:
with urllib.request.urlopen(req, timeout=30) as resp:
raw = resp.read()
except urllib.error.HTTPError as exc:
body = exc.read().decode("utf-8", errors="replace")
raise RuntimeError(
f"GitHub GraphQL HTTP {exc.code}: {body[:400]}"
) from exc
result: dict = json.loads(raw)
if result.get("errors"):
messages = "; ".join(e.get("message", "") for e in result["errors"])
raise RuntimeError(f"GitHub GraphQL errors: {messages}")
return result.get("data", {})