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.
Files changed (67) hide show
  1. {airbyte_source_github-2.1.41 → airbyte_source_github-2.2.0}/PKG-INFO +2 -2
  2. {airbyte_source_github-2.1.41 → airbyte_source_github-2.2.0}/pyproject.toml +2 -2
  3. airbyte_source_github-2.2.0/source_github/backoff_strategies.py +61 -0
  4. {airbyte_source_github-2.1.41 → airbyte_source_github-2.2.0}/source_github/errors_handlers.py +30 -23
  5. airbyte_source_github-2.2.0/source_github/manifest.yaml +1331 -0
  6. {airbyte_source_github-2.1.41 → airbyte_source_github-2.2.0}/source_github/source.py +149 -95
  7. {airbyte_source_github-2.1.41 → airbyte_source_github-2.2.0}/source_github/streams.py +16 -42
  8. airbyte_source_github-2.2.0/source_github/utils.py +24 -0
  9. airbyte_source_github-2.1.41/source_github/backoff_strategies.py +0 -52
  10. airbyte_source_github-2.1.41/source_github/manifest.yaml +0 -6
  11. airbyte_source_github-2.1.41/source_github/schemas/repositories.json +0 -427
  12. airbyte_source_github-2.1.41/source_github/spec.json +0 -204
  13. airbyte_source_github-2.1.41/source_github/utils.py +0 -273
  14. {airbyte_source_github-2.1.41 → airbyte_source_github-2.2.0}/.airbyte-pypi-readme.md +0 -0
  15. {airbyte_source_github-2.1.41 → airbyte_source_github-2.2.0}/source_github/__init__.py +0 -0
  16. {airbyte_source_github-2.1.41 → airbyte_source_github-2.2.0}/source_github/config_migrations.py +0 -0
  17. {airbyte_source_github-2.1.41 → airbyte_source_github-2.2.0}/source_github/constants.py +0 -0
  18. {airbyte_source_github-2.1.41 → airbyte_source_github-2.2.0}/source_github/github_schema.py +0 -0
  19. {airbyte_source_github-2.1.41 → airbyte_source_github-2.2.0}/source_github/graphql.py +0 -0
  20. {airbyte_source_github-2.1.41 → airbyte_source_github-2.2.0}/source_github/run.py +0 -0
  21. {airbyte_source_github-2.1.41 → airbyte_source_github-2.2.0}/source_github/schemas/assignees.json +0 -0
  22. {airbyte_source_github-2.1.41 → airbyte_source_github-2.2.0}/source_github/schemas/branches.json +0 -0
  23. {airbyte_source_github-2.1.41 → airbyte_source_github-2.2.0}/source_github/schemas/collaborators.json +0 -0
  24. {airbyte_source_github-2.1.41 → airbyte_source_github-2.2.0}/source_github/schemas/comments.json +0 -0
  25. {airbyte_source_github-2.1.41 → airbyte_source_github-2.2.0}/source_github/schemas/commit_comment_reactions.json +0 -0
  26. {airbyte_source_github-2.1.41 → airbyte_source_github-2.2.0}/source_github/schemas/commit_comments.json +0 -0
  27. {airbyte_source_github-2.1.41 → airbyte_source_github-2.2.0}/source_github/schemas/commits.json +0 -0
  28. {airbyte_source_github-2.1.41 → airbyte_source_github-2.2.0}/source_github/schemas/contributor_activity.json +0 -0
  29. {airbyte_source_github-2.1.41 → airbyte_source_github-2.2.0}/source_github/schemas/deployments.json +0 -0
  30. {airbyte_source_github-2.1.41 → airbyte_source_github-2.2.0}/source_github/schemas/events.json +0 -0
  31. {airbyte_source_github-2.1.41 → airbyte_source_github-2.2.0}/source_github/schemas/issue_comment_reactions.json +0 -0
  32. {airbyte_source_github-2.1.41 → airbyte_source_github-2.2.0}/source_github/schemas/issue_events.json +0 -0
  33. {airbyte_source_github-2.1.41 → airbyte_source_github-2.2.0}/source_github/schemas/issue_labels.json +0 -0
  34. {airbyte_source_github-2.1.41 → airbyte_source_github-2.2.0}/source_github/schemas/issue_milestones.json +0 -0
  35. {airbyte_source_github-2.1.41 → airbyte_source_github-2.2.0}/source_github/schemas/issue_reactions.json +0 -0
  36. {airbyte_source_github-2.1.41 → airbyte_source_github-2.2.0}/source_github/schemas/issue_timeline_events.json +0 -0
  37. {airbyte_source_github-2.1.41 → airbyte_source_github-2.2.0}/source_github/schemas/issues.json +0 -0
  38. {airbyte_source_github-2.1.41 → airbyte_source_github-2.2.0}/source_github/schemas/organizations.json +0 -0
  39. {airbyte_source_github-2.1.41 → airbyte_source_github-2.2.0}/source_github/schemas/project_cards.json +0 -0
  40. {airbyte_source_github-2.1.41 → airbyte_source_github-2.2.0}/source_github/schemas/project_columns.json +0 -0
  41. {airbyte_source_github-2.1.41 → airbyte_source_github-2.2.0}/source_github/schemas/projects.json +0 -0
  42. {airbyte_source_github-2.1.41 → airbyte_source_github-2.2.0}/source_github/schemas/projects_v2.json +0 -0
  43. {airbyte_source_github-2.1.41 → airbyte_source_github-2.2.0}/source_github/schemas/pull_request_comment_reactions.json +0 -0
  44. {airbyte_source_github-2.1.41 → airbyte_source_github-2.2.0}/source_github/schemas/pull_request_commits.json +0 -0
  45. {airbyte_source_github-2.1.41 → airbyte_source_github-2.2.0}/source_github/schemas/pull_request_stats.json +0 -0
  46. {airbyte_source_github-2.1.41 → airbyte_source_github-2.2.0}/source_github/schemas/pull_requests.json +0 -0
  47. {airbyte_source_github-2.1.41 → airbyte_source_github-2.2.0}/source_github/schemas/releases.json +0 -0
  48. {airbyte_source_github-2.1.41 → airbyte_source_github-2.2.0}/source_github/schemas/review_comments.json +0 -0
  49. {airbyte_source_github-2.1.41 → airbyte_source_github-2.2.0}/source_github/schemas/reviews.json +0 -0
  50. {airbyte_source_github-2.1.41 → airbyte_source_github-2.2.0}/source_github/schemas/shared/events/comment.json +0 -0
  51. {airbyte_source_github-2.1.41 → airbyte_source_github-2.2.0}/source_github/schemas/shared/events/commented.json +0 -0
  52. {airbyte_source_github-2.1.41 → airbyte_source_github-2.2.0}/source_github/schemas/shared/events/committed.json +0 -0
  53. {airbyte_source_github-2.1.41 → airbyte_source_github-2.2.0}/source_github/schemas/shared/events/cross_referenced.json +0 -0
  54. {airbyte_source_github-2.1.41 → airbyte_source_github-2.2.0}/source_github/schemas/shared/events/reviewed.json +0 -0
  55. {airbyte_source_github-2.1.41 → airbyte_source_github-2.2.0}/source_github/schemas/shared/reaction.json +0 -0
  56. {airbyte_source_github-2.1.41 → airbyte_source_github-2.2.0}/source_github/schemas/shared/reactions.json +0 -0
  57. {airbyte_source_github-2.1.41 → airbyte_source_github-2.2.0}/source_github/schemas/shared/user.json +0 -0
  58. {airbyte_source_github-2.1.41 → airbyte_source_github-2.2.0}/source_github/schemas/shared/user_graphql.json +0 -0
  59. {airbyte_source_github-2.1.41 → airbyte_source_github-2.2.0}/source_github/schemas/stargazers.json +0 -0
  60. {airbyte_source_github-2.1.41 → airbyte_source_github-2.2.0}/source_github/schemas/tags.json +0 -0
  61. {airbyte_source_github-2.1.41 → airbyte_source_github-2.2.0}/source_github/schemas/team_members.json +0 -0
  62. {airbyte_source_github-2.1.41 → airbyte_source_github-2.2.0}/source_github/schemas/team_memberships.json +0 -0
  63. {airbyte_source_github-2.1.41 → airbyte_source_github-2.2.0}/source_github/schemas/teams.json +0 -0
  64. {airbyte_source_github-2.1.41 → airbyte_source_github-2.2.0}/source_github/schemas/users.json +0 -0
  65. {airbyte_source_github-2.1.41 → airbyte_source_github-2.2.0}/source_github/schemas/workflow_jobs.json +0 -0
  66. {airbyte_source_github-2.1.41 → airbyte_source_github-2.2.0}/source_github/schemas/workflow_runs.json +0 -0
  67. {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.1.41
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.12.0,<8.0.0)
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.1.41"
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.12.0"
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
@@ -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
- class GithubStreamABCErrorHandler(HttpStatusErrorHandler):
88
- def __init__(self, stream: HttpStream, **kwargs): # type: ignore # noqa
89
- self.stream = stream
90
- super().__init__(**kwargs)
91
-
92
- def _safe_json_check_graphql_rate_limited(self, response: requests.Response) -> bool:
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
- self._logger.warning(
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
- return False
102
- return self.stream.check_graphql_rate_limited(body or {})
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
- # The GitHub GraphQL API has limitations
108
- # https://docs.github.com/en/graphql/overview/resource-limitations
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 = [