airbyte-source-github 1.5.7__py3-none-any.whl
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-1.5.7.dist-info/METADATA +144 -0
- airbyte_source_github-1.5.7.dist-info/RECORD +88 -0
- airbyte_source_github-1.5.7.dist-info/WHEEL +5 -0
- airbyte_source_github-1.5.7.dist-info/entry_points.txt +2 -0
- airbyte_source_github-1.5.7.dist-info/top_level.txt +3 -0
- integration_tests/__init__.py +0 -0
- integration_tests/abnormal_state.json +237 -0
- integration_tests/acceptance.py +16 -0
- integration_tests/configured_catalog.json +435 -0
- integration_tests/configured_catalog_full_refresh_test.json +415 -0
- integration_tests/invalid_config.json +5 -0
- integration_tests/sample_config.json +5 -0
- integration_tests/sample_state.json +137 -0
- source_github/__init__.py +27 -0
- source_github/config_migrations.py +106 -0
- source_github/constants.py +9 -0
- source_github/github_schema.py +41034 -0
- source_github/graphql.py +327 -0
- source_github/run.py +17 -0
- source_github/schemas/assignees.json +63 -0
- source_github/schemas/branches.json +48 -0
- source_github/schemas/collaborators.json +80 -0
- source_github/schemas/comments.json +104 -0
- source_github/schemas/commit_comment_reactions.json +4 -0
- source_github/schemas/commit_comments.json +53 -0
- source_github/schemas/commits.json +126 -0
- source_github/schemas/contributor_activity.json +109 -0
- source_github/schemas/deployments.json +77 -0
- source_github/schemas/events.json +63 -0
- source_github/schemas/issue_comment_reactions.json +4 -0
- source_github/schemas/issue_events.json +335 -0
- source_github/schemas/issue_labels.json +30 -0
- source_github/schemas/issue_milestones.json +61 -0
- source_github/schemas/issue_reactions.json +28 -0
- source_github/schemas/issue_timeline_events.json +1056 -0
- source_github/schemas/issues.json +281 -0
- source_github/schemas/organizations.json +197 -0
- source_github/schemas/project_cards.json +50 -0
- source_github/schemas/project_columns.json +38 -0
- source_github/schemas/projects.json +50 -0
- source_github/schemas/projects_v2.json +80 -0
- source_github/schemas/pull_request_comment_reactions.json +28 -0
- source_github/schemas/pull_request_commits.json +122 -0
- source_github/schemas/pull_request_stats.json +84 -0
- source_github/schemas/pull_requests.json +363 -0
- source_github/schemas/releases.json +126 -0
- source_github/schemas/repositories.json +313 -0
- source_github/schemas/review_comments.json +118 -0
- source_github/schemas/reviews.json +69 -0
- source_github/schemas/shared/events/comment.json +188 -0
- source_github/schemas/shared/events/commented.json +118 -0
- source_github/schemas/shared/events/committed.json +56 -0
- source_github/schemas/shared/events/cross_referenced.json +784 -0
- source_github/schemas/shared/events/reviewed.json +139 -0
- source_github/schemas/shared/reaction.json +27 -0
- source_github/schemas/shared/reactions.json +35 -0
- source_github/schemas/shared/user.json +59 -0
- source_github/schemas/shared/user_graphql.json +26 -0
- source_github/schemas/stargazers.json +19 -0
- source_github/schemas/tags.json +32 -0
- source_github/schemas/team_members.json +66 -0
- source_github/schemas/team_memberships.json +24 -0
- source_github/schemas/teams.json +50 -0
- source_github/schemas/users.json +63 -0
- source_github/schemas/workflow_jobs.json +109 -0
- source_github/schemas/workflow_runs.json +449 -0
- source_github/schemas/workflows.json +41 -0
- source_github/source.py +339 -0
- source_github/spec.json +179 -0
- source_github/streams.py +1678 -0
- source_github/utils.py +152 -0
- unit_tests/__init__.py +3 -0
- unit_tests/conftest.py +29 -0
- unit_tests/projects_v2_pull_requests_query.json +3 -0
- unit_tests/pull_request_stats_query.json +3 -0
- unit_tests/responses/contributor_activity_response.json +33 -0
- unit_tests/responses/graphql_reviews_responses.json +405 -0
- unit_tests/responses/issue_timeline_events.json +166 -0
- unit_tests/responses/issue_timeline_events_response.json +170 -0
- unit_tests/responses/projects_v2_response.json +45 -0
- unit_tests/responses/pull_request_comment_reactions.json +744 -0
- unit_tests/responses/pull_request_stats_response.json +317 -0
- unit_tests/test_migrations/test_config.json +8 -0
- unit_tests/test_migrations/test_new_config.json +8 -0
- unit_tests/test_multiple_token_authenticator.py +160 -0
- unit_tests/test_source.py +326 -0
- unit_tests/test_stream.py +1471 -0
- unit_tests/utils.py +78 -0
source_github/source.py
ADDED
@@ -0,0 +1,339 @@
|
|
1
|
+
#
|
2
|
+
# Copyright (c) 2023 Airbyte, Inc., all rights reserved.
|
3
|
+
#
|
4
|
+
|
5
|
+
from os import getenv
|
6
|
+
from typing import Any, Dict, List, Mapping, MutableMapping, Tuple
|
7
|
+
from urllib.parse import urlparse
|
8
|
+
|
9
|
+
from airbyte_cdk import AirbyteLogger
|
10
|
+
from airbyte_cdk.models import FailureType, SyncMode
|
11
|
+
from airbyte_cdk.sources import AbstractSource
|
12
|
+
from airbyte_cdk.sources.streams import Stream
|
13
|
+
from airbyte_cdk.sources.streams.http.auth import MultipleTokenAuthenticator
|
14
|
+
from airbyte_cdk.utils.traced_exception import AirbyteTracedException
|
15
|
+
from source_github.utils import MultipleTokenAuthenticatorWithRateLimiter
|
16
|
+
|
17
|
+
from . import constants
|
18
|
+
from .streams import (
|
19
|
+
Assignees,
|
20
|
+
Branches,
|
21
|
+
Collaborators,
|
22
|
+
Comments,
|
23
|
+
CommitCommentReactions,
|
24
|
+
CommitComments,
|
25
|
+
Commits,
|
26
|
+
ContributorActivity,
|
27
|
+
Deployments,
|
28
|
+
Events,
|
29
|
+
IssueCommentReactions,
|
30
|
+
IssueEvents,
|
31
|
+
IssueLabels,
|
32
|
+
IssueMilestones,
|
33
|
+
IssueReactions,
|
34
|
+
Issues,
|
35
|
+
IssueTimelineEvents,
|
36
|
+
Organizations,
|
37
|
+
ProjectCards,
|
38
|
+
ProjectColumns,
|
39
|
+
Projects,
|
40
|
+
ProjectsV2,
|
41
|
+
PullRequestCommentReactions,
|
42
|
+
PullRequestCommits,
|
43
|
+
PullRequests,
|
44
|
+
PullRequestStats,
|
45
|
+
Releases,
|
46
|
+
Repositories,
|
47
|
+
RepositoryStats,
|
48
|
+
ReviewComments,
|
49
|
+
Reviews,
|
50
|
+
Stargazers,
|
51
|
+
Tags,
|
52
|
+
TeamMembers,
|
53
|
+
TeamMemberships,
|
54
|
+
Teams,
|
55
|
+
Users,
|
56
|
+
WorkflowJobs,
|
57
|
+
WorkflowRuns,
|
58
|
+
Workflows,
|
59
|
+
)
|
60
|
+
from .utils import read_full_refresh
|
61
|
+
|
62
|
+
|
63
|
+
class SourceGithub(AbstractSource):
|
64
|
+
@staticmethod
|
65
|
+
def _get_org_repositories(config: Mapping[str, Any], authenticator: MultipleTokenAuthenticator) -> Tuple[List[str], List[str]]:
|
66
|
+
"""
|
67
|
+
Parse config/repositories and produce two lists: organizations, repositories.
|
68
|
+
Args:
|
69
|
+
config (dict): Dict representing connector's config
|
70
|
+
authenticator(MultipleTokenAuthenticator): authenticator object
|
71
|
+
"""
|
72
|
+
config_repositories = set(config.get("repositories"))
|
73
|
+
|
74
|
+
repositories = set()
|
75
|
+
organizations = set()
|
76
|
+
unchecked_repos = set()
|
77
|
+
unchecked_orgs = set()
|
78
|
+
|
79
|
+
for org_repos in config_repositories:
|
80
|
+
org, _, repos = org_repos.partition("/")
|
81
|
+
if repos == "*":
|
82
|
+
unchecked_orgs.add(org)
|
83
|
+
else:
|
84
|
+
unchecked_repos.add(org_repos)
|
85
|
+
|
86
|
+
if unchecked_orgs:
|
87
|
+
stream = Repositories(authenticator=authenticator, organizations=unchecked_orgs, api_url=config.get("api_url"))
|
88
|
+
for record in read_full_refresh(stream):
|
89
|
+
repositories.add(record["full_name"])
|
90
|
+
organizations.add(record["organization"])
|
91
|
+
|
92
|
+
unchecked_repos = unchecked_repos - repositories
|
93
|
+
if unchecked_repos:
|
94
|
+
stream = RepositoryStats(
|
95
|
+
authenticator=authenticator,
|
96
|
+
repositories=unchecked_repos,
|
97
|
+
api_url=config.get("api_url"),
|
98
|
+
# This parameter is deprecated and in future will be used sane default, page_size: 10
|
99
|
+
page_size_for_large_streams=config.get("page_size_for_large_streams", constants.DEFAULT_PAGE_SIZE_FOR_LARGE_STREAM),
|
100
|
+
)
|
101
|
+
for record in read_full_refresh(stream):
|
102
|
+
repositories.add(record["full_name"])
|
103
|
+
organization = record.get("organization", {}).get("login")
|
104
|
+
if organization:
|
105
|
+
organizations.add(organization)
|
106
|
+
|
107
|
+
return list(organizations), list(repositories)
|
108
|
+
|
109
|
+
@staticmethod
|
110
|
+
def get_access_token(config: Mapping[str, Any]):
|
111
|
+
# Before we supported oauth, personal_access_token was called `access_token` and it lived at the
|
112
|
+
# config root. So we first check to make sure any backwards compatbility is handled.
|
113
|
+
if "access_token" in config:
|
114
|
+
return constants.PERSONAL_ACCESS_TOKEN_TITLE, config["access_token"]
|
115
|
+
|
116
|
+
credentials = config.get("credentials", {})
|
117
|
+
if "access_token" in credentials:
|
118
|
+
return constants.ACCESS_TOKEN_TITLE, credentials["access_token"]
|
119
|
+
if "personal_access_token" in credentials:
|
120
|
+
return constants.PERSONAL_ACCESS_TOKEN_TITLE, credentials["personal_access_token"]
|
121
|
+
raise Exception("Invalid config format")
|
122
|
+
|
123
|
+
def _get_authenticator(self, config: Mapping[str, Any]):
|
124
|
+
_, token = self.get_access_token(config)
|
125
|
+
tokens = [t.strip() for t in token.split(constants.TOKEN_SEPARATOR)]
|
126
|
+
return MultipleTokenAuthenticatorWithRateLimiter(tokens=tokens)
|
127
|
+
|
128
|
+
def _validate_and_transform_config(self, config: MutableMapping[str, Any]) -> MutableMapping[str, Any]:
|
129
|
+
config = self._ensure_default_values(config)
|
130
|
+
config = self._validate_repositories(config)
|
131
|
+
config = self._validate_branches(config)
|
132
|
+
return config
|
133
|
+
|
134
|
+
def _ensure_default_values(self, config: MutableMapping[str, Any]) -> MutableMapping[str, Any]:
|
135
|
+
config.setdefault("api_url", "https://api.github.com")
|
136
|
+
api_url_parsed = urlparse(config["api_url"])
|
137
|
+
|
138
|
+
if not api_url_parsed.scheme.startswith("http"):
|
139
|
+
message = "Please enter a full url for `API URL` field starting with `http`"
|
140
|
+
elif api_url_parsed.scheme == "http" and not self._is_http_allowed():
|
141
|
+
message = "HTTP connection is insecure and is not allowed in this environment. Please use `https` instead."
|
142
|
+
elif not api_url_parsed.netloc:
|
143
|
+
message = "Please provide a correct API URL."
|
144
|
+
else:
|
145
|
+
return config
|
146
|
+
|
147
|
+
raise AirbyteTracedException(message=message, failure_type=FailureType.config_error)
|
148
|
+
|
149
|
+
def _validate_repositories(self, config: MutableMapping[str, Any]) -> MutableMapping[str, Any]:
|
150
|
+
if config.get("repositories"):
|
151
|
+
pass
|
152
|
+
elif config.get("repository"):
|
153
|
+
config["repositories"] = set(filter(None, config["repository"].split(" ")))
|
154
|
+
|
155
|
+
return config
|
156
|
+
|
157
|
+
def _validate_branches(self, config: MutableMapping[str, Any]) -> MutableMapping[str, Any]:
|
158
|
+
if config.get("branches"):
|
159
|
+
pass
|
160
|
+
elif config.get("branch"):
|
161
|
+
config["branches"] = set(filter(None, config["branch"].split(" ")))
|
162
|
+
|
163
|
+
return config
|
164
|
+
|
165
|
+
@staticmethod
|
166
|
+
def _is_http_allowed() -> bool:
|
167
|
+
return getenv("DEPLOYMENT_MODE", "").upper() != "CLOUD"
|
168
|
+
|
169
|
+
@staticmethod
|
170
|
+
def _get_branches_data(
|
171
|
+
selected_branches: List, full_refresh_args: Dict[str, Any] = None
|
172
|
+
) -> Tuple[Dict[str, str], Dict[str, List[str]]]:
|
173
|
+
selected_branches = set(selected_branches)
|
174
|
+
|
175
|
+
# Get the default branch for each repository
|
176
|
+
default_branches = {}
|
177
|
+
repository_stats_stream = RepositoryStats(**full_refresh_args)
|
178
|
+
for stream_slice in repository_stats_stream.stream_slices(sync_mode=SyncMode.full_refresh):
|
179
|
+
default_branches.update(
|
180
|
+
{
|
181
|
+
repo_stats["full_name"]: repo_stats["default_branch"]
|
182
|
+
for repo_stats in repository_stats_stream.read_records(sync_mode=SyncMode.full_refresh, stream_slice=stream_slice)
|
183
|
+
}
|
184
|
+
)
|
185
|
+
|
186
|
+
all_branches = []
|
187
|
+
branches_stream = Branches(**full_refresh_args)
|
188
|
+
for stream_slice in branches_stream.stream_slices(sync_mode=SyncMode.full_refresh):
|
189
|
+
for branch in branches_stream.read_records(sync_mode=SyncMode.full_refresh, stream_slice=stream_slice):
|
190
|
+
all_branches.append(f"{branch['repository']}/{branch['name']}")
|
191
|
+
|
192
|
+
# Create mapping of repository to list of branches to pull commits for
|
193
|
+
# If no branches are specified for a repo, use its default branch
|
194
|
+
branches_to_pull: Dict[str, List[str]] = {}
|
195
|
+
for repo in full_refresh_args["repositories"]:
|
196
|
+
repo_branches = []
|
197
|
+
for branch in selected_branches:
|
198
|
+
branch_parts = branch.split("/", 2)
|
199
|
+
if "/".join(branch_parts[:2]) == repo and branch in all_branches:
|
200
|
+
repo_branches.append(branch_parts[-1])
|
201
|
+
if not repo_branches:
|
202
|
+
repo_branches = [default_branches[repo]]
|
203
|
+
|
204
|
+
branches_to_pull[repo] = repo_branches
|
205
|
+
|
206
|
+
return default_branches, branches_to_pull
|
207
|
+
|
208
|
+
def user_friendly_error_message(self, message: str) -> str:
|
209
|
+
user_message = ""
|
210
|
+
if "404 Client Error: Not Found for url: https://api.github.com/repos/" in message:
|
211
|
+
# 404 Client Error: Not Found for url: https://api.github.com/repos/airbytehq/airbyte3?per_page=100
|
212
|
+
full_repo_name = message.split("https://api.github.com/repos/")[1].split("?")[0]
|
213
|
+
user_message = f'Repo name: "{full_repo_name}" is unknown, "repository" config option should use existing full repo name <organization>/<repository>'
|
214
|
+
elif "404 Client Error: Not Found for url: https://api.github.com/orgs/" in message:
|
215
|
+
# 404 Client Error: Not Found for url: https://api.github.com/orgs/airbytehqBLA/repos?per_page=100
|
216
|
+
org_name = message.split("https://api.github.com/orgs/")[1].split("/")[0]
|
217
|
+
user_message = f'Organization name: "{org_name}" is unknown, "repository" config option should be updated. Please validate your repository config.'
|
218
|
+
elif "401 Client Error: Unauthorized for url" in message:
|
219
|
+
# 401 Client Error: Unauthorized for url: https://api.github.com/orgs/datarootsio/repos?per_page=100&sort=updated&direction=desc
|
220
|
+
user_message = (
|
221
|
+
"Github credentials have expired or changed, please review your credentials and re-authenticate or renew your access token."
|
222
|
+
)
|
223
|
+
return user_message
|
224
|
+
|
225
|
+
def check_connection(self, logger: AirbyteLogger, config: Mapping[str, Any]) -> Tuple[bool, Any]:
|
226
|
+
config = self._validate_and_transform_config(config)
|
227
|
+
try:
|
228
|
+
authenticator = self._get_authenticator(config)
|
229
|
+
_, repositories = self._get_org_repositories(config=config, authenticator=authenticator)
|
230
|
+
if not repositories:
|
231
|
+
return (
|
232
|
+
False,
|
233
|
+
"Some of the provided repositories couldn't be found. Please verify if every entered repository has a valid name and it matches the following format: airbytehq/airbyte airbytehq/another-repo airbytehq/* airbytehq/airbyte.",
|
234
|
+
)
|
235
|
+
return True, None
|
236
|
+
|
237
|
+
except Exception as e:
|
238
|
+
message = repr(e)
|
239
|
+
user_message = self.user_friendly_error_message(message)
|
240
|
+
return False, user_message or message
|
241
|
+
|
242
|
+
def streams(self, config: Mapping[str, Any]) -> List[Stream]:
|
243
|
+
authenticator = self._get_authenticator(config)
|
244
|
+
config = self._validate_and_transform_config(config)
|
245
|
+
try:
|
246
|
+
organizations, repositories = self._get_org_repositories(config=config, authenticator=authenticator)
|
247
|
+
except Exception as e:
|
248
|
+
message = repr(e)
|
249
|
+
user_message = self.user_friendly_error_message(message)
|
250
|
+
if user_message:
|
251
|
+
raise AirbyteTracedException(
|
252
|
+
internal_message=message, message=user_message, failure_type=FailureType.config_error, exception=e
|
253
|
+
)
|
254
|
+
else:
|
255
|
+
raise e
|
256
|
+
|
257
|
+
if not any((organizations, repositories)):
|
258
|
+
user_message = (
|
259
|
+
"No streams available. Looks like your config for repositories or organizations is not valid."
|
260
|
+
" Please, check your permissions, names of repositories and organizations."
|
261
|
+
" Needed scopes: repo, read:org, read:repo_hook, read:user, read:discussion, workflow."
|
262
|
+
)
|
263
|
+
raise AirbyteTracedException(
|
264
|
+
internal_message="No streams available. Please check permissions",
|
265
|
+
message=user_message,
|
266
|
+
failure_type=FailureType.config_error,
|
267
|
+
)
|
268
|
+
|
269
|
+
# This parameter is deprecated and in future will be used sane default, page_size: 10
|
270
|
+
page_size = config.get("page_size_for_large_streams", constants.DEFAULT_PAGE_SIZE_FOR_LARGE_STREAM)
|
271
|
+
access_token_type, _ = self.get_access_token(config)
|
272
|
+
|
273
|
+
organization_args = {
|
274
|
+
"authenticator": authenticator,
|
275
|
+
"organizations": organizations,
|
276
|
+
"api_url": config.get("api_url"),
|
277
|
+
"access_token_type": access_token_type,
|
278
|
+
}
|
279
|
+
start_date = config.get("start_date")
|
280
|
+
organization_args_with_start_date = {**organization_args, "start_date": start_date}
|
281
|
+
|
282
|
+
repository_args = {
|
283
|
+
"authenticator": authenticator,
|
284
|
+
"api_url": config.get("api_url"),
|
285
|
+
"repositories": repositories,
|
286
|
+
"page_size_for_large_streams": page_size,
|
287
|
+
"access_token_type": access_token_type,
|
288
|
+
}
|
289
|
+
repository_args_with_start_date = {**repository_args, "start_date": start_date}
|
290
|
+
|
291
|
+
default_branches, branches_to_pull = self._get_branches_data(config.get("branch", []), repository_args)
|
292
|
+
pull_requests_stream = PullRequests(**repository_args_with_start_date)
|
293
|
+
projects_stream = Projects(**repository_args_with_start_date)
|
294
|
+
project_columns_stream = ProjectColumns(projects_stream, **repository_args_with_start_date)
|
295
|
+
teams_stream = Teams(**organization_args)
|
296
|
+
team_members_stream = TeamMembers(parent=teams_stream, **repository_args)
|
297
|
+
workflow_runs_stream = WorkflowRuns(**repository_args_with_start_date)
|
298
|
+
|
299
|
+
return [
|
300
|
+
IssueTimelineEvents(**repository_args),
|
301
|
+
Assignees(**repository_args),
|
302
|
+
Branches(**repository_args),
|
303
|
+
Collaborators(**repository_args),
|
304
|
+
Comments(**repository_args_with_start_date),
|
305
|
+
CommitCommentReactions(**repository_args_with_start_date),
|
306
|
+
CommitComments(**repository_args_with_start_date),
|
307
|
+
Commits(**repository_args_with_start_date, branches_to_pull=branches_to_pull, default_branches=default_branches),
|
308
|
+
ContributorActivity(**repository_args),
|
309
|
+
Deployments(**repository_args_with_start_date),
|
310
|
+
Events(**repository_args_with_start_date),
|
311
|
+
IssueCommentReactions(**repository_args_with_start_date),
|
312
|
+
IssueEvents(**repository_args_with_start_date),
|
313
|
+
IssueLabels(**repository_args),
|
314
|
+
IssueMilestones(**repository_args_with_start_date),
|
315
|
+
IssueReactions(**repository_args_with_start_date),
|
316
|
+
Issues(**repository_args_with_start_date),
|
317
|
+
Organizations(**organization_args),
|
318
|
+
ProjectCards(project_columns_stream, **repository_args_with_start_date),
|
319
|
+
project_columns_stream,
|
320
|
+
projects_stream,
|
321
|
+
PullRequestCommentReactions(**repository_args_with_start_date),
|
322
|
+
PullRequestCommits(parent=pull_requests_stream, **repository_args),
|
323
|
+
PullRequestStats(**repository_args_with_start_date),
|
324
|
+
ProjectsV2(**repository_args_with_start_date),
|
325
|
+
pull_requests_stream,
|
326
|
+
Releases(**repository_args_with_start_date),
|
327
|
+
Repositories(**organization_args_with_start_date),
|
328
|
+
ReviewComments(**repository_args_with_start_date),
|
329
|
+
Reviews(**repository_args_with_start_date),
|
330
|
+
Stargazers(**repository_args_with_start_date),
|
331
|
+
Tags(**repository_args),
|
332
|
+
teams_stream,
|
333
|
+
team_members_stream,
|
334
|
+
Users(**organization_args),
|
335
|
+
Workflows(**repository_args_with_start_date),
|
336
|
+
workflow_runs_stream,
|
337
|
+
WorkflowJobs(parent=workflow_runs_stream, **repository_args_with_start_date),
|
338
|
+
TeamMemberships(parent=team_members_stream, **repository_args),
|
339
|
+
]
|
source_github/spec.json
ADDED
@@ -0,0 +1,179 @@
|
|
1
|
+
{
|
2
|
+
"documentationUrl": "https://docs.airbyte.com/integrations/sources/github",
|
3
|
+
"connectionSpecification": {
|
4
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
5
|
+
"title": "GitHub Source Spec",
|
6
|
+
"type": "object",
|
7
|
+
"required": ["credentials", "repositories"],
|
8
|
+
"additionalProperties": true,
|
9
|
+
"properties": {
|
10
|
+
"credentials": {
|
11
|
+
"title": "Authentication",
|
12
|
+
"description": "Choose how to authenticate to GitHub",
|
13
|
+
"type": "object",
|
14
|
+
"order": 0,
|
15
|
+
"group": "auth",
|
16
|
+
"oneOf": [
|
17
|
+
{
|
18
|
+
"type": "object",
|
19
|
+
"title": "OAuth",
|
20
|
+
"required": ["access_token"],
|
21
|
+
"properties": {
|
22
|
+
"option_title": {
|
23
|
+
"type": "string",
|
24
|
+
"const": "OAuth Credentials",
|
25
|
+
"order": 0
|
26
|
+
},
|
27
|
+
"access_token": {
|
28
|
+
"type": "string",
|
29
|
+
"title": "Access Token",
|
30
|
+
"description": "OAuth access token",
|
31
|
+
"airbyte_secret": true
|
32
|
+
},
|
33
|
+
"client_id": {
|
34
|
+
"type": "string",
|
35
|
+
"title": "Client Id",
|
36
|
+
"description": "OAuth Client Id",
|
37
|
+
"airbyte_secret": true
|
38
|
+
},
|
39
|
+
"client_secret": {
|
40
|
+
"type": "string",
|
41
|
+
"title": "Client ssecret",
|
42
|
+
"description": "OAuth Client secret",
|
43
|
+
"airbyte_secret": true
|
44
|
+
}
|
45
|
+
}
|
46
|
+
},
|
47
|
+
{
|
48
|
+
"type": "object",
|
49
|
+
"title": "Personal Access Token",
|
50
|
+
"required": ["personal_access_token"],
|
51
|
+
"properties": {
|
52
|
+
"option_title": {
|
53
|
+
"type": "string",
|
54
|
+
"const": "PAT Credentials",
|
55
|
+
"order": 0
|
56
|
+
},
|
57
|
+
"personal_access_token": {
|
58
|
+
"type": "string",
|
59
|
+
"title": "Personal Access Tokens",
|
60
|
+
"description": "Log into GitHub and then generate a <a href=\"https://github.com/settings/tokens\">personal access token</a>. To load balance your API quota consumption across multiple API tokens, input multiple tokens separated with \",\"",
|
61
|
+
"airbyte_secret": true
|
62
|
+
}
|
63
|
+
}
|
64
|
+
}
|
65
|
+
]
|
66
|
+
},
|
67
|
+
"repository": {
|
68
|
+
"type": "string",
|
69
|
+
"examples": [
|
70
|
+
"airbytehq/airbyte airbytehq/another-repo",
|
71
|
+
"airbytehq/*",
|
72
|
+
"airbytehq/airbyte"
|
73
|
+
],
|
74
|
+
"title": "GitHub Repositories",
|
75
|
+
"description": "(DEPRCATED) Space-delimited list of GitHub organizations/repositories, e.g. `airbytehq/airbyte` for single repository, `airbytehq/*` for get all repositories from organization and `airbytehq/airbyte airbytehq/another-repo` for multiple repositories.",
|
76
|
+
"airbyte_hidden": true,
|
77
|
+
"pattern": "^([\\w.-]+/(\\*|[\\w.-]+(?<!\\.git))\\s+)*[\\w.-]+/(\\*|[\\w.-]+(?<!\\.git))$",
|
78
|
+
"pattern_descriptor": "org/repo org/another-repo org/*"
|
79
|
+
},
|
80
|
+
"repositories": {
|
81
|
+
"type": "array",
|
82
|
+
"items": {
|
83
|
+
"type": "string",
|
84
|
+
"pattern": "^([\\w.-]+/(\\*|[\\w.-]+(?<!\\.git))\\s+)*[\\w.-]+/(\\*|[\\w.-]+(?<!\\.git))$"
|
85
|
+
},
|
86
|
+
"minItems": 1,
|
87
|
+
"examples": [
|
88
|
+
"airbytehq/airbyte airbytehq/another-repo",
|
89
|
+
"airbytehq/*",
|
90
|
+
"airbytehq/airbyte"
|
91
|
+
],
|
92
|
+
"title": "GitHub Repositories",
|
93
|
+
"description": "List of GitHub organizations/repositories, e.g. `airbytehq/airbyte` for single repository, `airbytehq/*` for get all repositories from organization and `airbytehq/airbyte airbytehq/another-repo` for multiple repositories.",
|
94
|
+
"order": 1,
|
95
|
+
"pattern_descriptor": "org/repo org/another-repo org/*"
|
96
|
+
},
|
97
|
+
"start_date": {
|
98
|
+
"type": "string",
|
99
|
+
"title": "Start date",
|
100
|
+
"description": "The date from which you'd like to replicate data from GitHub in the format YYYY-MM-DDT00:00:00Z. If the date is not set, all data will be replicated. For the streams which support this configuration, only data generated on or after the start date will be replicated. This field doesn't apply to all streams, see the <a href=\"https://docs.airbyte.com/integrations/sources/github\">docs</a> for more info",
|
101
|
+
"examples": ["2021-03-01T00:00:00Z"],
|
102
|
+
"pattern": "^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}Z$",
|
103
|
+
"pattern_descriptor": "YYYY-MM-DDTHH:mm:ssZ",
|
104
|
+
"order": 2,
|
105
|
+
"format": "date-time"
|
106
|
+
},
|
107
|
+
"api_url": {
|
108
|
+
"type": "string",
|
109
|
+
"examples": ["https://github.com", "https://github.company.org"],
|
110
|
+
"title": "API URL",
|
111
|
+
"default": "https://api.github.com/",
|
112
|
+
"description": "Please enter your basic URL from self-hosted GitHub instance or leave it empty to use GitHub.",
|
113
|
+
"order": 3
|
114
|
+
},
|
115
|
+
"branch": {
|
116
|
+
"type": "string",
|
117
|
+
"title": "Branch",
|
118
|
+
"examples": ["airbytehq/airbyte/master airbytehq/airbyte/my-branch"],
|
119
|
+
"description": "(DEPRCATED) Space-delimited list of GitHub repository branches to pull commits for, e.g. `airbytehq/airbyte/master`. If no branches are specified for a repository, the default branch will be pulled.",
|
120
|
+
"airbyte_hidden": true,
|
121
|
+
"pattern_descriptor": "org/repo/branch1 org/repo/branch2"
|
122
|
+
},
|
123
|
+
"branches": {
|
124
|
+
"type": "array",
|
125
|
+
"items": {
|
126
|
+
"type": "string"
|
127
|
+
},
|
128
|
+
"title": "Branches",
|
129
|
+
"examples": ["airbytehq/airbyte/master airbytehq/airbyte/my-branch"],
|
130
|
+
"description": "List of GitHub repository branches to pull commits for, e.g. `airbytehq/airbyte/master`. If no branches are specified for a repository, the default branch will be pulled.",
|
131
|
+
"order": 4,
|
132
|
+
"pattern_descriptor": "org/repo/branch1 org/repo/branch2"
|
133
|
+
}
|
134
|
+
}
|
135
|
+
},
|
136
|
+
"advanced_auth": {
|
137
|
+
"auth_flow_type": "oauth2.0",
|
138
|
+
"predicate_key": ["credentials", "option_title"],
|
139
|
+
"predicate_value": "OAuth Credentials",
|
140
|
+
"oauth_config_specification": {
|
141
|
+
"complete_oauth_output_specification": {
|
142
|
+
"type": "object",
|
143
|
+
"additionalProperties": false,
|
144
|
+
"properties": {
|
145
|
+
"access_token": {
|
146
|
+
"type": "string",
|
147
|
+
"path_in_connector_config": ["credentials", "access_token"]
|
148
|
+
}
|
149
|
+
}
|
150
|
+
},
|
151
|
+
"complete_oauth_server_input_specification": {
|
152
|
+
"type": "object",
|
153
|
+
"additionalProperties": false,
|
154
|
+
"properties": {
|
155
|
+
"client_id": {
|
156
|
+
"type": "string"
|
157
|
+
},
|
158
|
+
"client_secret": {
|
159
|
+
"type": "string"
|
160
|
+
}
|
161
|
+
}
|
162
|
+
},
|
163
|
+
"complete_oauth_server_output_specification": {
|
164
|
+
"type": "object",
|
165
|
+
"additionalProperties": false,
|
166
|
+
"properties": {
|
167
|
+
"client_id": {
|
168
|
+
"type": "string",
|
169
|
+
"path_in_connector_config": ["credentials", "client_id"]
|
170
|
+
},
|
171
|
+
"client_secret": {
|
172
|
+
"type": "string",
|
173
|
+
"path_in_connector_config": ["credentials", "client_secret"]
|
174
|
+
}
|
175
|
+
}
|
176
|
+
}
|
177
|
+
}
|
178
|
+
}
|
179
|
+
}
|