airbyte-source-github 2.1.41__tar.gz → 2.2.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {airbyte_source_github-2.1.41 → airbyte_source_github-2.2.0}/PKG-INFO +2 -2
- {airbyte_source_github-2.1.41 → airbyte_source_github-2.2.0}/pyproject.toml +2 -2
- airbyte_source_github-2.2.0/source_github/backoff_strategies.py +61 -0
- {airbyte_source_github-2.1.41 → airbyte_source_github-2.2.0}/source_github/errors_handlers.py +30 -23
- airbyte_source_github-2.2.0/source_github/manifest.yaml +1331 -0
- {airbyte_source_github-2.1.41 → airbyte_source_github-2.2.0}/source_github/source.py +149 -95
- {airbyte_source_github-2.1.41 → airbyte_source_github-2.2.0}/source_github/streams.py +16 -42
- airbyte_source_github-2.2.0/source_github/utils.py +24 -0
- airbyte_source_github-2.1.41/source_github/backoff_strategies.py +0 -52
- airbyte_source_github-2.1.41/source_github/manifest.yaml +0 -6
- airbyte_source_github-2.1.41/source_github/schemas/repositories.json +0 -427
- airbyte_source_github-2.1.41/source_github/spec.json +0 -204
- airbyte_source_github-2.1.41/source_github/utils.py +0 -273
- {airbyte_source_github-2.1.41 → airbyte_source_github-2.2.0}/.airbyte-pypi-readme.md +0 -0
- {airbyte_source_github-2.1.41 → airbyte_source_github-2.2.0}/source_github/__init__.py +0 -0
- {airbyte_source_github-2.1.41 → airbyte_source_github-2.2.0}/source_github/config_migrations.py +0 -0
- {airbyte_source_github-2.1.41 → airbyte_source_github-2.2.0}/source_github/constants.py +0 -0
- {airbyte_source_github-2.1.41 → airbyte_source_github-2.2.0}/source_github/github_schema.py +0 -0
- {airbyte_source_github-2.1.41 → airbyte_source_github-2.2.0}/source_github/graphql.py +0 -0
- {airbyte_source_github-2.1.41 → airbyte_source_github-2.2.0}/source_github/run.py +0 -0
- {airbyte_source_github-2.1.41 → airbyte_source_github-2.2.0}/source_github/schemas/assignees.json +0 -0
- {airbyte_source_github-2.1.41 → airbyte_source_github-2.2.0}/source_github/schemas/branches.json +0 -0
- {airbyte_source_github-2.1.41 → airbyte_source_github-2.2.0}/source_github/schemas/collaborators.json +0 -0
- {airbyte_source_github-2.1.41 → airbyte_source_github-2.2.0}/source_github/schemas/comments.json +0 -0
- {airbyte_source_github-2.1.41 → airbyte_source_github-2.2.0}/source_github/schemas/commit_comment_reactions.json +0 -0
- {airbyte_source_github-2.1.41 → airbyte_source_github-2.2.0}/source_github/schemas/commit_comments.json +0 -0
- {airbyte_source_github-2.1.41 → airbyte_source_github-2.2.0}/source_github/schemas/commits.json +0 -0
- {airbyte_source_github-2.1.41 → airbyte_source_github-2.2.0}/source_github/schemas/contributor_activity.json +0 -0
- {airbyte_source_github-2.1.41 → airbyte_source_github-2.2.0}/source_github/schemas/deployments.json +0 -0
- {airbyte_source_github-2.1.41 → airbyte_source_github-2.2.0}/source_github/schemas/events.json +0 -0
- {airbyte_source_github-2.1.41 → airbyte_source_github-2.2.0}/source_github/schemas/issue_comment_reactions.json +0 -0
- {airbyte_source_github-2.1.41 → airbyte_source_github-2.2.0}/source_github/schemas/issue_events.json +0 -0
- {airbyte_source_github-2.1.41 → airbyte_source_github-2.2.0}/source_github/schemas/issue_labels.json +0 -0
- {airbyte_source_github-2.1.41 → airbyte_source_github-2.2.0}/source_github/schemas/issue_milestones.json +0 -0
- {airbyte_source_github-2.1.41 → airbyte_source_github-2.2.0}/source_github/schemas/issue_reactions.json +0 -0
- {airbyte_source_github-2.1.41 → airbyte_source_github-2.2.0}/source_github/schemas/issue_timeline_events.json +0 -0
- {airbyte_source_github-2.1.41 → airbyte_source_github-2.2.0}/source_github/schemas/issues.json +0 -0
- {airbyte_source_github-2.1.41 → airbyte_source_github-2.2.0}/source_github/schemas/organizations.json +0 -0
- {airbyte_source_github-2.1.41 → airbyte_source_github-2.2.0}/source_github/schemas/project_cards.json +0 -0
- {airbyte_source_github-2.1.41 → airbyte_source_github-2.2.0}/source_github/schemas/project_columns.json +0 -0
- {airbyte_source_github-2.1.41 → airbyte_source_github-2.2.0}/source_github/schemas/projects.json +0 -0
- {airbyte_source_github-2.1.41 → airbyte_source_github-2.2.0}/source_github/schemas/projects_v2.json +0 -0
- {airbyte_source_github-2.1.41 → airbyte_source_github-2.2.0}/source_github/schemas/pull_request_comment_reactions.json +0 -0
- {airbyte_source_github-2.1.41 → airbyte_source_github-2.2.0}/source_github/schemas/pull_request_commits.json +0 -0
- {airbyte_source_github-2.1.41 → airbyte_source_github-2.2.0}/source_github/schemas/pull_request_stats.json +0 -0
- {airbyte_source_github-2.1.41 → airbyte_source_github-2.2.0}/source_github/schemas/pull_requests.json +0 -0
- {airbyte_source_github-2.1.41 → airbyte_source_github-2.2.0}/source_github/schemas/releases.json +0 -0
- {airbyte_source_github-2.1.41 → airbyte_source_github-2.2.0}/source_github/schemas/review_comments.json +0 -0
- {airbyte_source_github-2.1.41 → airbyte_source_github-2.2.0}/source_github/schemas/reviews.json +0 -0
- {airbyte_source_github-2.1.41 → airbyte_source_github-2.2.0}/source_github/schemas/shared/events/comment.json +0 -0
- {airbyte_source_github-2.1.41 → airbyte_source_github-2.2.0}/source_github/schemas/shared/events/commented.json +0 -0
- {airbyte_source_github-2.1.41 → airbyte_source_github-2.2.0}/source_github/schemas/shared/events/committed.json +0 -0
- {airbyte_source_github-2.1.41 → airbyte_source_github-2.2.0}/source_github/schemas/shared/events/cross_referenced.json +0 -0
- {airbyte_source_github-2.1.41 → airbyte_source_github-2.2.0}/source_github/schemas/shared/events/reviewed.json +0 -0
- {airbyte_source_github-2.1.41 → airbyte_source_github-2.2.0}/source_github/schemas/shared/reaction.json +0 -0
- {airbyte_source_github-2.1.41 → airbyte_source_github-2.2.0}/source_github/schemas/shared/reactions.json +0 -0
- {airbyte_source_github-2.1.41 → airbyte_source_github-2.2.0}/source_github/schemas/shared/user.json +0 -0
- {airbyte_source_github-2.1.41 → airbyte_source_github-2.2.0}/source_github/schemas/shared/user_graphql.json +0 -0
- {airbyte_source_github-2.1.41 → airbyte_source_github-2.2.0}/source_github/schemas/stargazers.json +0 -0
- {airbyte_source_github-2.1.41 → airbyte_source_github-2.2.0}/source_github/schemas/tags.json +0 -0
- {airbyte_source_github-2.1.41 → airbyte_source_github-2.2.0}/source_github/schemas/team_members.json +0 -0
- {airbyte_source_github-2.1.41 → airbyte_source_github-2.2.0}/source_github/schemas/team_memberships.json +0 -0
- {airbyte_source_github-2.1.41 → airbyte_source_github-2.2.0}/source_github/schemas/teams.json +0 -0
- {airbyte_source_github-2.1.41 → airbyte_source_github-2.2.0}/source_github/schemas/users.json +0 -0
- {airbyte_source_github-2.1.41 → airbyte_source_github-2.2.0}/source_github/schemas/workflow_jobs.json +0 -0
- {airbyte_source_github-2.1.41 → airbyte_source_github-2.2.0}/source_github/schemas/workflow_runs.json +0 -0
- {airbyte_source_github-2.1.41 → airbyte_source_github-2.2.0}/source_github/schemas/workflows.json +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: airbyte-source-github
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.2.0
|
|
4
4
|
Summary: Source implementation for GitHub.
|
|
5
5
|
Home-page: https://airbyte.com
|
|
6
6
|
License: ELv2
|
|
@@ -13,7 +13,7 @@ Classifier: Programming Language :: Python :: 3.10
|
|
|
13
13
|
Classifier: Programming Language :: Python :: 3.11
|
|
14
14
|
Classifier: Programming Language :: Python :: 3.12
|
|
15
15
|
Classifier: Programming Language :: Python :: 3.13
|
|
16
|
-
Requires-Dist: airbyte-cdk (>=7.
|
|
16
|
+
Requires-Dist: airbyte-cdk (>=7.28.2,<8.0.0)
|
|
17
17
|
Requires-Dist: sgqlc (==16.3)
|
|
18
18
|
Project-URL: Documentation, https://docs.airbyte.com/integrations/sources/github
|
|
19
19
|
Project-URL: Repository, https://github.com/airbytehq/airbyte
|
|
@@ -3,7 +3,7 @@ requires = [ "poetry-core>=1.0.0",]
|
|
|
3
3
|
build-backend = "poetry.core.masonry.api"
|
|
4
4
|
|
|
5
5
|
[tool.poetry]
|
|
6
|
-
version = "2.
|
|
6
|
+
version = "2.2.0"
|
|
7
7
|
name = "airbyte-source-github"
|
|
8
8
|
description = "Source implementation for GitHub."
|
|
9
9
|
authors = [ "Airbyte <contact@airbyte.io>",]
|
|
@@ -17,7 +17,7 @@ include = "source_github"
|
|
|
17
17
|
|
|
18
18
|
[tool.poetry.dependencies]
|
|
19
19
|
python = "^3.10,<3.14"
|
|
20
|
-
airbyte-cdk = "^7.
|
|
20
|
+
airbyte-cdk = "^7.28.2"
|
|
21
21
|
sgqlc = "==16.3"
|
|
22
22
|
|
|
23
23
|
[tool.poetry.scripts]
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
#
|
|
2
|
+
# Copyright (c) 2023 Airbyte, Inc., all rights reserved.
|
|
3
|
+
#
|
|
4
|
+
|
|
5
|
+
import time
|
|
6
|
+
from typing import Any, Optional, Union
|
|
7
|
+
|
|
8
|
+
import requests
|
|
9
|
+
|
|
10
|
+
from airbyte_cdk import BackoffStrategy
|
|
11
|
+
from airbyte_cdk.sources.streams.http import HttpStream
|
|
12
|
+
|
|
13
|
+
from .errors_handlers import is_rate_limited_response
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class GithubStreamABCBackoffStrategy(BackoffStrategy):
|
|
17
|
+
def __init__(self, stream: HttpStream, max_wait_time_seconds: float = 120 * 60, **kwargs): # type: ignore # noqa
|
|
18
|
+
self.stream = stream
|
|
19
|
+
self.max_wait_time_seconds = max_wait_time_seconds
|
|
20
|
+
super().__init__(**kwargs)
|
|
21
|
+
|
|
22
|
+
def backoff_time(
|
|
23
|
+
self, response_or_exception: Optional[Union[requests.Response, requests.RequestException]], **kwargs: Any
|
|
24
|
+
) -> Optional[float]:
|
|
25
|
+
if isinstance(response_or_exception, requests.Response):
|
|
26
|
+
if not is_rate_limited_response(
|
|
27
|
+
response_or_exception,
|
|
28
|
+
self.stream.check_graphql_rate_limited,
|
|
29
|
+
self.stream.logger,
|
|
30
|
+
):
|
|
31
|
+
return None
|
|
32
|
+
|
|
33
|
+
min_backoff_time = 60.0
|
|
34
|
+
retry_after = response_or_exception.headers.get("Retry-After")
|
|
35
|
+
if retry_after is not None:
|
|
36
|
+
wait_time = max(float(retry_after), min_backoff_time)
|
|
37
|
+
else:
|
|
38
|
+
reset_time = response_or_exception.headers.get("X-RateLimit-Reset")
|
|
39
|
+
if not reset_time:
|
|
40
|
+
return None
|
|
41
|
+
wait_time = max(float(reset_time) - time.time(), min_backoff_time)
|
|
42
|
+
|
|
43
|
+
if wait_time > self.max_wait_time_seconds:
|
|
44
|
+
self.stream.logger.warning(
|
|
45
|
+
"Rate limit wait for stream `%s` is %.2f seconds, exceeding the maximum of %.2f seconds; using default backoff.",
|
|
46
|
+
self.stream.name,
|
|
47
|
+
wait_time,
|
|
48
|
+
self.max_wait_time_seconds,
|
|
49
|
+
)
|
|
50
|
+
return None
|
|
51
|
+
return wait_time
|
|
52
|
+
return None
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
class ContributorActivityBackoffStrategy(BackoffStrategy):
|
|
56
|
+
def backoff_time(
|
|
57
|
+
self, response_or_exception: Optional[Union[requests.Response, requests.RequestException]], **kwargs: Any
|
|
58
|
+
) -> Optional[float]:
|
|
59
|
+
if isinstance(response_or_exception, requests.Response) and response_or_exception.status_code == requests.codes.ACCEPTED:
|
|
60
|
+
return 90
|
|
61
|
+
return None
|
{airbyte_source_github-2.1.41 → airbyte_source_github-2.2.0}/source_github/errors_handlers.py
RENAMED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
#
|
|
4
4
|
|
|
5
5
|
import logging
|
|
6
|
-
from typing import Optional, Union
|
|
6
|
+
from typing import Callable, Optional, Union
|
|
7
7
|
|
|
8
8
|
import requests
|
|
9
9
|
|
|
@@ -84,38 +84,45 @@ def is_gone_with_feature_disabled(response_or_exception: Optional[Union[requests
|
|
|
84
84
|
return False
|
|
85
85
|
|
|
86
86
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
87
|
+
def is_rate_limited_response(
|
|
88
|
+
response: requests.Response, graphql_rate_limit_checker: Callable[[dict], bool], logger: logging.Logger
|
|
89
|
+
) -> bool:
|
|
90
|
+
# GitHub GraphQL resource limitations:
|
|
91
|
+
# https://docs.github.com/en/graphql/overview/resource-limitations
|
|
92
|
+
if response.headers.get("X-RateLimit-Resource") == "graphql":
|
|
93
93
|
try:
|
|
94
94
|
body = response.json()
|
|
95
95
|
except ValueError:
|
|
96
|
-
|
|
96
|
+
logger.warning(
|
|
97
97
|
"GraphQL rate-limit check received non-JSON response (HTTP %s, first 50 chars: %r).",
|
|
98
98
|
response.status_code,
|
|
99
99
|
response.text[:50],
|
|
100
100
|
)
|
|
101
|
-
|
|
102
|
-
|
|
101
|
+
graphql_rate_limited = False
|
|
102
|
+
else:
|
|
103
|
+
graphql_rate_limited = graphql_rate_limit_checker(body or {})
|
|
104
|
+
|
|
105
|
+
if graphql_rate_limited:
|
|
106
|
+
return True
|
|
107
|
+
|
|
108
|
+
# GitHub REST rate-limit HTTP headers:
|
|
109
|
+
# https://docs.github.com/en/rest/overview/resources-in-the-rest-api#rate-limit-http-headers
|
|
110
|
+
# GitHub secondary rate limits:
|
|
111
|
+
# https://docs.github.com/en/rest/overview/resources-in-the-rest-api#secondary-rate-limits
|
|
112
|
+
return (response.status_code != 200 and response.headers.get("X-RateLimit-Remaining") == "0") or "Retry-After" in response.headers
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
class GithubStreamABCErrorHandler(HttpStatusErrorHandler):
|
|
116
|
+
def __init__(self, stream: HttpStream, **kwargs): # type: ignore # noqa
|
|
117
|
+
self.stream = stream
|
|
118
|
+
super().__init__(**kwargs)
|
|
103
119
|
|
|
104
120
|
def interpret_response(self, response_or_exception: Optional[Union[requests.Response, Exception]] = None) -> ErrorResolution:
|
|
105
121
|
if isinstance(response_or_exception, requests.Response):
|
|
106
|
-
retry_flag = (
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
response_or_exception.headers.get("X-RateLimit-Resource") == "graphql"
|
|
111
|
-
and self._safe_json_check_graphql_rate_limited(response_or_exception)
|
|
112
|
-
)
|
|
113
|
-
# Rate limit HTTP headers
|
|
114
|
-
# https://docs.github.com/en/rest/overview/resources-in-the-rest-api#rate-limit-http-headers
|
|
115
|
-
or (response_or_exception.status_code != 200 and response_or_exception.headers.get("X-RateLimit-Remaining") == "0")
|
|
116
|
-
# Secondary rate limits
|
|
117
|
-
# https://docs.github.com/en/rest/overview/resources-in-the-rest-api#secondary-rate-limits
|
|
118
|
-
or "Retry-After" in response_or_exception.headers
|
|
122
|
+
retry_flag = is_rate_limited_response(
|
|
123
|
+
response_or_exception,
|
|
124
|
+
self.stream.check_graphql_rate_limited,
|
|
125
|
+
self._logger,
|
|
119
126
|
)
|
|
120
127
|
if retry_flag:
|
|
121
128
|
headers = [
|