airbyte-source-github 1.6.0__py3-none-any.whl → 1.6.1__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.6.1.dist-info/METADATA +111 -0
- {airbyte_source_github-1.6.0.dist-info → airbyte_source_github-1.6.1.dist-info}/RECORD +17 -43
- {airbyte_source_github-1.6.0.dist-info → airbyte_source_github-1.6.1.dist-info}/WHEEL +1 -2
- airbyte_source_github-1.6.1.dist-info/entry_points.txt +3 -0
- airbyte_source_github-1.6.0.dist-info/METADATA +0 -144
- airbyte_source_github-1.6.0.dist-info/entry_points.txt +0 -2
- airbyte_source_github-1.6.0.dist-info/top_level.txt +0 -3
- integration_tests/__init__.py +0 -0
- integration_tests/abnormal_state.json +0 -237
- integration_tests/acceptance.py +0 -16
- integration_tests/configured_catalog.json +0 -435
- integration_tests/configured_catalog_full_refresh_test.json +0 -415
- integration_tests/invalid_config.json +0 -5
- integration_tests/sample_config.json +0 -5
- integration_tests/sample_state.json +0 -137
- unit_tests/__init__.py +0 -3
- unit_tests/conftest.py +0 -29
- unit_tests/projects_v2_pull_requests_query.json +0 -3
- unit_tests/pull_request_stats_query.json +0 -3
- unit_tests/responses/contributor_activity_response.json +0 -33
- unit_tests/responses/graphql_reviews_responses.json +0 -405
- unit_tests/responses/issue_timeline_events.json +0 -166
- unit_tests/responses/issue_timeline_events_response.json +0 -170
- unit_tests/responses/projects_v2_response.json +0 -45
- unit_tests/responses/pull_request_comment_reactions.json +0 -744
- unit_tests/responses/pull_request_stats_response.json +0 -317
- unit_tests/test_migrations/test_config.json +0 -8
- unit_tests/test_migrations/test_new_config.json +0 -8
- unit_tests/test_multiple_token_authenticator.py +0 -163
- unit_tests/test_source.py +0 -331
- unit_tests/test_stream.py +0 -1471
- unit_tests/utils.py +0 -78
unit_tests/test_source.py
DELETED
@@ -1,331 +0,0 @@
|
|
1
|
-
#
|
2
|
-
# Copyright (c) 2023 Airbyte, Inc., all rights reserved.
|
3
|
-
#
|
4
|
-
|
5
|
-
import logging
|
6
|
-
import os
|
7
|
-
from unittest.mock import MagicMock
|
8
|
-
|
9
|
-
import pytest
|
10
|
-
import responses
|
11
|
-
from airbyte_cdk.models import AirbyteConnectionStatus, Status
|
12
|
-
from airbyte_cdk.utils.traced_exception import AirbyteTracedException
|
13
|
-
from source_github import constants
|
14
|
-
from source_github.source import SourceGithub
|
15
|
-
|
16
|
-
from .utils import command_check
|
17
|
-
|
18
|
-
|
19
|
-
def check_source(repo_line: str) -> AirbyteConnectionStatus:
|
20
|
-
source = SourceGithub()
|
21
|
-
config = {"access_token": "test_token", "repository": repo_line}
|
22
|
-
logger_mock = MagicMock()
|
23
|
-
return source.check(logger_mock, config)
|
24
|
-
|
25
|
-
|
26
|
-
def test_source_will_continue_sync_on_stream_failure():
|
27
|
-
source = SourceGithub()
|
28
|
-
assert source.continue_sync_on_stream_failure
|
29
|
-
|
30
|
-
|
31
|
-
@responses.activate
|
32
|
-
@pytest.mark.parametrize(
|
33
|
-
"config, expected",
|
34
|
-
(
|
35
|
-
(
|
36
|
-
{
|
37
|
-
"start_date": "2021-08-27T00:00:46Z",
|
38
|
-
"access_token": "test_token",
|
39
|
-
"repository": "airbyte/test",
|
40
|
-
},
|
41
|
-
True,
|
42
|
-
),
|
43
|
-
({"access_token": "test_token", "repository": "airbyte/test"}, True),
|
44
|
-
),
|
45
|
-
)
|
46
|
-
def test_check_start_date(config, expected, rate_limit_mock_response):
|
47
|
-
responses.add(responses.GET, "https://api.github.com/repos/airbyte/test?per_page=100", json={"full_name": "test_full_name"})
|
48
|
-
source = SourceGithub()
|
49
|
-
status, _ = source.check_connection(logger=logging.getLogger("airbyte"), config=config)
|
50
|
-
assert status == expected
|
51
|
-
|
52
|
-
|
53
|
-
@pytest.mark.parametrize(
|
54
|
-
"api_url, deployment_env, expected_message",
|
55
|
-
(
|
56
|
-
("github.my.company.org", "CLOUD", "Please enter a full url for `API URL` field starting with `http`"),
|
57
|
-
(
|
58
|
-
"http://github.my.company.org",
|
59
|
-
"CLOUD",
|
60
|
-
"HTTP connection is insecure and is not allowed in this environment. Please use `https` instead.",
|
61
|
-
),
|
62
|
-
("http:/github.my.company.org", "NOT_CLOUD", "Please provide a correct API URL."),
|
63
|
-
("https:/github.my.company.org", "CLOUD", "Please provide a correct API URL."),
|
64
|
-
),
|
65
|
-
)
|
66
|
-
def test_connection_fail_due_to_config_error(api_url, deployment_env, expected_message):
|
67
|
-
os.environ["DEPLOYMENT_MODE"] = deployment_env
|
68
|
-
source = SourceGithub()
|
69
|
-
config = {"access_token": "test_token", "repository": "airbyte/test", "api_url": api_url}
|
70
|
-
|
71
|
-
with pytest.raises(AirbyteTracedException) as e:
|
72
|
-
source.check_connection(logging.getLogger(), config)
|
73
|
-
assert e.value.message == expected_message
|
74
|
-
|
75
|
-
|
76
|
-
@responses.activate
|
77
|
-
def test_check_connection_repos_only(rate_limit_mock_response):
|
78
|
-
responses.add("GET", "https://api.github.com/repos/airbytehq/airbyte", json={"full_name": "airbytehq/airbyte"})
|
79
|
-
|
80
|
-
status = check_source("airbytehq/airbyte airbytehq/airbyte airbytehq/airbyte")
|
81
|
-
assert not status.message
|
82
|
-
assert status.status == Status.SUCCEEDED
|
83
|
-
# Only one request since 3 repos have same name
|
84
|
-
assert len(responses.calls) == 2
|
85
|
-
|
86
|
-
|
87
|
-
@responses.activate
|
88
|
-
def test_check_connection_repos_and_org_repos(rate_limit_mock_response):
|
89
|
-
repos = [{"name": f"name {i}", "full_name": f"full name {i}", "updated_at": "2020-01-01T00:00:00Z"} for i in range(1000)]
|
90
|
-
responses.add(
|
91
|
-
"GET", "https://api.github.com/repos/airbyte/test", json={"full_name": "airbyte/test", "organization": {"login": "airbyte"}}
|
92
|
-
)
|
93
|
-
responses.add(
|
94
|
-
"GET", "https://api.github.com/repos/airbyte/test2", json={"full_name": "airbyte/test2", "organization": {"login": "airbyte"}}
|
95
|
-
)
|
96
|
-
responses.add("GET", "https://api.github.com/orgs/airbytehq/repos", json=repos)
|
97
|
-
responses.add("GET", "https://api.github.com/orgs/org/repos", json=repos)
|
98
|
-
|
99
|
-
status = check_source("airbyte/test airbyte/test2 airbytehq/* org/*")
|
100
|
-
assert not status.message
|
101
|
-
assert status.status == Status.SUCCEEDED
|
102
|
-
# Two requests for repos and two for organization
|
103
|
-
assert len(responses.calls) == 5
|
104
|
-
|
105
|
-
|
106
|
-
@responses.activate
|
107
|
-
def test_check_connection_org_only(rate_limit_mock_response):
|
108
|
-
repos = [{"name": f"name {i}", "full_name": f"full name {i}", "updated_at": "2020-01-01T00:00:00Z"} for i in range(1000)]
|
109
|
-
responses.add("GET", "https://api.github.com/orgs/airbytehq/repos", json=repos)
|
110
|
-
|
111
|
-
status = check_source("airbytehq/*")
|
112
|
-
assert not status.message
|
113
|
-
assert status.status == Status.SUCCEEDED
|
114
|
-
# One request to check organization
|
115
|
-
assert len(responses.calls) == 2
|
116
|
-
|
117
|
-
|
118
|
-
@responses.activate
|
119
|
-
def test_get_branches_data():
|
120
|
-
|
121
|
-
repository_args = {"repositories": ["airbytehq/integration-test"], "page_size_for_large_streams": 10}
|
122
|
-
|
123
|
-
source = SourceGithub()
|
124
|
-
|
125
|
-
responses.add(
|
126
|
-
"GET",
|
127
|
-
"https://api.github.com/repos/airbytehq/integration-test",
|
128
|
-
json={"full_name": "airbytehq/integration-test", "default_branch": "master"},
|
129
|
-
)
|
130
|
-
|
131
|
-
responses.add(
|
132
|
-
"GET",
|
133
|
-
"https://api.github.com/repos/airbytehq/integration-test/branches",
|
134
|
-
json=[
|
135
|
-
{"repository": "airbytehq/integration-test", "name": "feature/branch_0"},
|
136
|
-
{"repository": "airbytehq/integration-test", "name": "feature/branch_1"},
|
137
|
-
{"repository": "airbytehq/integration-test", "name": "feature/branch_2"},
|
138
|
-
{"repository": "airbytehq/integration-test", "name": "master"},
|
139
|
-
],
|
140
|
-
)
|
141
|
-
|
142
|
-
default_branches, branches_to_pull = source._get_branches_data([], repository_args)
|
143
|
-
assert default_branches == {"airbytehq/integration-test": "master"}
|
144
|
-
assert branches_to_pull == {"airbytehq/integration-test": ["master"]}
|
145
|
-
|
146
|
-
default_branches, branches_to_pull = source._get_branches_data(
|
147
|
-
[
|
148
|
-
"airbytehq/integration-test/feature/branch_0",
|
149
|
-
"airbytehq/integration-test/feature/branch_1",
|
150
|
-
"airbytehq/integration-test/feature/branch_3",
|
151
|
-
],
|
152
|
-
repository_args,
|
153
|
-
)
|
154
|
-
|
155
|
-
assert default_branches == {"airbytehq/integration-test": "master"}
|
156
|
-
assert len(branches_to_pull["airbytehq/integration-test"]) == 2
|
157
|
-
assert "feature/branch_0" in branches_to_pull["airbytehq/integration-test"]
|
158
|
-
assert "feature/branch_1" in branches_to_pull["airbytehq/integration-test"]
|
159
|
-
|
160
|
-
|
161
|
-
@responses.activate
|
162
|
-
def test_get_org_repositories():
|
163
|
-
responses.add(
|
164
|
-
"GET",
|
165
|
-
"https://api.github.com/repos/airbytehq/integration-test",
|
166
|
-
json={"full_name": "airbytehq/integration-test", "organization": {"login": "airbytehq"}},
|
167
|
-
)
|
168
|
-
|
169
|
-
responses.add(
|
170
|
-
"GET",
|
171
|
-
"https://api.github.com/orgs/docker/repos",
|
172
|
-
json=[
|
173
|
-
{"full_name": "docker/docker-py", "updated_at": "2020-01-01T00:00:00Z"},
|
174
|
-
{"full_name": "docker/compose", "updated_at": "2020-01-01T00:00:00Z"},
|
175
|
-
],
|
176
|
-
)
|
177
|
-
|
178
|
-
config = {"repositories": ["airbytehq/integration-test", "docker/*"]}
|
179
|
-
source = SourceGithub()
|
180
|
-
config = source._ensure_default_values(config)
|
181
|
-
organisations, repositories = source._get_org_repositories(config, authenticator=None)
|
182
|
-
|
183
|
-
assert set(repositories) == {"airbytehq/integration-test", "docker/docker-py", "docker/compose"}
|
184
|
-
assert set(organisations) == {"airbytehq", "docker"}
|
185
|
-
|
186
|
-
|
187
|
-
@responses.activate
|
188
|
-
def test_organization_or_repo_available(monkeypatch, rate_limit_mock_response):
|
189
|
-
monkeypatch.setattr(SourceGithub, "_get_org_repositories", MagicMock(return_value=(False, False)))
|
190
|
-
source = SourceGithub()
|
191
|
-
with pytest.raises(Exception) as exc_info:
|
192
|
-
config = {"access_token": "test_token", "repository": ""}
|
193
|
-
source.streams(config=config)
|
194
|
-
assert exc_info.value.args[0] == "No streams available. Please check permissions"
|
195
|
-
|
196
|
-
|
197
|
-
def test_check_config_repository():
|
198
|
-
source = SourceGithub()
|
199
|
-
source.check = MagicMock(return_value=True)
|
200
|
-
config = {"credentials": {"access_token": "access_token"}, "start_date": "1900-01-01T00:00:00Z"}
|
201
|
-
|
202
|
-
repos_ok = [
|
203
|
-
"airbytehq/airbyte",
|
204
|
-
"airbytehq/airbyte-test",
|
205
|
-
"airbytehq/airbyte_test",
|
206
|
-
"erohmensing/thismonth.rocks",
|
207
|
-
"airbytehq/*",
|
208
|
-
"airbytehq/.",
|
209
|
-
"airbyte_hq/airbyte",
|
210
|
-
"airbytehq/123",
|
211
|
-
"airbytehq/airbytexgit",
|
212
|
-
]
|
213
|
-
|
214
|
-
repos_fail = [
|
215
|
-
"airbytehq",
|
216
|
-
"airbytehq/",
|
217
|
-
"airbytehq/*/",
|
218
|
-
"airbytehq/airbyte.git",
|
219
|
-
"airbytehq/airbyte/",
|
220
|
-
"airbytehq/air*yte",
|
221
|
-
"airbyte*/airbyte",
|
222
|
-
"airbytehq/airbyte-test/master-branch",
|
223
|
-
"https://github.com/airbytehq/airbyte",
|
224
|
-
]
|
225
|
-
|
226
|
-
config["repositories"] = []
|
227
|
-
with pytest.raises(AirbyteTracedException):
|
228
|
-
assert command_check(source, config)
|
229
|
-
config["repositories"] = []
|
230
|
-
with pytest.raises(AirbyteTracedException):
|
231
|
-
assert command_check(source, config)
|
232
|
-
|
233
|
-
for repos in repos_ok:
|
234
|
-
config["repositories"] = [repos]
|
235
|
-
assert command_check(source, config)
|
236
|
-
|
237
|
-
for repos in repos_fail:
|
238
|
-
config["repositories"] = [repos]
|
239
|
-
with pytest.raises(AirbyteTracedException):
|
240
|
-
assert command_check(source, config)
|
241
|
-
|
242
|
-
|
243
|
-
@responses.activate
|
244
|
-
def test_streams_no_streams_available_error(monkeypatch, rate_limit_mock_response):
|
245
|
-
monkeypatch.setattr(SourceGithub, "_get_org_repositories", MagicMock(return_value=(False, False)))
|
246
|
-
with pytest.raises(AirbyteTracedException) as e:
|
247
|
-
SourceGithub().streams(config={"access_token": "test_token", "repository": "airbytehq/airbyte-test"})
|
248
|
-
assert str(e.value) == "No streams available. Please check permissions"
|
249
|
-
|
250
|
-
|
251
|
-
@responses.activate
|
252
|
-
def test_streams_page_size(rate_limit_mock_response):
|
253
|
-
responses.get("https://api.github.com/repos/airbytehq/airbyte", json={"full_name": "airbytehq/airbyte", "default_branch": "master"})
|
254
|
-
responses.get("https://api.github.com/repos/airbytehq/airbyte/branches", json=[{"repository": "airbytehq/airbyte", "name": "master"}])
|
255
|
-
|
256
|
-
config = {
|
257
|
-
"credentials": {"access_token": "access_token"},
|
258
|
-
"repository": "airbytehq/airbyte",
|
259
|
-
"start_date": "1900-07-12T00:00:00Z",
|
260
|
-
}
|
261
|
-
|
262
|
-
source = SourceGithub()
|
263
|
-
streams = source.streams(config)
|
264
|
-
assert constants.DEFAULT_PAGE_SIZE != constants.DEFAULT_PAGE_SIZE_FOR_LARGE_STREAM
|
265
|
-
|
266
|
-
for stream in streams:
|
267
|
-
if stream.large_stream:
|
268
|
-
assert stream.page_size == constants.DEFAULT_PAGE_SIZE_FOR_LARGE_STREAM
|
269
|
-
else:
|
270
|
-
assert stream.page_size == constants.DEFAULT_PAGE_SIZE
|
271
|
-
|
272
|
-
|
273
|
-
@responses.activate
|
274
|
-
@pytest.mark.parametrize(
|
275
|
-
"config, expected",
|
276
|
-
(
|
277
|
-
(
|
278
|
-
{
|
279
|
-
"start_date": "2021-08-27T00:00:46Z",
|
280
|
-
"access_token": "test_token",
|
281
|
-
"repository": "airbyte/test",
|
282
|
-
},
|
283
|
-
39,
|
284
|
-
),
|
285
|
-
({"access_token": "test_token", "repository": "airbyte/test"}, 39),
|
286
|
-
),
|
287
|
-
)
|
288
|
-
def test_streams_config_start_date(config, expected, rate_limit_mock_response):
|
289
|
-
responses.add(responses.GET, "https://api.github.com/repos/airbyte/test?per_page=100", json={"full_name": "airbyte/test"})
|
290
|
-
responses.add(
|
291
|
-
responses.GET,
|
292
|
-
"https://api.github.com/repos/airbyte/test?per_page=100",
|
293
|
-
json={"full_name": "airbyte/test", "default_branch": "default_branch"},
|
294
|
-
)
|
295
|
-
responses.add(
|
296
|
-
responses.GET,
|
297
|
-
"https://api.github.com/repos/airbyte/test/branches?per_page=100",
|
298
|
-
json=[{"repository": "airbyte/test", "name": "name"}],
|
299
|
-
)
|
300
|
-
source = SourceGithub()
|
301
|
-
streams = source.streams(config=config)
|
302
|
-
# projects stream that uses start date
|
303
|
-
project_stream = streams[4]
|
304
|
-
assert len(streams) == expected
|
305
|
-
if config.get("start_date"):
|
306
|
-
assert project_stream._start_date == "2021-08-27T00:00:46Z"
|
307
|
-
else:
|
308
|
-
assert not project_stream._start_date
|
309
|
-
|
310
|
-
|
311
|
-
@pytest.mark.parametrize(
|
312
|
-
"error_message, expected_user_friendly_message",
|
313
|
-
[
|
314
|
-
(
|
315
|
-
"404 Client Error: Not Found for url: https://api.github.com/repos/repo_name",
|
316
|
-
'Repo name: "repo_name" is unknown, "repository" config option should use existing full repo name <organization>/<repository>',
|
317
|
-
),
|
318
|
-
(
|
319
|
-
"404 Client Error: Not Found for url: https://api.github.com/orgs/org_name",
|
320
|
-
'Organization name: "org_name" is unknown, "repository" config option should be updated. Please validate your repository config.',
|
321
|
-
),
|
322
|
-
(
|
323
|
-
"401 Client Error: Unauthorized for url",
|
324
|
-
"Github credentials have expired or changed, please review your credentials and re-authenticate or renew your access token.",
|
325
|
-
),
|
326
|
-
],
|
327
|
-
)
|
328
|
-
def test_user_friendly_message(error_message, expected_user_friendly_message):
|
329
|
-
source = SourceGithub()
|
330
|
-
user_friendly_error_message = source.user_friendly_error_message(error_message)
|
331
|
-
assert user_friendly_error_message == expected_user_friendly_message
|