airbyte-source-github 2.1.26__tar.gz → 2.1.27__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.26 → airbyte_source_github-2.1.27}/PKG-INFO +1 -1
- {airbyte_source_github-2.1.26 → airbyte_source_github-2.1.27}/pyproject.toml +1 -1
- {airbyte_source_github-2.1.26 → airbyte_source_github-2.1.27}/source_github/errors_handlers.py +37 -3
- {airbyte_source_github-2.1.26 → airbyte_source_github-2.1.27}/source_github/streams.py +49 -6
- {airbyte_source_github-2.1.26 → airbyte_source_github-2.1.27}/README.md +0 -0
- {airbyte_source_github-2.1.26 → airbyte_source_github-2.1.27}/source_github/__init__.py +0 -0
- {airbyte_source_github-2.1.26 → airbyte_source_github-2.1.27}/source_github/backoff_strategies.py +0 -0
- {airbyte_source_github-2.1.26 → airbyte_source_github-2.1.27}/source_github/config_migrations.py +0 -0
- {airbyte_source_github-2.1.26 → airbyte_source_github-2.1.27}/source_github/constants.py +0 -0
- {airbyte_source_github-2.1.26 → airbyte_source_github-2.1.27}/source_github/github_schema.py +0 -0
- {airbyte_source_github-2.1.26 → airbyte_source_github-2.1.27}/source_github/graphql.py +0 -0
- {airbyte_source_github-2.1.26 → airbyte_source_github-2.1.27}/source_github/run.py +0 -0
- {airbyte_source_github-2.1.26 → airbyte_source_github-2.1.27}/source_github/schemas/assignees.json +0 -0
- {airbyte_source_github-2.1.26 → airbyte_source_github-2.1.27}/source_github/schemas/branches.json +0 -0
- {airbyte_source_github-2.1.26 → airbyte_source_github-2.1.27}/source_github/schemas/collaborators.json +0 -0
- {airbyte_source_github-2.1.26 → airbyte_source_github-2.1.27}/source_github/schemas/comments.json +0 -0
- {airbyte_source_github-2.1.26 → airbyte_source_github-2.1.27}/source_github/schemas/commit_comment_reactions.json +0 -0
- {airbyte_source_github-2.1.26 → airbyte_source_github-2.1.27}/source_github/schemas/commit_comments.json +0 -0
- {airbyte_source_github-2.1.26 → airbyte_source_github-2.1.27}/source_github/schemas/commits.json +0 -0
- {airbyte_source_github-2.1.26 → airbyte_source_github-2.1.27}/source_github/schemas/contributor_activity.json +0 -0
- {airbyte_source_github-2.1.26 → airbyte_source_github-2.1.27}/source_github/schemas/deployments.json +0 -0
- {airbyte_source_github-2.1.26 → airbyte_source_github-2.1.27}/source_github/schemas/events.json +0 -0
- {airbyte_source_github-2.1.26 → airbyte_source_github-2.1.27}/source_github/schemas/issue_comment_reactions.json +0 -0
- {airbyte_source_github-2.1.26 → airbyte_source_github-2.1.27}/source_github/schemas/issue_events.json +0 -0
- {airbyte_source_github-2.1.26 → airbyte_source_github-2.1.27}/source_github/schemas/issue_labels.json +0 -0
- {airbyte_source_github-2.1.26 → airbyte_source_github-2.1.27}/source_github/schemas/issue_milestones.json +0 -0
- {airbyte_source_github-2.1.26 → airbyte_source_github-2.1.27}/source_github/schemas/issue_reactions.json +0 -0
- {airbyte_source_github-2.1.26 → airbyte_source_github-2.1.27}/source_github/schemas/issue_timeline_events.json +0 -0
- {airbyte_source_github-2.1.26 → airbyte_source_github-2.1.27}/source_github/schemas/issues.json +0 -0
- {airbyte_source_github-2.1.26 → airbyte_source_github-2.1.27}/source_github/schemas/organizations.json +0 -0
- {airbyte_source_github-2.1.26 → airbyte_source_github-2.1.27}/source_github/schemas/project_cards.json +0 -0
- {airbyte_source_github-2.1.26 → airbyte_source_github-2.1.27}/source_github/schemas/project_columns.json +0 -0
- {airbyte_source_github-2.1.26 → airbyte_source_github-2.1.27}/source_github/schemas/projects.json +0 -0
- {airbyte_source_github-2.1.26 → airbyte_source_github-2.1.27}/source_github/schemas/projects_v2.json +0 -0
- {airbyte_source_github-2.1.26 → airbyte_source_github-2.1.27}/source_github/schemas/pull_request_comment_reactions.json +0 -0
- {airbyte_source_github-2.1.26 → airbyte_source_github-2.1.27}/source_github/schemas/pull_request_commits.json +0 -0
- {airbyte_source_github-2.1.26 → airbyte_source_github-2.1.27}/source_github/schemas/pull_request_stats.json +0 -0
- {airbyte_source_github-2.1.26 → airbyte_source_github-2.1.27}/source_github/schemas/pull_requests.json +0 -0
- {airbyte_source_github-2.1.26 → airbyte_source_github-2.1.27}/source_github/schemas/releases.json +0 -0
- {airbyte_source_github-2.1.26 → airbyte_source_github-2.1.27}/source_github/schemas/repositories.json +0 -0
- {airbyte_source_github-2.1.26 → airbyte_source_github-2.1.27}/source_github/schemas/review_comments.json +0 -0
- {airbyte_source_github-2.1.26 → airbyte_source_github-2.1.27}/source_github/schemas/reviews.json +0 -0
- {airbyte_source_github-2.1.26 → airbyte_source_github-2.1.27}/source_github/schemas/shared/events/comment.json +0 -0
- {airbyte_source_github-2.1.26 → airbyte_source_github-2.1.27}/source_github/schemas/shared/events/commented.json +0 -0
- {airbyte_source_github-2.1.26 → airbyte_source_github-2.1.27}/source_github/schemas/shared/events/committed.json +0 -0
- {airbyte_source_github-2.1.26 → airbyte_source_github-2.1.27}/source_github/schemas/shared/events/cross_referenced.json +0 -0
- {airbyte_source_github-2.1.26 → airbyte_source_github-2.1.27}/source_github/schemas/shared/events/reviewed.json +0 -0
- {airbyte_source_github-2.1.26 → airbyte_source_github-2.1.27}/source_github/schemas/shared/reaction.json +0 -0
- {airbyte_source_github-2.1.26 → airbyte_source_github-2.1.27}/source_github/schemas/shared/reactions.json +0 -0
- {airbyte_source_github-2.1.26 → airbyte_source_github-2.1.27}/source_github/schemas/shared/user.json +0 -0
- {airbyte_source_github-2.1.26 → airbyte_source_github-2.1.27}/source_github/schemas/shared/user_graphql.json +0 -0
- {airbyte_source_github-2.1.26 → airbyte_source_github-2.1.27}/source_github/schemas/stargazers.json +0 -0
- {airbyte_source_github-2.1.26 → airbyte_source_github-2.1.27}/source_github/schemas/tags.json +0 -0
- {airbyte_source_github-2.1.26 → airbyte_source_github-2.1.27}/source_github/schemas/team_members.json +0 -0
- {airbyte_source_github-2.1.26 → airbyte_source_github-2.1.27}/source_github/schemas/team_memberships.json +0 -0
- {airbyte_source_github-2.1.26 → airbyte_source_github-2.1.27}/source_github/schemas/teams.json +0 -0
- {airbyte_source_github-2.1.26 → airbyte_source_github-2.1.27}/source_github/schemas/users.json +0 -0
- {airbyte_source_github-2.1.26 → airbyte_source_github-2.1.27}/source_github/schemas/workflow_jobs.json +0 -0
- {airbyte_source_github-2.1.26 → airbyte_source_github-2.1.27}/source_github/schemas/workflow_runs.json +0 -0
- {airbyte_source_github-2.1.26 → airbyte_source_github-2.1.27}/source_github/schemas/workflows.json +0 -0
- {airbyte_source_github-2.1.26 → airbyte_source_github-2.1.27}/source_github/source.py +0 -0
- {airbyte_source_github-2.1.26 → airbyte_source_github-2.1.27}/source_github/spec.json +0 -0
- {airbyte_source_github-2.1.26 → airbyte_source_github-2.1.27}/source_github/utils.py +0 -0
|
@@ -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.1.
|
|
6
|
+
version = "2.1.27"
|
|
7
7
|
name = "airbyte-source-github"
|
|
8
8
|
description = "Source implementation for GitHub."
|
|
9
9
|
authors = [ "Airbyte <contact@airbyte.io>",]
|
{airbyte_source_github-2.1.26 → airbyte_source_github-2.1.27}/source_github/errors_handlers.py
RENAMED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
# Copyright (c) 2023 Airbyte, Inc., all rights reserved.
|
|
3
3
|
#
|
|
4
4
|
|
|
5
|
+
import logging
|
|
5
6
|
from typing import Optional, Union
|
|
6
7
|
|
|
7
8
|
import requests
|
|
@@ -14,6 +15,9 @@ from airbyte_cdk.sources.streams.http.error_handlers.default_error_mapping impor
|
|
|
14
15
|
from . import constants
|
|
15
16
|
|
|
16
17
|
|
|
18
|
+
logger = logging.getLogger("airbyte")
|
|
19
|
+
|
|
20
|
+
|
|
17
21
|
GITHUB_DEFAULT_ERROR_MAPPING = DEFAULT_ERROR_MAPPING | {
|
|
18
22
|
401: ErrorResolution(
|
|
19
23
|
response_action=ResponseAction.RETRY,
|
|
@@ -54,7 +58,14 @@ GITHUB_DEFAULT_ERROR_MAPPING = DEFAULT_ERROR_MAPPING | {
|
|
|
54
58
|
|
|
55
59
|
def is_conflict_with_empty_repository(response_or_exception: Optional[Union[requests.Response, Exception]] = None) -> bool:
|
|
56
60
|
if isinstance(response_or_exception, requests.Response) and response_or_exception.status_code == requests.codes.CONFLICT:
|
|
57
|
-
|
|
61
|
+
try:
|
|
62
|
+
response_data = response_or_exception.json()
|
|
63
|
+
except ValueError:
|
|
64
|
+
logger.warning(
|
|
65
|
+
"is_conflict_with_empty_repository received non-JSON 409 response (first 50 chars: %r).",
|
|
66
|
+
response_or_exception.text[:50],
|
|
67
|
+
)
|
|
68
|
+
return False
|
|
58
69
|
return response_data.get("message") == "Git Repository is empty."
|
|
59
70
|
return False
|
|
60
71
|
|
|
@@ -64,6 +75,10 @@ def is_gone_with_feature_disabled(response_or_exception: Optional[Union[requests
|
|
|
64
75
|
try:
|
|
65
76
|
message = (response_or_exception.json().get("message") or "").lower()
|
|
66
77
|
except ValueError:
|
|
78
|
+
logger.warning(
|
|
79
|
+
"is_gone_with_feature_disabled received non-JSON 410 response (first 50 chars: %r).",
|
|
80
|
+
response_or_exception.text[:50],
|
|
81
|
+
)
|
|
67
82
|
return False
|
|
68
83
|
return "are disabled" in message or "is disabled" in message
|
|
69
84
|
return False
|
|
@@ -74,6 +89,18 @@ class GithubStreamABCErrorHandler(HttpStatusErrorHandler):
|
|
|
74
89
|
self.stream = stream
|
|
75
90
|
super().__init__(**kwargs)
|
|
76
91
|
|
|
92
|
+
def _safe_json_check_graphql_rate_limited(self, response: requests.Response) -> bool:
|
|
93
|
+
try:
|
|
94
|
+
body = response.json()
|
|
95
|
+
except ValueError:
|
|
96
|
+
self._logger.warning(
|
|
97
|
+
"GraphQL rate-limit check received non-JSON response (HTTP %s, first 50 chars: %r).",
|
|
98
|
+
response.status_code,
|
|
99
|
+
response.text[:50],
|
|
100
|
+
)
|
|
101
|
+
return False
|
|
102
|
+
return self.stream.check_graphql_rate_limited(body or {})
|
|
103
|
+
|
|
77
104
|
def interpret_response(self, response_or_exception: Optional[Union[requests.Response, Exception]] = None) -> ErrorResolution:
|
|
78
105
|
if isinstance(response_or_exception, requests.Response):
|
|
79
106
|
retry_flag = (
|
|
@@ -81,7 +108,7 @@ class GithubStreamABCErrorHandler(HttpStatusErrorHandler):
|
|
|
81
108
|
# https://docs.github.com/en/graphql/overview/resource-limitations
|
|
82
109
|
(
|
|
83
110
|
response_or_exception.headers.get("X-RateLimit-Resource") == "graphql"
|
|
84
|
-
and self.
|
|
111
|
+
and self._safe_json_check_graphql_rate_limited(response_or_exception)
|
|
85
112
|
)
|
|
86
113
|
# Rate limit HTTP headers
|
|
87
114
|
# https://docs.github.com/en/rest/overview/resources-in-the-rest-api#rate-limit-http-headers
|
|
@@ -162,6 +189,13 @@ class ContributorActivityErrorHandler(GithubStreamABCErrorHandler):
|
|
|
162
189
|
|
|
163
190
|
|
|
164
191
|
class GitHubGraphQLErrorHandler(GithubStreamABCErrorHandler):
|
|
192
|
+
def _safe_json_get_errors(self, response: requests.Response) -> bool:
|
|
193
|
+
try:
|
|
194
|
+
body = response.json()
|
|
195
|
+
except ValueError:
|
|
196
|
+
return False
|
|
197
|
+
return bool((body or {}).get("errors"))
|
|
198
|
+
|
|
165
199
|
def interpret_response(self, response_or_exception: Optional[Union[requests.Response, Exception]] = None) -> ErrorResolution:
|
|
166
200
|
if isinstance(response_or_exception, requests.Response):
|
|
167
201
|
if response_or_exception.status_code in (requests.codes.BAD_GATEWAY, requests.codes.GATEWAY_TIMEOUT):
|
|
@@ -176,7 +210,7 @@ class GitHubGraphQLErrorHandler(GithubStreamABCErrorHandler):
|
|
|
176
210
|
constants.DEFAULT_PAGE_SIZE_FOR_LARGE_STREAM if self.stream.large_stream else constants.DEFAULT_PAGE_SIZE
|
|
177
211
|
)
|
|
178
212
|
|
|
179
|
-
if
|
|
213
|
+
if self._safe_json_get_errors(response_or_exception):
|
|
180
214
|
return ErrorResolution(
|
|
181
215
|
response_action=ResponseAction.RETRY,
|
|
182
216
|
failure_type=FailureType.transient_error,
|
|
@@ -251,6 +251,39 @@ class GithubStream(GithubStreamABC):
|
|
|
251
251
|
|
|
252
252
|
return record
|
|
253
253
|
|
|
254
|
+
def _safe_json_list(self, response: requests.Response, key: Optional[str] = None) -> Optional[list]:
|
|
255
|
+
"""Parse JSON from `response` and return a list, or ``None`` on failure.
|
|
256
|
+
|
|
257
|
+
When `key` is provided the body is expected to be a dict and the list is
|
|
258
|
+
extracted via ``body[key]``. When `key` is ``None`` the body itself must
|
|
259
|
+
be a list. On any parse/validation failure a warning is logged and
|
|
260
|
+
``None`` is returned so callers can short-circuit gracefully.
|
|
261
|
+
"""
|
|
262
|
+
try:
|
|
263
|
+
body = response.json()
|
|
264
|
+
except ValueError:
|
|
265
|
+
self.logger.warning(
|
|
266
|
+
"`%s` received non-JSON response (HTTP %s, first 50 chars: %r).",
|
|
267
|
+
self.name,
|
|
268
|
+
response.status_code,
|
|
269
|
+
response.text[:50],
|
|
270
|
+
)
|
|
271
|
+
return None
|
|
272
|
+
if key is not None:
|
|
273
|
+
items = (body or {}).get(key)
|
|
274
|
+
else:
|
|
275
|
+
items = body
|
|
276
|
+
if not isinstance(items, list):
|
|
277
|
+
self.logger.warning(
|
|
278
|
+
"`%s` response has unexpected structure (HTTP %s, key=%r, got %s).",
|
|
279
|
+
self.name,
|
|
280
|
+
response.status_code,
|
|
281
|
+
key,
|
|
282
|
+
type(items).__name__,
|
|
283
|
+
)
|
|
284
|
+
return None
|
|
285
|
+
return items
|
|
286
|
+
|
|
254
287
|
def parse_response(
|
|
255
288
|
self,
|
|
256
289
|
response: requests.Response,
|
|
@@ -1595,8 +1628,10 @@ class Workflows(SemiIncrementalMixin, GithubStream):
|
|
|
1595
1628
|
return f"repos/{stream_slice['repository']}/actions/workflows"
|
|
1596
1629
|
|
|
1597
1630
|
def parse_response(self, response: requests.Response, stream_slice: Mapping[str, Any] = None, **kwargs) -> Iterable[Mapping]:
|
|
1598
|
-
|
|
1599
|
-
|
|
1631
|
+
items = self._safe_json_list(response, key="workflows")
|
|
1632
|
+
if items is None:
|
|
1633
|
+
return
|
|
1634
|
+
for record in items:
|
|
1600
1635
|
yield self.transform(record=record, stream_slice=stream_slice)
|
|
1601
1636
|
|
|
1602
1637
|
def convert_cursor_value(self, value):
|
|
@@ -1620,8 +1655,10 @@ class WorkflowRuns(SemiIncrementalMixin, GithubStream):
|
|
|
1620
1655
|
return f"repos/{stream_slice['repository']}/actions/runs"
|
|
1621
1656
|
|
|
1622
1657
|
def parse_response(self, response: requests.Response, stream_slice: Mapping[str, Any] = None, **kwargs) -> Iterable[Mapping]:
|
|
1623
|
-
|
|
1624
|
-
|
|
1658
|
+
items = self._safe_json_list(response, key="workflow_runs")
|
|
1659
|
+
if items is None:
|
|
1660
|
+
return
|
|
1661
|
+
for record in items:
|
|
1625
1662
|
yield record
|
|
1626
1663
|
|
|
1627
1664
|
def read_records(
|
|
@@ -1699,7 +1736,10 @@ class WorkflowJobs(SemiIncrementalMixin, GithubStream):
|
|
|
1699
1736
|
stream_slice: Mapping[str, Any] = None,
|
|
1700
1737
|
next_page_token: Mapping[str, Any] = None,
|
|
1701
1738
|
) -> Iterable[Mapping]:
|
|
1702
|
-
|
|
1739
|
+
items = self._safe_json_list(response, key="jobs")
|
|
1740
|
+
if items is None:
|
|
1741
|
+
return
|
|
1742
|
+
for record in items:
|
|
1703
1743
|
if record.get(self.cursor_field):
|
|
1704
1744
|
yield self.transform(record=record, stream_slice=stream_slice)
|
|
1705
1745
|
|
|
@@ -1885,8 +1925,11 @@ class IssueTimelineEvents(GithubStream):
|
|
|
1885
1925
|
stream_slice: Mapping[str, Any] = None,
|
|
1886
1926
|
next_page_token: Mapping[str, Any] = None,
|
|
1887
1927
|
) -> Iterable[Mapping]:
|
|
1888
|
-
events_list = response.json()
|
|
1889
1928
|
record = {"repository": stream_slice["repository"], "issue_number": stream_slice["number"]}
|
|
1929
|
+
events_list = self._safe_json_list(response)
|
|
1930
|
+
if events_list is None:
|
|
1931
|
+
yield record
|
|
1932
|
+
return
|
|
1890
1933
|
for event in events_list:
|
|
1891
1934
|
record[event["event"]] = event
|
|
1892
1935
|
yield record
|
|
File without changes
|
|
File without changes
|
{airbyte_source_github-2.1.26 → airbyte_source_github-2.1.27}/source_github/backoff_strategies.py
RENAMED
|
File without changes
|
{airbyte_source_github-2.1.26 → airbyte_source_github-2.1.27}/source_github/config_migrations.py
RENAMED
|
File without changes
|
|
File without changes
|
{airbyte_source_github-2.1.26 → airbyte_source_github-2.1.27}/source_github/github_schema.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{airbyte_source_github-2.1.26 → airbyte_source_github-2.1.27}/source_github/schemas/assignees.json
RENAMED
|
File without changes
|
{airbyte_source_github-2.1.26 → airbyte_source_github-2.1.27}/source_github/schemas/branches.json
RENAMED
|
File without changes
|
|
File without changes
|
{airbyte_source_github-2.1.26 → airbyte_source_github-2.1.27}/source_github/schemas/comments.json
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{airbyte_source_github-2.1.26 → airbyte_source_github-2.1.27}/source_github/schemas/commits.json
RENAMED
|
File without changes
|
|
File without changes
|
{airbyte_source_github-2.1.26 → airbyte_source_github-2.1.27}/source_github/schemas/deployments.json
RENAMED
|
File without changes
|
{airbyte_source_github-2.1.26 → airbyte_source_github-2.1.27}/source_github/schemas/events.json
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{airbyte_source_github-2.1.26 → airbyte_source_github-2.1.27}/source_github/schemas/issues.json
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{airbyte_source_github-2.1.26 → airbyte_source_github-2.1.27}/source_github/schemas/projects.json
RENAMED
|
File without changes
|
{airbyte_source_github-2.1.26 → airbyte_source_github-2.1.27}/source_github/schemas/projects_v2.json
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{airbyte_source_github-2.1.26 → airbyte_source_github-2.1.27}/source_github/schemas/releases.json
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{airbyte_source_github-2.1.26 → airbyte_source_github-2.1.27}/source_github/schemas/reviews.json
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{airbyte_source_github-2.1.26 → airbyte_source_github-2.1.27}/source_github/schemas/shared/user.json
RENAMED
|
File without changes
|
|
File without changes
|
{airbyte_source_github-2.1.26 → airbyte_source_github-2.1.27}/source_github/schemas/stargazers.json
RENAMED
|
File without changes
|
{airbyte_source_github-2.1.26 → airbyte_source_github-2.1.27}/source_github/schemas/tags.json
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{airbyte_source_github-2.1.26 → airbyte_source_github-2.1.27}/source_github/schemas/teams.json
RENAMED
|
File without changes
|
{airbyte_source_github-2.1.26 → airbyte_source_github-2.1.27}/source_github/schemas/users.json
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{airbyte_source_github-2.1.26 → airbyte_source_github-2.1.27}/source_github/schemas/workflows.json
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|