airbyte-source-github 2.4.0__tar.gz → 2.5.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.4.0 → airbyte_source_github-2.5.0}/PKG-INFO +1 -1
- {airbyte_source_github-2.4.0 → airbyte_source_github-2.5.0}/pyproject.toml +1 -1
- airbyte_source_github-2.5.0/source_github/components.py +83 -0
- {airbyte_source_github-2.4.0 → airbyte_source_github-2.5.0}/source_github/manifest.yaml +1179 -0
- {airbyte_source_github-2.4.0 → airbyte_source_github-2.5.0}/source_github/source.py +0 -31
- {airbyte_source_github-2.4.0 → airbyte_source_github-2.5.0}/source_github/streams.py +15 -673
- airbyte_source_github-2.4.0/source_github/schemas/commit_comment_reactions.json +0 -4
- airbyte_source_github-2.4.0/source_github/schemas/issue_comment_reactions.json +0 -4
- airbyte_source_github-2.4.0/source_github/schemas/project_cards.json +0 -64
- airbyte_source_github-2.4.0/source_github/schemas/project_columns.json +0 -48
- airbyte_source_github-2.4.0/source_github/schemas/pull_request_commits.json +0 -155
- airbyte_source_github-2.4.0/source_github/schemas/team_members.json +0 -86
- airbyte_source_github-2.4.0/source_github/schemas/team_memberships.json +0 -30
- {airbyte_source_github-2.4.0 → airbyte_source_github-2.5.0}/.airbyte-pypi-readme.md +0 -0
- {airbyte_source_github-2.4.0 → airbyte_source_github-2.5.0}/source_github/__init__.py +0 -0
- {airbyte_source_github-2.4.0 → airbyte_source_github-2.5.0}/source_github/backoff_strategies.py +0 -0
- {airbyte_source_github-2.4.0 → airbyte_source_github-2.5.0}/source_github/config_migrations.py +0 -0
- {airbyte_source_github-2.4.0 → airbyte_source_github-2.5.0}/source_github/constants.py +0 -0
- {airbyte_source_github-2.4.0 → airbyte_source_github-2.5.0}/source_github/errors_handlers.py +0 -0
- {airbyte_source_github-2.4.0 → airbyte_source_github-2.5.0}/source_github/github_schema.py +0 -0
- {airbyte_source_github-2.4.0 → airbyte_source_github-2.5.0}/source_github/graphql.py +0 -0
- {airbyte_source_github-2.4.0 → airbyte_source_github-2.5.0}/source_github/run.py +0 -0
- {airbyte_source_github-2.4.0 → airbyte_source_github-2.5.0}/source_github/schemas/commit_comments.json +0 -0
- {airbyte_source_github-2.4.0 → airbyte_source_github-2.5.0}/source_github/schemas/commits.json +0 -0
- {airbyte_source_github-2.4.0 → airbyte_source_github-2.5.0}/source_github/schemas/contributor_activity.json +0 -0
- {airbyte_source_github-2.4.0 → airbyte_source_github-2.5.0}/source_github/schemas/issue_reactions.json +0 -0
- {airbyte_source_github-2.4.0 → airbyte_source_github-2.5.0}/source_github/schemas/issue_timeline_events.json +0 -0
- {airbyte_source_github-2.4.0 → airbyte_source_github-2.5.0}/source_github/schemas/projects.json +0 -0
- {airbyte_source_github-2.4.0 → airbyte_source_github-2.5.0}/source_github/schemas/projects_v2.json +0 -0
- {airbyte_source_github-2.4.0 → airbyte_source_github-2.5.0}/source_github/schemas/pull_request_comment_reactions.json +0 -0
- {airbyte_source_github-2.4.0 → airbyte_source_github-2.5.0}/source_github/schemas/pull_request_stats.json +0 -0
- {airbyte_source_github-2.4.0 → airbyte_source_github-2.5.0}/source_github/schemas/pull_requests.json +0 -0
- {airbyte_source_github-2.4.0 → airbyte_source_github-2.5.0}/source_github/schemas/releases.json +0 -0
- {airbyte_source_github-2.4.0 → airbyte_source_github-2.5.0}/source_github/schemas/reviews.json +0 -0
- {airbyte_source_github-2.4.0 → airbyte_source_github-2.5.0}/source_github/schemas/shared/events/comment.json +0 -0
- {airbyte_source_github-2.4.0 → airbyte_source_github-2.5.0}/source_github/schemas/shared/events/commented.json +0 -0
- {airbyte_source_github-2.4.0 → airbyte_source_github-2.5.0}/source_github/schemas/shared/events/committed.json +0 -0
- {airbyte_source_github-2.4.0 → airbyte_source_github-2.5.0}/source_github/schemas/shared/events/cross_referenced.json +0 -0
- {airbyte_source_github-2.4.0 → airbyte_source_github-2.5.0}/source_github/schemas/shared/events/reviewed.json +0 -0
- {airbyte_source_github-2.4.0 → airbyte_source_github-2.5.0}/source_github/schemas/shared/reaction.json +0 -0
- {airbyte_source_github-2.4.0 → airbyte_source_github-2.5.0}/source_github/schemas/shared/reactions.json +0 -0
- {airbyte_source_github-2.4.0 → airbyte_source_github-2.5.0}/source_github/schemas/shared/user.json +0 -0
- {airbyte_source_github-2.4.0 → airbyte_source_github-2.5.0}/source_github/schemas/shared/user_graphql.json +0 -0
- {airbyte_source_github-2.4.0 → airbyte_source_github-2.5.0}/source_github/schemas/workflow_jobs.json +0 -0
- {airbyte_source_github-2.4.0 → airbyte_source_github-2.5.0}/source_github/schemas/workflow_runs.json +0 -0
- {airbyte_source_github-2.4.0 → airbyte_source_github-2.5.0}/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.
|
|
6
|
+
version = "2.5.0"
|
|
7
7
|
name = "airbyte-source-github"
|
|
8
8
|
description = "Source implementation for GitHub."
|
|
9
9
|
authors = [ "Airbyte <contact@airbyte.io>",]
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
#
|
|
2
|
+
# Copyright (c) 2023 Airbyte, Inc., all rights reserved.
|
|
3
|
+
#
|
|
4
|
+
|
|
5
|
+
from dataclasses import dataclass
|
|
6
|
+
from typing import Any, Iterable, List, Mapping, MutableMapping
|
|
7
|
+
|
|
8
|
+
import requests
|
|
9
|
+
|
|
10
|
+
from airbyte_cdk.sources.declarative.extractors.record_extractor import RecordExtractor
|
|
11
|
+
from airbyte_cdk.sources.declarative.migrations.state_migration import StateMigration
|
|
12
|
+
from airbyte_cdk.sources.types import Config
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@dataclass
|
|
16
|
+
class IssueTimelineEventsExtractor(RecordExtractor):
|
|
17
|
+
"""Collapse an issue's timeline page into one record keyed by event type.
|
|
18
|
+
|
|
19
|
+
The legacy `IssueTimelineEvents.parse_response` yielded `{<event type>: <event>, ...}` per page,
|
|
20
|
+
the last event of each type winning, and a bare record when the body was not a list.
|
|
21
|
+
`repository` and `issue_number` are added by the stream's transformations.
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
config: Config
|
|
25
|
+
parameters: Mapping[str, Any]
|
|
26
|
+
|
|
27
|
+
def extract_records(self, response: requests.Response) -> Iterable[Mapping[str, Any]]:
|
|
28
|
+
try:
|
|
29
|
+
body = response.json()
|
|
30
|
+
except ValueError:
|
|
31
|
+
body = None
|
|
32
|
+
record: MutableMapping[str, Any] = {}
|
|
33
|
+
if isinstance(body, list):
|
|
34
|
+
for event in body:
|
|
35
|
+
if isinstance(event, Mapping) and "event" in event:
|
|
36
|
+
record[event["event"]] = event
|
|
37
|
+
yield record
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
@dataclass
|
|
41
|
+
class NestedLegacyToPerPartitionStateMigration(StateMigration):
|
|
42
|
+
"""Migrate the nested state the Python parent-child streams wrote to per-partition state.
|
|
43
|
+
|
|
44
|
+
Legacy shape: `{repository: {<parent id>: {cursor_field: value}}}`, one more level of ids per
|
|
45
|
+
parent (`project_cards` nests `project_id` then `column_id`). `partition_fields` lists those id
|
|
46
|
+
levels, outermost first. Ids were stored as strings; the declarative partitions carry them as
|
|
47
|
+
the integers GitHub returns.
|
|
48
|
+
"""
|
|
49
|
+
|
|
50
|
+
config: Config
|
|
51
|
+
parameters: Mapping[str, Any]
|
|
52
|
+
cursor_field: str
|
|
53
|
+
partition_fields: List[str]
|
|
54
|
+
|
|
55
|
+
def should_migrate(self, stream_state: Mapping[str, Any]) -> bool:
|
|
56
|
+
if not stream_state or "states" in stream_state or "state" in stream_state:
|
|
57
|
+
return False
|
|
58
|
+
return all(self._is_legacy_repository_state(value) for value in stream_state.values())
|
|
59
|
+
|
|
60
|
+
def _is_legacy_repository_state(self, node: Any, depth: int = 0) -> bool:
|
|
61
|
+
if not isinstance(node, Mapping) or not node:
|
|
62
|
+
return False
|
|
63
|
+
if depth == len(self.partition_fields):
|
|
64
|
+
return set(node) == {self.cursor_field}
|
|
65
|
+
return all(self._is_legacy_repository_state(child, depth + 1) for child in node.values())
|
|
66
|
+
|
|
67
|
+
def migrate(self, stream_state: Mapping[str, Any]) -> Mapping[str, Any]:
|
|
68
|
+
states = []
|
|
69
|
+
for repository, node in stream_state.items():
|
|
70
|
+
self._collect(node, {"repository": repository}, 0, states)
|
|
71
|
+
return {"states": states}
|
|
72
|
+
|
|
73
|
+
def _collect(self, node: Mapping[str, Any], partition: Mapping[str, Any], depth: int, states: List[Mapping[str, Any]]) -> None:
|
|
74
|
+
if depth == len(self.partition_fields):
|
|
75
|
+
states.append({"partition": partition, "cursor": {self.cursor_field: node[self.cursor_field]}})
|
|
76
|
+
return
|
|
77
|
+
for key, child in node.items():
|
|
78
|
+
child_partition = {self.partition_fields[depth]: self._to_id(key), "parent_slice": partition}
|
|
79
|
+
self._collect(child, child_partition, depth + 1, states)
|
|
80
|
+
|
|
81
|
+
@staticmethod
|
|
82
|
+
def _to_id(key: str) -> Any:
|
|
83
|
+
return int(key) if isinstance(key, str) and key.isdigit() else key
|