Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 33 additions & 29 deletions backend/core/services/github_graphql_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ class GitHubGraphQLClient:
def __init__(self, token: str):
self.token = token
self.endpoint = "https://api.github.com/graphql"

self.transport = RequestsHTTPTransport(
url=self.endpoint,
headers={
Expand All @@ -26,7 +26,7 @@ def __init__(self, token: str):
timeout=30,
retries=3
)

self.client = Client(transport=self.transport, fetch_schema_from_transport=False)

def _execute_query(self, query: str, variables: Optional[Dict[str, Any]] = None) -> Dict[str, Any]:
Expand All @@ -46,16 +46,16 @@ def _execute_query(self, query: str, variables: Optional[Dict[str, Any]] = None)

except TransportServerError as e:
error_data = str(e)

if "rate limit" in error_data.lower() or "api rate limit exceeded" in error_data.lower():
logger.warning(f"Rate limit hit, waiting {retry_delay * (2 ** attempt)} seconds")
time.sleep(retry_delay * (2 ** attempt))
continue

if "bad credentials" in error_data.lower() or "unauthorized" in error_data.lower():
logger.error(f"Authentication error: {e}")
raise

logger.error(f"GraphQL execution error: {e}")
if attempt < max_retries - 1:
time.sleep(retry_delay * (2 ** attempt))
Expand All @@ -72,21 +72,25 @@ def _execute_query(self, query: str, variables: Optional[Dict[str, Any]] = None)

raise Exception("Max retries exceeded")

def get_issues_with_comments(self, owner: str, repo: str,
states: List[str] = None,
def get_issues_with_comments(self, owner: str, repo: str,
states: List[str] = None,
since: Optional[str] = None,
first: int = 100,
after_cursor: Optional[str] = None) -> Dict[str, Any]:
"""
Get issues with their comments in a single GraphQL query

Args:
owner: Repository owner
repo: Repository name
states: List of issue states (OPEN, CLOSED, etc.)
since: ISO datetime string for filtering by creation date
first: Number of items per page
after_cursor: Pagination cursor

Returns:
Dictionary containing issues and pagination info
"""
if states is None:
states = ["OPEN", "CLOSED"]

Expand Down Expand Up @@ -166,18 +170,18 @@ def get_issues_with_comments(self, owner: str, repo: str,
"direction": "DESC"
}
}

if after_cursor:
variables["after"] = after_cursor

return self._execute_query(query, variables)

def get_all_issues_with_comments(self, owner: str, repo: str,
def get_all_issues_with_comments(self, owner: str, repo: str,
states: List[str] = None,
since: Optional[str] = None) -> List[Dict[str, Any]]:
"""
Get all issues with comments, handling pagination automatically

Returns:
List of all issues with their comments
"""
Expand All @@ -187,29 +191,29 @@ def get_all_issues_with_comments(self, owner: str, repo: str,

while has_next_page:
result = self.get_issues_with_comments(
owner=owner,
repo=repo,
owner=owner,
repo=repo,
states=states,
since=since,
after_cursor=after_cursor
)

repository_data = result.get('repository')
if not repository_data:
break

issues_data = repository_data.get('issues', {})
edges = issues_data.get('edges', [])

for edge in edges:
issue_node = edge.get('node', {})
if issue_node:
all_issues.append(issue_node)

page_info = issues_data.get('pageInfo', {})
has_next_page = page_info.get('hasNextPage', False)
after_cursor = page_info.get('endCursor')

logger.info(f"Fetched {len(edges)} issues, total so far: {len(all_issues)}")

logger.info(f"Total issues fetched: {len(all_issues)}")
Expand All @@ -220,7 +224,7 @@ def get_all_pull_requests_with_comments(self, owner: str, repo: str,
since: Optional[str] = None) -> List[Dict[str, Any]]:
"""
Get all pull requests with comments, handling pagination automatically

Returns:
List of all pull requests with their comments
"""
Expand All @@ -230,29 +234,29 @@ def get_all_pull_requests_with_comments(self, owner: str, repo: str,

while has_next_page:
result = self.get_pull_requests_with_comments(
owner=owner,
repo=repo,
owner=owner,
repo=repo,
states=states,
since=since,
after_cursor=after_cursor
)

repository_data = result.get('repository')
if not repository_data:
break

prs_data = repository_data.get('pullRequests', {})
edges = prs_data.get('edges', [])

for edge in edges:
pr_node = edge.get('node', {})
if pr_node:
all_prs.append(pr_node)

page_info = prs_data.get('pageInfo', {})
has_next_page = page_info.get('hasNextPage', False)
after_cursor = page_info.get('endCursor')

logger.info(f"Fetched {len(edges)} pull requests, total so far: {len(all_prs)}")

logger.info(f"Total pull requests fetched: {len(all_prs)}")
Expand All @@ -265,15 +269,15 @@ def get_pull_requests_with_comments(self, owner: str, repo: str,
after_cursor: Optional[str] = None) -> Dict[str, Any]:
"""
Get pull requests with their comments in a single GraphQL query

Args:
owner: Repository owner
repo: Repository name
states: List of PR states (OPEN, CLOSED, MERGED)
since: ISO datetime string for filtering by creation date
first: Number of items per page
after_cursor: Pagination cursor

Returns:
Dictionary containing pull requests and pagination info
"""
Expand Down Expand Up @@ -363,7 +367,7 @@ def get_pull_requests_with_comments(self, owner: str, repo: str,
"direction": "DESC"
}
}

if after_cursor:
variables["after"] = after_cursor

Expand Down
Loading